@@ -6,7 +6,7 @@ import SwiftSyntaxMacros
6
6
7
7
protocol ParamInfo : CustomStringConvertible {
8
8
var description : String { get }
9
- var original : ExprSyntax { get }
9
+ var original : SyntaxProtocol { get }
10
10
var pointerIndex : Int { get }
11
11
var nonescaping : Bool { get set }
12
12
@@ -16,12 +16,31 @@ protocol ParamInfo: CustomStringConvertible {
16
16
) -> BoundsCheckedThunkBuilder
17
17
}
18
18
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
+
19
38
struct CountedBy : ParamInfo {
20
39
var pointerIndex : Int
21
40
var count : ExprSyntax
22
41
var sizedBy : Bool
23
42
var nonescaping : Bool
24
- var original : ExprSyntax
43
+ var original : SyntaxProtocol
25
44
26
45
var description : String {
27
46
if sizedBy {
@@ -43,11 +62,12 @@ struct CountedBy: ParamInfo {
43
62
nonescaping: nonescaping, isSizedBy: sizedBy)
44
63
}
45
64
}
65
+
46
66
struct EndedBy : ParamInfo {
47
67
var pointerIndex : Int
48
68
var endIndex : Int
49
69
var nonescaping : Bool
50
- var original : ExprSyntax
70
+ var original : SyntaxProtocol
51
71
52
72
var description : String {
53
73
return " .endedBy(start: \( pointerIndex) , end: \( endIndex) , nonescaping: \( nonescaping) ) "
@@ -196,6 +216,7 @@ func getParam(_ signature: FunctionSignatureSyntax, _ paramIndex: Int) -> Functi
196
216
return params [ params. startIndex]
197
217
}
198
218
}
219
+
199
220
func getParam( _ funcDecl: FunctionDeclSyntax , _ paramIndex: Int ) -> FunctionParameterSyntax {
200
221
return getParam ( funcDecl. signature, paramIndex)
201
222
}
@@ -257,6 +278,43 @@ struct FunctionCallBuilder: BoundsCheckedThunkBuilder {
257
278
}
258
279
}
259
280
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
+
260
318
protocol PointerBoundsThunkBuilder : BoundsCheckedThunkBuilder {
261
319
var name : TokenSyntax { get }
262
320
var nullable : Bool { get }
@@ -461,7 +519,8 @@ func getParameterIndexForDeclRef(
461
519
/// Depends on bounds, escapability and lifetime information for each pointer.
462
520
/// Intended to map to C attributes like __counted_by, __ended_by and __no_escape,
463
521
/// 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.
465
524
public struct SwiftifyImportMacro : PeerMacro {
466
525
static func parseEnumName( _ enumConstructorExpr: FunctionCallExprSyntax ) throws -> String {
467
526
guard let calledExpr = enumConstructorExpr. calledExpression. as ( MemberAccessExprSyntax . self)
@@ -558,6 +617,54 @@ public struct SwiftifyImportMacro: PeerMacro {
558
617
return pointerParamIndex
559
618
}
560
619
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
+
561
668
static func parseMacroParam(
562
669
_ paramAST: LabeledExprSyntax , _ signature: FunctionSignatureSyntax ,
563
670
nonescapingPointers: inout Set < Int >
@@ -652,11 +759,20 @@ public struct SwiftifyImportMacro: PeerMacro {
652
759
}
653
760
654
761
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
+ }
655
767
var nonescapingPointers = Set < Int > ( )
656
- var parsedArgs = try argumentList . compactMap {
768
+ var parsedArgs = try arguments . compactMap {
657
769
try parseMacroParam ( $0, funcDecl. signature, nonescapingPointers: & nonescapingPointers)
658
770
}
771
+ parsedArgs. append ( contentsOf: try parseCxxSpanParams ( funcDecl. signature, typeMappings) )
659
772
setNonescapingPointers ( & parsedArgs, nonescapingPointers)
773
+ parsedArgs = parsedArgs. filter {
774
+ !( $0 is CxxSpan ) || ( $0 as! CxxSpan ) . nonescaping
775
+ }
660
776
try checkArgs ( parsedArgs, funcDecl)
661
777
let baseBuilder = FunctionCallBuilder ( funcDecl)
662
778
0 commit comments