mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[6.2][cxx-interop] Support Swiftifying C++ constructors
Explanation: We did not have support to generate swiftified overload for initializers. This PR adds that support. Issue: rdar://152112660 Risk: Low, the feature is localized to swiftified overloads. Testing: Regression test added. Original PR: #81947 Reviewer: @hnrklssn
This commit is contained in:
committed by
Gabor Horvath
parent
c9d5e4b26c
commit
2b33c44796
@@ -40,11 +40,11 @@ protocol ParamInfo: CustomStringConvertible {
|
||||
var dependencies: [LifetimeDependence] { get set }
|
||||
|
||||
func getBoundsCheckedThunkBuilder(
|
||||
_ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionDeclSyntax
|
||||
_ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionParts
|
||||
) -> BoundsCheckedThunkBuilder
|
||||
}
|
||||
|
||||
func tryGetParamName(_ funcDecl: FunctionDeclSyntax, _ expr: SwiftifyExpr) -> TokenSyntax? {
|
||||
func tryGetParamName(_ funcDecl: FunctionParts, _ expr: SwiftifyExpr) -> TokenSyntax? {
|
||||
switch expr {
|
||||
case .param(let i):
|
||||
let funcParam = getParam(funcDecl, i - 1)
|
||||
@@ -55,7 +55,7 @@ func tryGetParamName(_ funcDecl: FunctionDeclSyntax, _ expr: SwiftifyExpr) -> To
|
||||
}
|
||||
}
|
||||
|
||||
func getSwiftifyExprType(_ funcDecl: FunctionDeclSyntax, _ expr: SwiftifyExpr) -> TypeSyntax {
|
||||
func getSwiftifyExprType(_ funcDecl: FunctionParts, _ expr: SwiftifyExpr) -> TypeSyntax {
|
||||
switch expr {
|
||||
case .param(let i):
|
||||
let funcParam = getParam(funcDecl, i - 1)
|
||||
@@ -79,7 +79,7 @@ struct CxxSpan: ParamInfo {
|
||||
}
|
||||
|
||||
func getBoundsCheckedThunkBuilder(
|
||||
_ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionDeclSyntax
|
||||
_ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionParts
|
||||
) -> BoundsCheckedThunkBuilder {
|
||||
switch pointerIndex {
|
||||
case .param(let i):
|
||||
@@ -115,7 +115,7 @@ struct CountedBy: ParamInfo {
|
||||
}
|
||||
|
||||
func getBoundsCheckedThunkBuilder(
|
||||
_ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionDeclSyntax
|
||||
_ base: BoundsCheckedThunkBuilder, _ funcDecl: FunctionParts
|
||||
) -> BoundsCheckedThunkBuilder {
|
||||
switch pointerIndex {
|
||||
case .param(let i):
|
||||
@@ -400,14 +400,14 @@ func getParam(_ signature: FunctionSignatureSyntax, _ paramIndex: Int) -> Functi
|
||||
}
|
||||
}
|
||||
|
||||
func getParam(_ funcDecl: FunctionDeclSyntax, _ paramIndex: Int) -> FunctionParameterSyntax {
|
||||
func getParam(_ funcDecl: FunctionParts, _ paramIndex: Int) -> FunctionParameterSyntax {
|
||||
return getParam(funcDecl.signature, paramIndex)
|
||||
}
|
||||
|
||||
struct FunctionCallBuilder: BoundsCheckedThunkBuilder {
|
||||
let base: FunctionDeclSyntax
|
||||
let base: FunctionParts
|
||||
|
||||
init(_ function: FunctionDeclSyntax) {
|
||||
init(_ function: FunctionParts) {
|
||||
base = function
|
||||
}
|
||||
|
||||
@@ -467,14 +467,18 @@ struct FunctionCallBuilder: BoundsCheckedThunkBuilder {
|
||||
FunctionCallExprSyntax(
|
||||
calledExpression: functionRef, leftParen: .leftParenToken(),
|
||||
arguments: LabeledExprListSyntax(labeledArgs), rightParen: .rightParenToken()))
|
||||
return "unsafe \(call)"
|
||||
if base.name.tokenKind == .keyword(.`init`) {
|
||||
return "unsafe self.\(call)"
|
||||
} else {
|
||||
return "unsafe \(call)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CxxSpanThunkBuilder: SpanBoundsThunkBuilder, ParamBoundsThunkBuilder {
|
||||
public let base: BoundsCheckedThunkBuilder
|
||||
public let index: Int
|
||||
public let funcDecl: FunctionDeclSyntax
|
||||
public let funcDecl: FunctionParts
|
||||
public let typeMappings: [String: String]
|
||||
public let node: SyntaxProtocol
|
||||
public let nonescaping: Bool
|
||||
@@ -525,7 +529,7 @@ struct CxxSpanThunkBuilder: SpanBoundsThunkBuilder, ParamBoundsThunkBuilder {
|
||||
|
||||
struct CxxSpanReturnThunkBuilder: SpanBoundsThunkBuilder {
|
||||
public let base: BoundsCheckedThunkBuilder
|
||||
public let funcDecl: FunctionDeclSyntax
|
||||
public let funcDecl: FunctionParts
|
||||
public let typeMappings: [String: String]
|
||||
public let node: SyntaxProtocol
|
||||
let isParameter: Bool = false
|
||||
@@ -564,7 +568,7 @@ struct CxxSpanReturnThunkBuilder: SpanBoundsThunkBuilder {
|
||||
protocol BoundsThunkBuilder: BoundsCheckedThunkBuilder {
|
||||
var oldType: TypeSyntax { get }
|
||||
var newType: TypeSyntax { get throws }
|
||||
var funcDecl: FunctionDeclSyntax { get }
|
||||
var funcDecl: FunctionParts { get }
|
||||
}
|
||||
|
||||
extension BoundsThunkBuilder {
|
||||
@@ -675,7 +679,7 @@ extension ParamBoundsThunkBuilder {
|
||||
struct CountedOrSizedReturnPointerThunkBuilder: PointerBoundsThunkBuilder {
|
||||
public let base: BoundsCheckedThunkBuilder
|
||||
public let countExpr: ExprSyntax
|
||||
public let funcDecl: FunctionDeclSyntax
|
||||
public let funcDecl: FunctionParts
|
||||
public let nonescaping: Bool
|
||||
public let isSizedBy: Bool
|
||||
public let dependencies: [LifetimeDependence]
|
||||
@@ -743,7 +747,7 @@ struct CountedOrSizedPointerThunkBuilder: ParamBoundsThunkBuilder, PointerBounds
|
||||
public let base: BoundsCheckedThunkBuilder
|
||||
public let index: Int
|
||||
public let countExpr: ExprSyntax
|
||||
public let funcDecl: FunctionDeclSyntax
|
||||
public let funcDecl: FunctionParts
|
||||
public let nonescaping: Bool
|
||||
public let isSizedBy: Bool
|
||||
let isParameter: Bool = true
|
||||
@@ -1237,22 +1241,22 @@ func parseMacroParam(
|
||||
}
|
||||
}
|
||||
|
||||
func checkArgs(_ args: [ParamInfo], _ funcDecl: FunctionDeclSyntax) throws {
|
||||
func checkArgs(_ args: [ParamInfo], _ funcComponents: FunctionParts) throws {
|
||||
var argByIndex: [Int: ParamInfo] = [:]
|
||||
var ret: ParamInfo? = nil
|
||||
let paramCount = funcDecl.signature.parameterClause.parameters.count
|
||||
let paramCount = funcComponents.signature.parameterClause.parameters.count
|
||||
try args.forEach { pointerInfo in
|
||||
switch pointerInfo.pointerIndex {
|
||||
case .param(let i):
|
||||
if i < 1 || i > paramCount {
|
||||
let noteMessage =
|
||||
paramCount > 0
|
||||
? "function \(funcDecl.name) has parameter indices 1..\(paramCount)"
|
||||
: "function \(funcDecl.name) has no parameters"
|
||||
? "function \(funcComponents.name) has parameter indices 1..\(paramCount)"
|
||||
: "function \(funcComponents.name) has no parameters"
|
||||
throw DiagnosticError(
|
||||
"pointer index out of bounds", node: pointerInfo.original,
|
||||
notes: [
|
||||
Note(node: Syntax(funcDecl.name), message: MacroExpansionNoteMessage(noteMessage))
|
||||
Note(node: Syntax(funcComponents.name), message: MacroExpansionNoteMessage(noteMessage))
|
||||
])
|
||||
}
|
||||
if argByIndex[i] != nil {
|
||||
@@ -1316,7 +1320,7 @@ func isInout(_ type: TypeSyntax) -> Bool {
|
||||
}
|
||||
|
||||
func getReturnLifetimeAttribute(
|
||||
_ funcDecl: FunctionDeclSyntax,
|
||||
_ funcDecl: FunctionParts,
|
||||
_ dependencies: [SwiftifyExpr: [LifetimeDependence]]
|
||||
) -> [AttributeListSyntax.Element] {
|
||||
let returnDependencies = dependencies[.`return`, default: []]
|
||||
@@ -1473,9 +1477,9 @@ class CountExprRewriter: SyntaxRewriter {
|
||||
}
|
||||
}
|
||||
|
||||
func renameParameterNamesIfNeeded(_ funcDecl: FunctionDeclSyntax) -> (FunctionDeclSyntax, CountExprRewriter) {
|
||||
let params = funcDecl.signature.parameterClause.parameters
|
||||
let funcName = funcDecl.name.withoutBackticks.trimmed.text
|
||||
func renameParameterNamesIfNeeded(_ funcComponents: FunctionParts) -> (FunctionParts, CountExprRewriter) {
|
||||
let params = funcComponents.signature.parameterClause.parameters
|
||||
let funcName = funcComponents.name.withoutBackticks.trimmed.text
|
||||
let shouldRename = params.contains(where: { param in
|
||||
let paramName = param.name.trimmed.text
|
||||
return paramName == "_" || paramName == funcName || "`\(paramName)`" == funcName
|
||||
@@ -1499,13 +1503,32 @@ func renameParameterNamesIfNeeded(_ funcDecl: FunctionDeclSyntax) -> (FunctionDe
|
||||
}
|
||||
return newParam
|
||||
}
|
||||
let newDecl = if renamedParams.count > 0 {
|
||||
funcDecl.with(\.signature.parameterClause.parameters, FunctionParameterListSyntax(newParams))
|
||||
let newSig = if renamedParams.count > 0 {
|
||||
funcComponents.signature.with(\.parameterClause.parameters, FunctionParameterListSyntax(newParams))
|
||||
} else {
|
||||
// Keeps source locations for diagnostics, in the common case where nothing was renamed
|
||||
funcDecl
|
||||
funcComponents.signature
|
||||
}
|
||||
return (newDecl, CountExprRewriter(renamedParams))
|
||||
return (FunctionParts(signature: newSig, name: funcComponents.name, attributes: funcComponents.attributes),
|
||||
CountExprRewriter(renamedParams))
|
||||
}
|
||||
|
||||
struct FunctionParts {
|
||||
let signature: FunctionSignatureSyntax
|
||||
let name: TokenSyntax
|
||||
let attributes: AttributeListSyntax
|
||||
}
|
||||
|
||||
func deconstructFunction(_ declaration: some DeclSyntaxProtocol) throws -> FunctionParts {
|
||||
if let origFuncDecl = declaration.as(FunctionDeclSyntax.self) {
|
||||
return FunctionParts(signature: origFuncDecl.signature, name: origFuncDecl.name,
|
||||
attributes: origFuncDecl.attributes)
|
||||
}
|
||||
if let origInitDecl = declaration.as(InitializerDeclSyntax.self) {
|
||||
return FunctionParts(signature: origInitDecl.signature, name: origInitDecl.initKeyword,
|
||||
attributes: origInitDecl.attributes)
|
||||
}
|
||||
throw DiagnosticError("@_SwiftifyImport only works on functions and initializers", node: declaration)
|
||||
}
|
||||
|
||||
/// A macro that adds safe(r) wrappers for functions with unsafe pointer types.
|
||||
@@ -1521,10 +1544,8 @@ public struct SwiftifyImportMacro: PeerMacro {
|
||||
in context: some MacroExpansionContext
|
||||
) throws -> [DeclSyntax] {
|
||||
do {
|
||||
guard let origFuncDecl = declaration.as(FunctionDeclSyntax.self) else {
|
||||
throw DiagnosticError("@_SwiftifyImport only works on functions", node: declaration)
|
||||
}
|
||||
let (funcDecl, rewriter) = renameParameterNamesIfNeeded(origFuncDecl)
|
||||
let origFuncComponents = try deconstructFunction(declaration)
|
||||
let (funcComponents, rewriter) = renameParameterNamesIfNeeded(origFuncComponents)
|
||||
|
||||
let argumentList = node.arguments!.as(LabeledExprListSyntax.self)!
|
||||
var arguments = [LabeledExprSyntax](argumentList)
|
||||
@@ -1540,10 +1561,10 @@ public struct SwiftifyImportMacro: PeerMacro {
|
||||
var lifetimeDependencies: [SwiftifyExpr: [LifetimeDependence]] = [:]
|
||||
var parsedArgs = try arguments.compactMap {
|
||||
try parseMacroParam(
|
||||
$0, funcDecl.signature, rewriter, nonescapingPointers: &nonescapingPointers,
|
||||
$0, funcComponents.signature, rewriter, nonescapingPointers: &nonescapingPointers,
|
||||
lifetimeDependencies: &lifetimeDependencies)
|
||||
}
|
||||
parsedArgs.append(contentsOf: try parseCxxSpansInSignature(funcDecl.signature, typeMappings))
|
||||
parsedArgs.append(contentsOf: try parseCxxSpansInSignature(funcComponents.signature, typeMappings))
|
||||
setNonescapingPointers(&parsedArgs, nonescapingPointers)
|
||||
setLifetimeDependencies(&parsedArgs, lifetimeDependencies)
|
||||
// We only transform non-escaping spans.
|
||||
@@ -1554,7 +1575,7 @@ public struct SwiftifyImportMacro: PeerMacro {
|
||||
return true
|
||||
}
|
||||
}
|
||||
try checkArgs(parsedArgs, funcDecl)
|
||||
try checkArgs(parsedArgs, funcComponents)
|
||||
parsedArgs.sort { a, b in
|
||||
// make sure return value cast to Span happens last so that withUnsafeBufferPointer
|
||||
// doesn't return a ~Escapable type
|
||||
@@ -1566,12 +1587,12 @@ public struct SwiftifyImportMacro: PeerMacro {
|
||||
}
|
||||
return paramOrReturnIndex(a.pointerIndex) < paramOrReturnIndex(b.pointerIndex)
|
||||
}
|
||||
let baseBuilder = FunctionCallBuilder(funcDecl)
|
||||
let baseBuilder = FunctionCallBuilder(funcComponents)
|
||||
|
||||
let builder: BoundsCheckedThunkBuilder = parsedArgs.reduce(
|
||||
baseBuilder,
|
||||
{ (prev, parsedArg) in
|
||||
parsedArg.getBoundsCheckedThunkBuilder(prev, funcDecl)
|
||||
parsedArg.getBoundsCheckedThunkBuilder(prev, funcComponents)
|
||||
})
|
||||
let newSignature = try builder.buildFunctionSignature([:], nil)
|
||||
var eliminatedArgs = Set<Int>()
|
||||
@@ -1580,15 +1601,22 @@ public struct SwiftifyImportMacro: PeerMacro {
|
||||
let checks = (basicChecks + compoundChecks).map { e in
|
||||
CodeBlockItemSyntax(leadingTrivia: "\n", item: e)
|
||||
}
|
||||
let call = CodeBlockItemSyntax(
|
||||
item: CodeBlockItemSyntax.Item(
|
||||
ReturnStmtSyntax(
|
||||
returnKeyword: .keyword(.return, trailingTrivia: " "),
|
||||
expression: try builder.buildFunctionCall([:]))))
|
||||
var call : CodeBlockItemSyntax
|
||||
if declaration.is(InitializerDeclSyntax.self) {
|
||||
call = CodeBlockItemSyntax(
|
||||
item: CodeBlockItemSyntax.Item(
|
||||
try builder.buildFunctionCall([:])))
|
||||
} else {
|
||||
call = CodeBlockItemSyntax(
|
||||
item: CodeBlockItemSyntax.Item(
|
||||
ReturnStmtSyntax(
|
||||
returnKeyword: .keyword(.return, trailingTrivia: " "),
|
||||
expression: try builder.buildFunctionCall([:]))))
|
||||
}
|
||||
let body = CodeBlockSyntax(statements: CodeBlockItemListSyntax(checks + [call]))
|
||||
let returnLifetimeAttribute = getReturnLifetimeAttribute(funcDecl, lifetimeDependencies)
|
||||
let returnLifetimeAttribute = getReturnLifetimeAttribute(funcComponents, lifetimeDependencies)
|
||||
let lifetimeAttrs =
|
||||
returnLifetimeAttribute + paramLifetimeAttributes(newSignature, funcDecl.attributes)
|
||||
returnLifetimeAttribute + paramLifetimeAttributes(newSignature, funcComponents.attributes)
|
||||
let availabilityAttr = try getAvailability(newSignature, spanAvailability)
|
||||
let disfavoredOverload: [AttributeListSyntax.Element] =
|
||||
[
|
||||
@@ -1597,13 +1625,7 @@ public struct SwiftifyImportMacro: PeerMacro {
|
||||
atSign: .atSignToken(),
|
||||
attributeName: IdentifierTypeSyntax(name: "_disfavoredOverload")))
|
||||
]
|
||||
let newFunc =
|
||||
funcDecl
|
||||
.with(\.signature, newSignature)
|
||||
.with(\.body, body)
|
||||
.with(
|
||||
\.attributes,
|
||||
funcDecl.attributes.filter { e in
|
||||
let attributes = funcComponents.attributes.filter { e in
|
||||
switch e {
|
||||
case .attribute(let attr):
|
||||
// don't apply this macro recursively, and avoid dupe _alwaysEmitIntoClient
|
||||
@@ -1619,9 +1641,23 @@ public struct SwiftifyImportMacro: PeerMacro {
|
||||
]
|
||||
+ availabilityAttr
|
||||
+ lifetimeAttrs
|
||||
+ disfavoredOverload)
|
||||
.with(\.leadingTrivia, node.leadingTrivia + .docLineComment("/// This is an auto-generated wrapper for safer interop\n"))
|
||||
return [DeclSyntax(newFunc)]
|
||||
+ disfavoredOverload
|
||||
let trivia = node.leadingTrivia + .docLineComment("/// This is an auto-generated wrapper for safer interop\n")
|
||||
if let origFuncDecl = declaration.as(FunctionDeclSyntax.self) {
|
||||
return [DeclSyntax(origFuncDecl
|
||||
.with(\.signature, newSignature)
|
||||
.with(\.body, body)
|
||||
.with(\.attributes, AttributeListSyntax(attributes))
|
||||
.with(\.leadingTrivia, trivia))]
|
||||
}
|
||||
if let origInitDecl = declaration.as(InitializerDeclSyntax.self) {
|
||||
return [DeclSyntax(origInitDecl
|
||||
.with(\.signature, newSignature)
|
||||
.with(\.body, body)
|
||||
.with(\.attributes, AttributeListSyntax(attributes))
|
||||
.with(\.leadingTrivia, trivia))]
|
||||
}
|
||||
return []
|
||||
} catch let error as DiagnosticError {
|
||||
context.diagnose(
|
||||
Diagnostic(
|
||||
@@ -1686,6 +1722,9 @@ extension FunctionParameterSyntax {
|
||||
|
||||
extension TokenSyntax {
|
||||
public var withoutBackticks: TokenSyntax {
|
||||
if self.identifier == nil {
|
||||
return self
|
||||
}
|
||||
return .identifier(self.identifier!.name)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user