@@ -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}
@@ -256,6 +277,43 @@ struct FunctionCallBuilder: BoundsCheckedThunkBuilder {
256277 }
257278}
258279
280+ struct CxxSpanThunkBuilder : BoundsCheckedThunkBuilder {
281+ public let base : BoundsCheckedThunkBuilder
282+ public let index : Int
283+ public let signature : FunctionSignatureSyntax
284+ public let typeMappings : [ String : String ]
285+ public let node : SyntaxProtocol
286+
287+ func buildBoundsChecks( _ variant: Variant ) throws -> [ CodeBlockItemSyntax . Item ] {
288+ return [ ]
289+ }
290+
291+ func buildFunctionSignature( _ argTypes: [ Int : TypeSyntax ? ] , _ variant: Variant ) throws
292+ -> FunctionSignatureSyntax {
293+ var types = argTypes
294+ let param = getParam ( signature, index)
295+ let typeName = try getTypeName ( param. type) . text;
296+ guard let desugaredType = typeMappings [ typeName] else {
297+ throw DiagnosticError (
298+ " unable to desugar type with name ' \( typeName) ' " , node: node)
299+ }
300+
301+ let parsedDesugaredType = try TypeSyntax ( " \( raw: desugaredType) " )
302+ types [ index] = TypeSyntax ( IdentifierTypeSyntax ( name: " Span " ,
303+ genericArgumentClause: parsedDesugaredType. as ( IdentifierTypeSyntax . self) !. genericArgumentClause) )
304+ return try base. buildFunctionSignature ( types, variant)
305+ }
306+
307+ func buildFunctionCall( _ pointerArgs: [ Int : ExprSyntax ] , _ variant: Variant ) throws -> ExprSyntax {
308+ var args = pointerArgs
309+ let param = getParam ( signature, index)
310+ let typeName = try getTypeName ( param. type) . text;
311+ assert ( args [ index] == nil )
312+ args [ index] = ExprSyntax ( " \( raw: typeName) ( \( raw: param. secondName ?? param. firstName) ) " )
313+ return try base. buildFunctionCall ( args, variant)
314+ }
315+ }
316+
259317protocol PointerBoundsThunkBuilder : BoundsCheckedThunkBuilder {
260318 var name : TokenSyntax { get }
261319 var nullable : Bool { get }
@@ -460,7 +518,8 @@ func getParameterIndexForDeclRef(
460518/// Depends on bounds, escapability and lifetime information for each pointer.
461519/// Intended to map to C attributes like __counted_by, __ended_by and __no_escape,
462520/// for automatic application by ClangImporter when the C declaration is annotated
463- /// appropriately.
521+ /// appropriately. Moreover, it can wrap C++ APIs using unsafe C++ types like
522+ /// std::span with APIs that use their safer Swift equivalents.
464523public struct SwiftifyImportMacro : PeerMacro {
465524 static func parseEnumName( _ enumConstructorExpr: FunctionCallExprSyntax ) throws -> String {
466525 guard let calledExpr = enumConstructorExpr. calledExpression. as ( MemberAccessExprSyntax . self)
@@ -557,6 +616,54 @@ public struct SwiftifyImportMacro: PeerMacro {
557616 return pointerParamIndex
558617 }
559618
619+ static func parseTypeMappingParam( _ paramAST: LabeledExprSyntax ? ) throws -> [ String : String ] ? {
620+ guard let unwrappedParamAST = paramAST else {
621+ return nil
622+ }
623+ let paramExpr = unwrappedParamAST. expression
624+ guard let dictExpr = paramExpr. as ( DictionaryExprSyntax . self) else {
625+ return nil
626+ }
627+ var dict : [ String : String ] = [ : ]
628+ switch dictExpr. content {
629+ case . colon( _) :
630+ return dict
631+ case . elements( let types) :
632+ for element in types {
633+ guard let key = element. key. as ( StringLiteralExprSyntax . self) else {
634+ throw DiagnosticError ( " expected a string literal, got ' \( element. key) ' " , node: element. key)
635+ }
636+ guard let value = element. value. as ( StringLiteralExprSyntax . self) else {
637+ throw DiagnosticError ( " expected a string literal, got ' \( element. value) ' " , node: element. value)
638+ }
639+ dict [ key. representedLiteralValue!] = value. representedLiteralValue!
640+ }
641+ default :
642+ throw DiagnosticError ( " unknown dictionary literal " , node: dictExpr)
643+ }
644+ return dict
645+ }
646+
647+ static func parseCxxSpanParams(
648+ _ signature: FunctionSignatureSyntax ,
649+ _ typeMappings: [ String : String ] ?
650+ ) throws -> [ ParamInfo ] {
651+ guard let typeMappings else {
652+ return [ ]
653+ }
654+ var result : [ ParamInfo ] = [ ]
655+ for (idx, param) in signature. parameterClause. parameters. enumerated ( ) {
656+ let typeName = try getTypeName ( param. type) . text;
657+ if let desugaredType = typeMappings [ typeName] {
658+ if desugaredType. starts ( with: " span " ) {
659+ result. append ( CxxSpan ( pointerIndex: idx + 1 , nonescaping: false ,
660+ original: param, typeMappings: typeMappings) )
661+ }
662+ }
663+ }
664+ return result
665+ }
666+
560667 static func parseMacroParam(
561668 _ paramAST: LabeledExprSyntax , _ signature: FunctionSignatureSyntax ,
562669 nonescapingPointers: inout Set < Int >
@@ -651,11 +758,20 @@ public struct SwiftifyImportMacro: PeerMacro {
651758 }
652759
653760 let argumentList = node. arguments!. as ( LabeledExprListSyntax . self) !
761+ var arguments = Array < LabeledExprSyntax > ( argumentList)
762+ let typeMappings = try parseTypeMappingParam ( arguments. last)
763+ if typeMappings != nil {
764+ arguments = arguments. dropLast ( )
765+ }
654766 var nonescapingPointers = Set < Int > ( )
655- var parsedArgs = try argumentList . compactMap {
767+ var parsedArgs = try arguments . compactMap {
656768 try parseMacroParam ( $0, funcDecl. signature, nonescapingPointers: & nonescapingPointers)
657769 }
770+ parsedArgs. append ( contentsOf: try parseCxxSpanParams ( funcDecl. signature, typeMappings) )
658771 setNonescapingPointers ( & parsedArgs, nonescapingPointers)
772+ parsedArgs = parsedArgs. filter {
773+ !( $0 is CxxSpan ) || ( $0 as! CxxSpan ) . nonescaping
774+ }
659775 try checkArgs ( parsedArgs, funcDecl)
660776 let baseBuilder = FunctionCallBuilder ( funcDecl)
661777
0 commit comments