mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[cxx-interop] Avoid generating ambiguous wrapper functions
When we generate a safe wrapper that only differs in the return type we might introduce ambiguities as some callers might not have enough information to disambiguate between the overloads. This PR makes sure the newly generated declarations are marked as @_disfavoredOverload so the compiler can keep calling the old functions without a source break when the feature is turned on. rdar://139074571
This commit is contained in:
@@ -289,8 +289,10 @@ func transformType(_ prev: TypeSyntax, _ generateSpan: Bool, _ isSizedBy: Bool)
|
||||
protocol BoundsCheckedThunkBuilder {
|
||||
func buildFunctionCall(_ pointerArgs: [Int: ExprSyntax]) throws -> ExprSyntax
|
||||
func buildBoundsChecks() throws -> [CodeBlockItemSyntax.Item]
|
||||
// The second component of the return value is true when only the return type of the
|
||||
// function signature was changed.
|
||||
func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ returnType: TypeSyntax?) throws
|
||||
-> FunctionSignatureSyntax
|
||||
-> (FunctionSignatureSyntax, Bool)
|
||||
}
|
||||
|
||||
func getParam(_ signature: FunctionSignatureSyntax, _ paramIndex: Int) -> FunctionParameterSyntax {
|
||||
@@ -308,6 +310,7 @@ func getParam(_ funcDecl: FunctionDeclSyntax, _ paramIndex: Int) -> FunctionPara
|
||||
|
||||
struct FunctionCallBuilder: BoundsCheckedThunkBuilder {
|
||||
let base: FunctionDeclSyntax
|
||||
|
||||
init(_ function: FunctionDeclSyntax) {
|
||||
base = function
|
||||
}
|
||||
@@ -317,7 +320,7 @@ struct FunctionCallBuilder: BoundsCheckedThunkBuilder {
|
||||
}
|
||||
|
||||
func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ returnType: TypeSyntax?) throws
|
||||
-> FunctionSignatureSyntax {
|
||||
-> (FunctionSignatureSyntax, Bool) {
|
||||
var newParams = base.signature.parameterClause.parameters.enumerated().filter {
|
||||
let type = argTypes[$0.offset]
|
||||
// filter out deleted parameters, i.e. ones where argTypes[i] _contains_ nil
|
||||
@@ -333,7 +336,7 @@ struct FunctionCallBuilder: BoundsCheckedThunkBuilder {
|
||||
if returnType != nil {
|
||||
sig = sig.with(\.returnClause!.type, returnType!)
|
||||
}
|
||||
return sig
|
||||
return (sig, (argTypes.count == 0 && returnType != nil))
|
||||
}
|
||||
|
||||
func buildFunctionCall(_ pointerArgs: [Int: ExprSyntax]) throws -> ExprSyntax {
|
||||
@@ -381,7 +384,7 @@ struct CxxSpanThunkBuilder: ParamPointerBoundsThunkBuilder {
|
||||
}
|
||||
|
||||
func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ returnType: TypeSyntax?) throws
|
||||
-> FunctionSignatureSyntax {
|
||||
-> (FunctionSignatureSyntax, Bool) {
|
||||
var types = argTypes
|
||||
let typeName = try getTypeName(oldType).text
|
||||
guard let desugaredType = typeMappings[typeName] else {
|
||||
@@ -417,7 +420,7 @@ struct CxxSpanReturnThunkBuilder: BoundsCheckedThunkBuilder {
|
||||
}
|
||||
|
||||
func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ returnType: TypeSyntax?) throws
|
||||
-> FunctionSignatureSyntax {
|
||||
-> (FunctionSignatureSyntax, Bool) {
|
||||
assert(returnType == nil)
|
||||
let typeName = try getTypeName(signature.returnClause!.type).text
|
||||
guard let desugaredType = typeMappings[typeName] else {
|
||||
@@ -490,7 +493,7 @@ struct CountedOrSizedReturnPointerThunkBuilder: PointerBoundsThunkBuilder {
|
||||
}
|
||||
|
||||
func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ returnType: TypeSyntax?) throws
|
||||
-> FunctionSignatureSyntax {
|
||||
-> (FunctionSignatureSyntax, Bool) {
|
||||
assert(returnType == nil)
|
||||
return try base.buildFunctionSignature(argTypes, newType)
|
||||
}
|
||||
@@ -518,7 +521,7 @@ struct CountedOrSizedPointerThunkBuilder: ParamPointerBoundsThunkBuilder {
|
||||
public let skipTrivialCount: Bool
|
||||
|
||||
func buildFunctionSignature(_ argTypes: [Int: TypeSyntax?], _ returnType: TypeSyntax?) throws
|
||||
-> FunctionSignatureSyntax {
|
||||
-> (FunctionSignatureSyntax, Bool) {
|
||||
var types = argTypes
|
||||
types[index] = try newType
|
||||
if skipTrivialCount {
|
||||
@@ -1104,7 +1107,7 @@ public struct SwiftifyImportMacro: PeerMacro {
|
||||
{ (prev, parsedArg) in
|
||||
parsedArg.getBoundsCheckedThunkBuilder(prev, funcDecl, skipTrivialCount)
|
||||
})
|
||||
let newSignature = try builder.buildFunctionSignature([:], nil)
|
||||
let (newSignature, onlyReturnTypeChanged) = try builder.buildFunctionSignature([:], nil)
|
||||
let checks =
|
||||
skipTrivialCount
|
||||
? [] as [CodeBlockItemSyntax]
|
||||
@@ -1118,6 +1121,12 @@ public struct SwiftifyImportMacro: PeerMacro {
|
||||
expression: try builder.buildFunctionCall([:]))))
|
||||
let body = CodeBlockSyntax(statements: CodeBlockItemListSyntax(checks + [call]))
|
||||
let lifetimeAttrs = lifetimeAttributes(funcDecl, lifetimeDependencies)
|
||||
let disfavoredOverload : [AttributeListSyntax.Element] = (onlyReturnTypeChanged ? [
|
||||
.attribute(
|
||||
AttributeSyntax(
|
||||
atSign: .atSignToken(),
|
||||
attributeName: IdentifierTypeSyntax(name: "_disfavoredOverload")))
|
||||
] : [])
|
||||
let newFunc =
|
||||
funcDecl
|
||||
.with(\.signature, newSignature)
|
||||
@@ -1138,7 +1147,8 @@ public struct SwiftifyImportMacro: PeerMacro {
|
||||
atSign: .atSignToken(),
|
||||
attributeName: IdentifierTypeSyntax(name: "_alwaysEmitIntoClient")))
|
||||
]
|
||||
+ lifetimeAttrs)
|
||||
+ lifetimeAttrs
|
||||
+ disfavoredOverload)
|
||||
return [DeclSyntax(newFunc)]
|
||||
} catch let error as DiagnosticError {
|
||||
context.diagnose(
|
||||
|
||||
@@ -14,7 +14,7 @@ import CountedByNoEscapeClang
|
||||
// CHECK: @_alwaysEmitIntoClient public func complexExpr(_ len: Int{{.*}}, _ offset: Int{{.*}}, _ p: MutableSpan<Int{{.*}}>)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func nonnull(_ p: MutableSpan<Int{{.*}}>)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullUnspecified(_ p: MutableSpan<Int{{.*}}>)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func returnPointer(_ len: Int{{.*}}) -> UnsafeMutableBufferPointer<Int{{.*}}>
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func returnPointer(_ len: Int{{.*}}) -> UnsafeMutableBufferPointer<Int{{.*}}>
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func shared(_ len: Int{{.*}}, _ p1: MutableSpan<Int{{.*}}>, _ p2: MutableSpan<Int{{.*}}>)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func simple(_ p: MutableSpan<Int{{.*}}>)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func swiftAttr(_ p: MutableSpan<Int{{.*}}>)
|
||||
|
||||
@@ -14,7 +14,7 @@ import CountedByClang
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func nonnull(_ p: UnsafeMutableBufferPointer<Int{{.*}}>)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullUnspecified(_ p: UnsafeMutableBufferPointer<Int{{.*}}>)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullable(_ p: UnsafeMutableBufferPointer<Int{{.*}}>?)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func returnPointer(_ len: Int{{.*}}) -> UnsafeMutableBufferPointer<Int{{.*}}>
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func returnPointer(_ len: Int{{.*}}) -> UnsafeMutableBufferPointer<Int{{.*}}>
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func shared(_ len: Int{{.*}}, _ p1: UnsafeMutableBufferPointer<Int{{.*}}>, _ p2: UnsafeMutableBufferPointer<Int{{.*}}>)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func simple(_ p: UnsafeMutableBufferPointer<Int{{.*}}>)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func swiftAttr(_ p: UnsafeMutableBufferPointer<Int{{.*}}>)
|
||||
|
||||
@@ -14,7 +14,7 @@ import SizedByNoEscapeClang
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func nonnull(_ p: RawSpan)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullUnspecified(_ p: RawSpan)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func opaque(_ p: RawSpan)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func returnPointer(_ len: Int{{.*}}) -> UnsafeRawBufferPointer
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func returnPointer(_ len: Int{{.*}}) -> UnsafeRawBufferPointer
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func shared(_ len: Int{{.*}}, _ p1: RawSpan, _ p2: RawSpan)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func simple(_ p: RawSpan)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func swiftAttr(_ p: RawSpan)
|
||||
|
||||
@@ -14,7 +14,7 @@ import SizedByClang
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullUnspecified(_ p: UnsafeMutableRawBufferPointer)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullable(_ p: UnsafeMutableRawBufferPointer?)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func opaque(_ p: UnsafeRawBufferPointer)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func returnPointer(_ len: Int{{.*}}) -> UnsafeMutableRawBufferPointer
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func returnPointer(_ len: Int{{.*}}) -> UnsafeMutableRawBufferPointer
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func shared(_ len: Int{{.*}}, _ p1: UnsafeMutableRawBufferPointer, _ p2: UnsafeMutableRawBufferPointer)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func simple(_ p: UnsafeMutableRawBufferPointer)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func swiftAttr(_ p: UnsafeMutableRawBufferPointer)
|
||||
|
||||
@@ -16,7 +16,7 @@ import CxxStdlib
|
||||
|
||||
// CHECK: struct DependsOnSelf {
|
||||
// CHECK: @lifetime(borrow self)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public mutating func get() -> Span<CInt>
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public mutating func get() -> Span<CInt>
|
||||
// CHECK-NEXT: mutating func get() -> ConstSpanOfInt
|
||||
|
||||
// CHECK: func funcWithSafeWrapper(_ s: ConstSpanOfInt)
|
||||
@@ -31,4 +31,4 @@ import CxxStdlib
|
||||
// CHECK-NEXT: @lifetime(s)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func funcWithSafeWrapper2(_ s: borrowing Span<CInt>) -> Span<CInt>
|
||||
// CHECK-NEXT: @lifetime(borrow v)
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient public func funcWithSafeWrapper3(_ v: borrowing VecOfInt) -> Span<CInt>
|
||||
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func funcWithSafeWrapper3(_ v: borrowing VecOfInt) -> Span<CInt>
|
||||
|
||||
@@ -10,12 +10,12 @@ func myFunc(_ len: CInt) -> UnsafeMutablePointer<CInt> {
|
||||
func nonEscaping(_ len: CInt) -> UnsafePointer<CInt> {
|
||||
}
|
||||
|
||||
// CHECK: @_alwaysEmitIntoClient
|
||||
// CHECK: @_alwaysEmitIntoClient @_disfavoredOverload
|
||||
// CHECK-NEXT: func myFunc(_ len: CInt) -> UnsafeMutableBufferPointer<CInt> {
|
||||
// CHECK-NEXT: return UnsafeMutableBufferPointer<CInt> (start: myFunc(len), count: Int(len))
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK: @_alwaysEmitIntoClient
|
||||
// CHECK: @_alwaysEmitIntoClient @_disfavoredOverload
|
||||
// CHECK-NEXT: func nonEscaping(_ len: CInt) -> UnsafeBufferPointer<CInt> {
|
||||
// CHECK-NEXT: return UnsafeBufferPointer<CInt> (start: nonEscaping(len), count: Int(len))
|
||||
// CHECK-NEXT: }
|
||||
|
||||
@@ -39,7 +39,7 @@ struct X {
|
||||
// CHECK-NEXT: return Span(_unsafeCxxSpan: myFunc(SpanOfInt(span)))
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK: @_alwaysEmitIntoClient @lifetime(borrow vec)
|
||||
// CHECK: @_alwaysEmitIntoClient @lifetime(borrow vec) @_disfavoredOverload
|
||||
// CHECK-NEXT: func myFunc2(_ vec: borrowing VecOfInt) -> Span<CInt> {
|
||||
// CHECK-NEXT: return Span(_unsafeCxxSpan: myFunc2(vec))
|
||||
// CHECK-NEXT: }
|
||||
@@ -54,7 +54,7 @@ struct X {
|
||||
// CHECK-NEXT: return Span(_unsafeCxxSpan: myFunc4(vec, SpanOfInt(span)))
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK: @_alwaysEmitIntoClient @lifetime(borrow self)
|
||||
// CHECK: @_alwaysEmitIntoClient @lifetime(borrow self) @_disfavoredOverload
|
||||
// CHECK-NEXT: func myFunc5() -> Span<CInt> {
|
||||
// CHECK-NEXT: return Span(_unsafeCxxSpan: myFunc5())
|
||||
// CHECK-NEXT: }
|
||||
|
||||
Reference in New Issue
Block a user