@@ -6,7 +6,7 @@ import SwiftSyntaxMacros
66
77protocol ParamInfo : CustomStringConvertible {
88 var description : String { get }
9- var original : ExprSyntax { get }
9+ var original : SyntaxProtocol { get }
1010 var pointerIndex : Int { get }
1111 var nonescaping : Bool { get set }
1212
@@ -16,12 +16,31 @@ protocol ParamInfo: CustomStringConvertible {
1616 ) -> BoundsCheckedThunkBuilder
1717}
1818
19+ struct CxxSpan : ParamInfo {
20+ var pointerIndex : Int
21+ var nonescaping : Bool
22+ var original : SyntaxProtocol
23+ var typeMappings : [ String : String ]
24+
25+ var description : String {
26+ return " std::span(pointer: \( pointerIndex) , nonescaping: \( nonescaping) ) "
27+ }
28+
29+ func getBoundsCheckedThunkBuilder(
30+ _ base: BoundsCheckedThunkBuilder , _ funcDecl: FunctionDeclSyntax ,
31+ _ variant: Variant
32+ ) -> BoundsCheckedThunkBuilder {
33+ CxxSpanThunkBuilder ( base: base, index: pointerIndex - 1 , signature: funcDecl. signature,
34+ typeMappings: typeMappings, node: original)
35+ }
36+ }
37+
1938struct CountedBy : ParamInfo {
2039 var pointerIndex : Int
2140 var count : ExprSyntax
2241 var sizedBy : Bool
2342 var nonescaping : Bool
24- var original : ExprSyntax
43+ var original : SyntaxProtocol
2544
2645 var description : String {
2746 if sizedBy {
@@ -43,11 +62,12 @@ struct CountedBy: ParamInfo {
4362 nonescaping: nonescaping, isSizedBy: sizedBy)
4463 }
4564}
65+
4666struct EndedBy : ParamInfo {
4767 var pointerIndex : Int
4868 var endIndex : Int
4969 var nonescaping : Bool
50- var original : ExprSyntax
70+ var original : SyntaxProtocol
5171
5272 var description : String {
5373 return " .endedBy(start: \( pointerIndex) , end: \( endIndex) , nonescaping: \( nonescaping) ) "
@@ -196,6 +216,7 @@ func getParam(_ signature: FunctionSignatureSyntax, _ paramIndex: Int) -> Functi
196216 return params [ params. startIndex]
197217 }
198218}
219+
199220func getParam( _ funcDecl: FunctionDeclSyntax , _ paramIndex: Int ) -> FunctionParameterSyntax {
200221 return getParam ( funcDecl. signature, paramIndex)
201222}
@@ -257,6 +278,43 @@ struct FunctionCallBuilder: BoundsCheckedThunkBuilder {
257278 }
258279}
259280
281+ struct CxxSpanThunkBuilder : BoundsCheckedThunkBuilder {
282+ public let base : BoundsCheckedThunkBuilder
283+ public let index : Int
284+ public let signature : FunctionSignatureSyntax
285+ public let typeMappings : [ String : String ]
286+ public let node : SyntaxProtocol
287+
288+ func buildBoundsChecks( _ variant: Variant ) throws -> [ CodeBlockItemSyntax . Item ] {
289+ return [ ]
290+ }
291+
292+ func buildFunctionSignature( _ argTypes: [ Int : TypeSyntax ? ] , _ variant: Variant ) throws
293+ -> FunctionSignatureSyntax {
294+ var types = argTypes
295+ let param = getParam ( signature, index)
296+ let typeName = try getTypeName ( param. type) . text;
297+ guard let desugaredType = typeMappings [ typeName] else {
298+ throw DiagnosticError (
299+ " unable to desugar type with name ' \( typeName) ' " , node: node)
300+ }
301+
302+ let parsedDesugaredType = try TypeSyntax ( " \( raw: desugaredType) " )
303+ types [ index] = TypeSyntax ( IdentifierTypeSyntax ( name: " Span " ,
304+ genericArgumentClause: parsedDesugaredType. as ( IdentifierTypeSyntax . self) !. genericArgumentClause) )
305+ return try base. buildFunctionSignature ( types, variant)
306+ }
307+
308+ func buildFunctionCall( _ pointerArgs: [ Int : ExprSyntax ] , _ variant: Variant ) throws -> ExprSyntax {
309+ var args = pointerArgs
310+ let param = getParam ( signature, index)
311+ let typeName = try getTypeName ( param. type) . text;
312+ assert ( args [ index] == nil )
313+ args [ index] = ExprSyntax ( " \( raw: typeName) ( \( raw: param. secondName ?? param. firstName) ) " )
314+ return try base. buildFunctionCall ( args, variant)
315+ }
316+ }
317+
260318protocol PointerBoundsThunkBuilder : BoundsCheckedThunkBuilder {
261319 var name : TokenSyntax { get }
262320 var nullable : Bool { get }
@@ -461,7 +519,8 @@ func getParameterIndexForDeclRef(
461519/// Depends on bounds, escapability and lifetime information for each pointer.
462520/// Intended to map to C attributes like __counted_by, __ended_by and __no_escape,
463521/// for automatic application by ClangImporter when the C declaration is annotated
464- /// appropriately.
522+ /// appropriately. Moreover, it can wrap C++ APIs using unsafe C++ types like
523+ /// std::span with APIs that use their safer Swift equivalents.
465524public struct SwiftifyImportMacro : PeerMacro {
466525 static func parseEnumName( _ enumConstructorExpr: FunctionCallExprSyntax ) throws -> String {
467526 guard let calledExpr = enumConstructorExpr. calledExpression. as ( MemberAccessExprSyntax . self)
@@ -558,6 +617,54 @@ public struct SwiftifyImportMacro: PeerMacro {
558617 return pointerParamIndex
559618 }
560619
620+ static func parseTypeMappingParam( _ paramAST: LabeledExprSyntax ? ) throws -> [ String : String ] ? {
621+ guard let unwrappedParamAST = paramAST else {
622+ return nil
623+ }
624+ let paramExpr = unwrappedParamAST. expression
625+ guard let dictExpr = paramExpr. as ( DictionaryExprSyntax . self) else {
626+ return nil
627+ }
628+ var dict : [ String : String ] = [ : ]
629+ switch dictExpr. content {
630+ case . colon( _) :
631+ return dict
632+ case . elements( let types) :
633+ for element in types {
634+ guard let key = element. key. as ( StringLiteralExprSyntax . self) else {
635+ throw DiagnosticError ( " expected a string literal, got ' \( element. key) ' " , node: element. key)
636+ }
637+ guard let value = element. value. as ( StringLiteralExprSyntax . self) else {
638+ throw DiagnosticError ( " expected a string literal, got ' \( element. value) ' " , node: element. value)
639+ }
640+ dict [ key. representedLiteralValue!] = value. representedLiteralValue!
641+ }
642+ default :
643+ throw DiagnosticError ( " unknown dictionary literal " , node: dictExpr)
644+ }
645+ return dict
646+ }
647+
648+ static func parseCxxSpanParams(
649+ _ signature: FunctionSignatureSyntax ,
650+ _ typeMappings: [ String : String ] ?
651+ ) throws -> [ ParamInfo ] {
652+ guard let typeMappings else {
653+ return [ ]
654+ }
655+ var result : [ ParamInfo ] = [ ]
656+ for (idx, param) in signature. parameterClause. parameters. enumerated ( ) {
657+ let typeName = try getTypeName ( param. type) . text;
658+ if let desugaredType = typeMappings [ typeName] {
659+ if desugaredType. starts ( with: " span " ) {
660+ result. append ( CxxSpan ( pointerIndex: idx + 1 , nonescaping: false ,
661+ original: param, typeMappings: typeMappings) )
662+ }
663+ }
664+ }
665+ return result
666+ }
667+
561668 static func parseMacroParam(
562669 _ paramAST: LabeledExprSyntax , _ signature: FunctionSignatureSyntax ,
563670 nonescapingPointers: inout Set < Int >
@@ -652,11 +759,20 @@ public struct SwiftifyImportMacro: PeerMacro {
652759 }
653760
654761 let argumentList = node. arguments!. as ( LabeledExprListSyntax . self) !
762+ var arguments = Array < LabeledExprSyntax > ( argumentList)
763+ let typeMappings = try parseTypeMappingParam ( arguments. last)
764+ if typeMappings != nil {
765+ arguments = arguments. dropLast ( )
766+ }
655767 var nonescapingPointers = Set < Int > ( )
656- var parsedArgs = try argumentList . compactMap {
768+ var parsedArgs = try arguments . compactMap {
657769 try parseMacroParam ( $0, funcDecl. signature, nonescapingPointers: & nonescapingPointers)
658770 }
771+ parsedArgs. append ( contentsOf: try parseCxxSpanParams ( funcDecl. signature, typeMappings) )
659772 setNonescapingPointers ( & parsedArgs, nonescapingPointers)
773+ parsedArgs = parsedArgs. filter {
774+ !( $0 is CxxSpan ) || ( $0 as! CxxSpan ) . nonescaping
775+ }
660776 try checkArgs ( parsedArgs, funcDecl)
661777 let baseBuilder = FunctionCallBuilder ( funcDecl)
662778
0 commit comments