From ce260ae75a060a5c86e70e9e0555b68525ec8d6a Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Sun, 6 Apr 2025 00:48:42 +0100 Subject: [PATCH] [CS] Make sure macro arguments go through `coerceCallArguments` Previously we would avoid rewriting the arguments in CSApply, but that can result in incorrect behavior in MiscDiagnostics passes, e.g incorrectly treating all closure arguments as escaping. Make sure we rewrite the arguments as we would in regular type-checking. rdar://148665502 --- lib/Sema/CSApply.cpp | 12 +++++++ test/Macros/macro_expand.swift | 22 ++++++++++++ test/Macros/macro_misc_diags.swift | 58 ++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 1f4cdb7fe0152..ee12fa37c8bbf 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -5580,6 +5580,18 @@ namespace { E->setMacroRef(macroRef); E->setType(expandedType); + auto fnType = + simplifyType(overload.adjustedOpenedType)->castTo(); + + auto newArgs = coerceCallArguments( + E->getArgs(), fnType, macroRef, /*applyExpr=*/nullptr, + cs.getConstraintLocator(E, ConstraintLocator::ApplyArgument), + solution.getAppliedPropertyWrappers(E)); + if (!newArgs) + return nullptr; + + E->setArgs(newArgs); + // FIXME: Expansion should be lazy. // i.e. 'ExpandMacroExpansionExprRequest' should be sinked into // 'getRewritten()', and performed on-demand. Unfortunately that requires diff --git a/test/Macros/macro_expand.swift b/test/Macros/macro_expand.swift index f52394d6a297e..554560efcb2d3 100644 --- a/test/Macros/macro_expand.swift +++ b/test/Macros/macro_expand.swift @@ -659,6 +659,28 @@ func testLocalAccessorMacroWithAutoclosure() { takesAutoclosure(1) } +@propertyWrapper +struct SomePropertyWrapper { + var wrappedValue: T + init(wrappedValue: T) { + self.wrappedValue = wrappedValue + } + init(projectedValue: Self) { + self = projectedValue + } + var projectedValue: Self { self } +} + +// Property wrappers on macros probably aren't all that useful, but make sure +// we don't crash. +@freestanding(expression) +macro hasPropertyWrapperParam(@SomePropertyWrapper x: Int) = #externalMacro(module: "MacroDefinition", type: "GenericToVoidMacro") + +func testPropertyWrapperMacro() { + #hasPropertyWrapperParam(x: 0) + #hasPropertyWrapperParam($x: .init(wrappedValue: 0)) +} + #if TEST_DIAGNOSTICS @freestanding(expression) macro missingMacro() = #externalMacro(module: "MacroDefinition", type: "BluhBlah") diff --git a/test/Macros/macro_misc_diags.swift b/test/Macros/macro_misc_diags.swift index 3bd8fd8035b4b..4201c04639621 100644 --- a/test/Macros/macro_misc_diags.swift +++ b/test/Macros/macro_misc_diags.swift @@ -36,6 +36,32 @@ public struct TrailingClosureMacro: ExpressionMacro { } } +public struct CallClosureMacro: ExpressionMacro { + public static func expansion( + of macro: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + guard let argument = macro.trailingClosure else { + fatalError() + } + return "\(argument)()" + } +} + +public struct AddFunctionThatCallsClosureMacro: PeerMacro { + public static func expansion( + of node: AttributeSyntax, + providingPeersOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { + guard case .argumentList(let args) = node.arguments else { + fatalError() + } + let arg = args.first! + return ["func qux() { \(arg)() }"] + } +} + public struct MakeBinding : DeclarationMacro { static public func expansion( of node: some FreestandingMacroExpansionSyntax, @@ -64,6 +90,12 @@ macro identity(_ x: T) -> T = #externalMacro(module: "MacroPlugin", type: "Id @freestanding(expression) macro trailingClosure(_ x: T) -> T = #externalMacro(module: "MacroPlugin", type: "TrailingClosureMacro") +@freestanding(expression) +macro takesNonEscapingClosure(_ x: () -> Void) = #externalMacro(module: "MacroPlugin", type: "CallClosureMacro") + +@attached(peer, names: named(qux)) +macro AddFunctionThatCallsClosure(_ fn: () -> T) = #externalMacro(module: "MacroPlugin", type: "AddFunctionThatCallsClosureMacro") + @freestanding(declaration, names: named(x)) macro makeBinding(_ x: T) = #externalMacro(module: "MacroPlugin", type: "MakeBinding") @@ -160,3 +192,29 @@ class rdar138997009_Class { } } } + +// https://github.com/swiftlang/swift/issues/80561 +class TestNonEscaping { + func foo() {} + func bar() { + _ = #takesNonEscapingClosure { + foo() + } + _ = { + _ = #takesNonEscapingClosure { + foo() + // CHECK-DIAG: @__swiftmacro_6Client0017Clientswift_yEEFcfMX[[@LINE-3]]{{.*}}takesNonEscapingClosurefMf_.swift:2:9: error: call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit + // CHECK-DIAG: Client.swift:[[@LINE-2]]:9: warning: call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit; this will be an error in a future Swift language mode + } + } + + @AddFunctionThatCallsClosure({ foo() }) + func baz() {} + } + func qux() { + @AddFunctionThatCallsClosure({ _ = { foo() } }) + func baz() {} + // CHECK-DIAG: Client.swift:[[@LINE-2]]:42: warning: call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit; this will be an error in a future Swift language mode + // CHECK-DIAG: @__swiftmacro_6Client15TestNonEscapingC3quxyyF7baz_$l{{.*}}AddFunctionThatCallsClosurefMp_.swift:4:13: error: call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit + } +}