From b6a09d1b33d1db413b024e4110ec5677a5d855fe Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Sun, 6 Apr 2025 00:48:42 +0100 Subject: [PATCH 1/3] Reapply: [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 c8e910c4661ee..647f8dc6af735 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -5612,6 +5612,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 e961b08b960c2..f861863a8fb9c 100644 --- a/test/Macros/macro_expand.swift +++ b/test/Macros/macro_expand.swift @@ -667,6 +667,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 + } +} From 14bd41159e6dd5c32b8c9c155097751fefa053a0 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Wed, 16 Apr 2025 12:25:07 +0100 Subject: [PATCH 2/3] NFC: Abstract away the use of '7' to represent the next language mode Introduce `Version::getFutureMajorLanguageVersion` to make it easier to find clients that will need to be updated once we have a new language mode. --- include/swift/AST/DiagnosticEngine.h | 11 +++++++++++ include/swift/Basic/Version.h | 7 +++++++ lib/AST/DiagnosticEngine.cpp | 7 ++++++- lib/Basic/Version.cpp | 17 ++++++++++------- lib/Sema/MiscDiagnostics.cpp | 11 ++++++----- lib/Sema/TypeCheckAttr.cpp | 2 +- lib/Sema/TypeCheckConcurrency.cpp | 2 +- lib/Sema/TypeCheckProtocol.cpp | 3 ++- 8 files changed, 44 insertions(+), 16 deletions(-) diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index ef4f58b3a76e6..c3761464f9008 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -390,6 +390,17 @@ namespace swift { return limitBehaviorUntilSwiftVersion(limit, languageMode); } + /// Limit the diagnostic behavior to warning until the next future + /// language mode. + /// + /// This should be preferred over passing the next major version to + /// `warnUntilSwiftVersion` to make it easier to find and update clients + /// when a new language mode is introduced. + /// + /// This helps stage in fixes for stricter diagnostics as warnings + /// until the next major language version. + InFlightDiagnostic &warnUntilFutureSwiftVersion(); + /// Limit the diagnostic behavior to warning until the specified version. /// /// This helps stage in fixes for stricter diagnostics as warnings diff --git a/include/swift/Basic/Version.h b/include/swift/Basic/Version.h index 092a4e626e518..a97ef7eec93b2 100644 --- a/include/swift/Basic/Version.h +++ b/include/swift/Basic/Version.h @@ -131,6 +131,13 @@ class Version { /// SWIFT_VERSION_MINOR. static Version getCurrentLanguageVersion(); + /// Returns a major version to represent the next future language mode. This + /// exists to make it easier to find and update clients when a new language + /// mode is added. + static constexpr unsigned getFutureMajorLanguageVersion() { + return 7; + } + // List of backward-compatibility versions that we permit passing as // -swift-version static std::array getValidEffectiveVersions() { diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 5e8c5054a8461..7ac3e7166d441 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -452,7 +452,7 @@ InFlightDiagnostic::limitBehaviorUntilSwiftVersion( // version. We do this before limiting the behavior, because // wrapIn will result in the behavior of the wrapping diagnostic. if (limit >= DiagnosticBehavior::Warning) { - if (majorVersion > 6) { + if (majorVersion >= version::Version::getFutureMajorLanguageVersion()) { wrapIn(diag::error_in_a_future_swift_lang_mode); } else { wrapIn(diag::error_in_swift_lang_mode, majorVersion); @@ -472,6 +472,11 @@ InFlightDiagnostic::limitBehaviorUntilSwiftVersion( return *this; } +InFlightDiagnostic &InFlightDiagnostic::warnUntilFutureSwiftVersion() { + using namespace version; + return warnUntilSwiftVersion(Version::getFutureMajorLanguageVersion()); +} + InFlightDiagnostic & InFlightDiagnostic::warnUntilSwiftVersion(unsigned majorVersion) { return limitBehaviorUntilSwiftVersion(DiagnosticBehavior::Warning, diff --git a/lib/Basic/Version.cpp b/lib/Basic/Version.cpp index 94085bf601654..13eb04be0db84 100644 --- a/lib/Basic/Version.cpp +++ b/lib/Basic/Version.cpp @@ -181,16 +181,19 @@ std::optional Version::getEffectiveLanguageVersion() const { static_assert(SWIFT_VERSION_MAJOR == 6, "getCurrentLanguageVersion is no longer correct here"); return Version::getCurrentLanguageVersion(); - case 7: - // Allow version '7' in asserts compilers *only* so that we can start - // testing changes planned for after Swift 6. Note that it's still not - // listed in `Version::getValidEffectiveVersions()`. - // FIXME: When Swift 7 becomes real, remove 'REQUIRES: swift7' from tests - // using '-swift-version 7'. + + // FIXME: When Swift 7 becomes real, remove 'REQUIRES: swift7' from tests + // using '-swift-version 7'. + + case Version::getFutureMajorLanguageVersion(): + // Allow the future language mode version in asserts compilers *only* so + // that we can start testing changes planned for after the current latest + // language mode. Note that it'll not be listed in + // `Version::getValidEffectiveVersions()`. #ifdef NDEBUG LLVM_FALLTHROUGH; #else - return Version{7}; + return Version{Version::getFutureMajorLanguageVersion()}; #endif default: return std::nullopt; diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 5f8180a96f144..640c220e34ef7 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -2240,11 +2240,12 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker { invalidImplicitSelfShouldOnlyWarn510(base, closure)) { warnUntilVersion.emplace(6); } - // Prior to Swift 7, downgrade to a warning if we're in a macro to preserve - // compatibility with the Swift 6 diagnostic behavior where we previously - // skipped diagnosing. - if (!Ctx.isSwiftVersionAtLeast(7) && isInMacro()) - warnUntilVersion.emplace(7); + // Prior to the next language mode, downgrade to a warning if we're in a + // macro to preserve compatibility with the Swift 6 diagnostic behavior + // where we previously skipped diagnosing. + auto futureVersion = version::Version::getFutureMajorLanguageVersion(); + if (!Ctx.isSwiftVersionAtLeast(futureVersion) && isInMacro()) + warnUntilVersion.emplace(futureVersion); auto diag = Ctx.Diags.diagnose(loc, ID, std::move(Args)...); if (warnUntilVersion) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 656db55f364ef..ff551cd7cb23d 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -1260,7 +1260,7 @@ void AttributeChecker::visitAccessControlAttr(AccessControlAttr *attr) { diagnose(attr->getLocation(), diag::access_control_non_objc_open_member, VD) .fixItReplace(attr->getRange(), "public") - .warnUntilSwiftVersion(7); + .warnUntilFutureSwiftVersion(); } } } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 019c98dbc7f32..174cc5cd4722c 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -2675,7 +2675,7 @@ namespace { fromType, toType); if (downgradeToWarning) - diag.warnUntilSwiftVersion(7); + diag.warnUntilFutureSwiftVersion(); } for (auto type : nonSendableTypes) { diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 2206251616367..3ac509fd88d92 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -5185,7 +5185,8 @@ static bool diagnoseTypeWitnessAvailability( return false; // In Swift 6 and earlier type witness availability diagnostics are warnings. - const unsigned warnBeforeVersion = 7; + using namespace version; + const unsigned warnBeforeVersion = Version::getFutureMajorLanguageVersion(); bool shouldError = ctx.LangOpts.EffectiveLanguageVersion.isVersionAtLeast(warnBeforeVersion); From b36eb57dbd50187ddbebe84e13cd68c8fc3cb845 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Wed, 16 Apr 2025 12:25:07 +0100 Subject: [PATCH 3/3] [Sema] Downgrade `noasync` diagnostic to warning for closure macro args Downgrade to a warning until the next language mode. This is necessary since we previously missed coercing macro arguments to parameter types, resulting in cases where closure arguments weren't being treated as `async` when they should have been. rdar://149328745 --- include/swift/AST/Expr.h | 21 +++++++- lib/Sema/CSApply.cpp | 17 ++++-- lib/Sema/TypeCheckAvailability.cpp | 54 +++++++++++++------ test/Macros/issue-80835.swift | 84 ++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 23 deletions(-) create mode 100644 test/Macros/issue-80835.swift diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 5579cf818cdd7..2d052da7ce819 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -267,7 +267,7 @@ class alignas(8) Expr : public ASTAllocated { Kind : 2 ); - SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1+1, /// True if closure parameters were synthesized from anonymous closure /// variables. HasAnonymousClosureVars : 1, @@ -295,7 +295,12 @@ class alignas(8) Expr : public ASTAllocated { /// isolation checks when it either specifies or inherits isolation /// and was passed as an argument to a function that is not fully /// concurrency checked. - RequiresDynamicIsolationChecking : 1 + RequiresDynamicIsolationChecking : 1, + + /// Whether this closure was type-checked as an argument to a macro. This + /// is only populated after type-checking, and only exists for diagnostic + /// logic. Do not add more uses of this. + IsMacroArgument : 1 ); SWIFT_INLINE_BITFIELD_FULL(BindOptionalExpr, Expr, 16, @@ -4316,6 +4321,7 @@ class ClosureExpr : public AbstractClosureExpr { Bits.ClosureExpr.IsPassedToSendingParameter = false; Bits.ClosureExpr.NoGlobalActorAttribute = false; Bits.ClosureExpr.RequiresDynamicIsolationChecking = false; + Bits.ClosureExpr.IsMacroArgument = false; } SourceRange getSourceRange() const; @@ -4394,6 +4400,17 @@ class ClosureExpr : public AbstractClosureExpr { Bits.ClosureExpr.RequiresDynamicIsolationChecking = value; } + /// Whether this closure was type-checked as an argument to a macro. This + /// is only populated after type-checking, and only exists for diagnostic + /// logic. Do not add more uses of this. + bool isMacroArgument() const { + return Bits.ClosureExpr.IsMacroArgument; + } + + void setIsMacroArgument(bool value = true) { + Bits.ClosureExpr.IsMacroArgument = value; + } + /// Determine whether this closure expression has an /// explicitly-specified result type. bool hasExplicitResultType() const { return ArrowLoc.isValid(); } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 647f8dc6af735..56bfa85819ded 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -6085,13 +6085,15 @@ static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee, static void applyContextualClosureFlags(Expr *expr, bool implicitSelfCapture, bool inheritActorContext, bool isPassedToSendingParameter, - bool requiresDynamicIsolationChecking) { + bool requiresDynamicIsolationChecking, + bool isMacroArg) { if (auto closure = dyn_cast(expr)) { closure->setAllowsImplicitSelfCapture(implicitSelfCapture); closure->setInheritsActorContext(inheritActorContext); closure->setIsPassedToSendingParameter(isPassedToSendingParameter); closure->setRequiresDynamicIsolationChecking( requiresDynamicIsolationChecking); + closure->setIsMacroArgument(isMacroArg); return; } @@ -6099,14 +6101,16 @@ static void applyContextualClosureFlags(Expr *expr, bool implicitSelfCapture, applyContextualClosureFlags(captureList->getClosureBody(), implicitSelfCapture, inheritActorContext, isPassedToSendingParameter, - requiresDynamicIsolationChecking); + requiresDynamicIsolationChecking, + isMacroArg); } if (auto identity = dyn_cast(expr)) { applyContextualClosureFlags(identity->getSubExpr(), implicitSelfCapture, inheritActorContext, isPassedToSendingParameter, - requiresDynamicIsolationChecking); + requiresDynamicIsolationChecking, + isMacroArg); } } @@ -6231,7 +6235,8 @@ ArgumentList *ExprRewriter::coerceCallArguments( }(); auto applyFlagsToArgument = [¶mInfo, - &closuresRequireDynamicIsolationChecking]( + &closuresRequireDynamicIsolationChecking, + &locator]( unsigned paramIdx, Expr *argument) { if (!isClosureLiteralExpr(argument)) return; @@ -6239,11 +6244,13 @@ ArgumentList *ExprRewriter::coerceCallArguments( bool isImplicitSelfCapture = paramInfo.isImplicitSelfCapture(paramIdx); bool inheritsActorContext = paramInfo.inheritsActorContext(paramIdx); bool isPassedToSendingParameter = paramInfo.isSendingParameter(paramIdx); + bool isMacroArg = isExpr(locator.getAnchor()); applyContextualClosureFlags(argument, isImplicitSelfCapture, inheritsActorContext, isPassedToSendingParameter, - closuresRequireDynamicIsolationChecking); + closuresRequireDynamicIsolationChecking, + isMacroArg); }; // Quickly test if any further fix-ups for the argument types are necessary. diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index b2f9c34dd5f13..03cffcf61d3c9 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -276,16 +276,18 @@ static bool shouldAllowReferenceToUnavailableInSwiftDeclaration( return false; } -// Utility function to help determine if noasync diagnostics are still -// appropriate even if a `DeclContext` returns `false` from `isAsyncContext()`. -static bool shouldTreatDeclContextAsAsyncForDiagnostics(const DeclContext *DC) { - if (auto *D = DC->getAsDecl()) - if (auto *FD = dyn_cast(D)) +/// Retrieve the innermost DeclContext that should be consulted for noasync +/// checking. +static const DeclContext * +getInnermostDeclContextForNoAsync(const DeclContext *DC) { + if (auto *D = DC->getAsDecl()) { + if (auto *FD = dyn_cast(D)) { if (FD->isDeferBody()) // If this is a defer body, we should delegate to its parent. - return shouldTreatDeclContextAsAsyncForDiagnostics(DC->getParent()); - - return DC->isAsyncContext(); + return getInnermostDeclContextForNoAsync(DC->getParent()); + } + } + return DC; } /// A class that walks the AST to find the innermost (i.e., deepest) node that @@ -2758,7 +2760,8 @@ static bool diagnoseDeclAsyncAvailability(const ValueDecl *D, SourceRange R, const Expr *call, const ExportContext &Where) { // If we are not in an (effective) async context, don't check it - if (!shouldTreatDeclContextAsAsyncForDiagnostics(Where.getDeclContext())) + auto *noAsyncDC = getInnermostDeclContextForNoAsync(Where.getDeclContext()); + if (!noAsyncDC->isAsyncContext()) return false; ASTContext &ctx = Where.getDeclContext()->getASTContext(); @@ -2774,14 +2777,27 @@ diagnoseDeclAsyncAvailability(const ValueDecl *D, SourceRange R, } } + // In Swift 6 we previously didn't coerce macro arguments to parameter types, + // so closure arguments may be treated as async in cases where they weren't in + // Swift 6. As such we need to warn if the use is within a closure macro + // argument until the next language mode. + auto shouldWarnUntilFutureVersion = [&]() { + auto *CE = dyn_cast(noAsyncDC); + return CE && CE->isMacroArgument(); + }; + // @available(noasync) spelling if (auto attr = D->getNoAsyncAttr()) { SourceLoc diagLoc = call ? call->getLoc() : R.Start; auto diag = ctx.Diags.diagnose(diagLoc, diag::async_unavailable_decl, D, attr->getMessage()); - diag.warnUntilSwiftVersion(6); - diag.limitBehaviorWithPreconcurrency(DiagnosticBehavior::Warning, - D->preconcurrency()); + if (D->preconcurrency()) { + diag.limitBehavior(DiagnosticBehavior::Warning); + } else if (shouldWarnUntilFutureVersion()) { + diag.warnUntilFutureSwiftVersion(); + } else { + diag.warnUntilSwiftVersion(6); + } if (!attr->getRename().empty()) { fixItAvailableAttrRename(diag, R, D, attr->getRename(), call); @@ -2797,10 +2813,16 @@ diagnoseDeclAsyncAvailability(const ValueDecl *D, SourceRange R, // @_unavailableFromAsync spelling const UnavailableFromAsyncAttr *attr = D->getAttrs().getAttribute(); - SourceLoc diagLoc = call ? call->getLoc() : R.Start; - ctx.Diags - .diagnose(diagLoc, diag::async_unavailable_decl, D, attr->Message) - .warnUntilSwiftVersion(6); + { + SourceLoc diagLoc = call ? call->getLoc() : R.Start; + auto diag = ctx.Diags.diagnose(diagLoc, diag::async_unavailable_decl, D, + attr->Message); + if (shouldWarnUntilFutureVersion()) { + diag.warnUntilFutureSwiftVersion(); + } else { + diag.warnUntilSwiftVersion(6); + } + } D->diagnose(diag::decl_declared_here, D); return true; } diff --git a/test/Macros/issue-80835.swift b/test/Macros/issue-80835.swift new file mode 100644 index 0000000000000..863e988ea6e34 --- /dev/null +++ b/test/Macros/issue-80835.swift @@ -0,0 +1,84 @@ +// RUN: %empty-directory(%t) +// RUN: %host-build-swift -swift-version 5 -emit-library -o %t/%target-library-name(MacroDefinition) -module-name=MacroDefinition %S/Inputs/syntax_macro_definitions.swift + +// RUN: %target-typecheck-verify-swift -swift-version 6 -load-plugin-library %t/%target-library-name(MacroDefinition) -verify-additional-prefix swift6- +// RUN: %target-typecheck-verify-swift -swift-version 7 -load-plugin-library %t/%target-library-name(MacroDefinition) -verify-additional-prefix swift7- + +// REQUIRES: swift_swift_parser +// REQUIRES: swift7 + +// https://github.com/swiftlang/swift/issues/80835 + +@available(*, noasync) +func noasyncFn() {} + +@_unavailableFromAsync +func unavailableFromAsyncFn() {} // expected-note {{'unavailableFromAsyncFn()' declared here}} + +@freestanding(expression) +macro asyncMacro(_ fn: () async -> Void) = #externalMacro(module: "MacroDefinition", type: "GenericToVoidMacro") + +@freestanding(declaration) +macro asyncMacroDecl(_ fn: () async -> Void) = #externalMacro(module: "MacroDefinition", type: "EmptyDeclarationMacro") + +@attached(peer) +macro AsyncMacro(_ fn: () async -> Void) = #externalMacro(module: "MacroDefinition", type: "WrapperMacro") + +func takesAsyncFn(_ fn: () async -> Void) {} + +#asyncMacro { + defer { + noasyncFn() + // expected-swift7-error@-1 {{global function 'noasyncFn' is unavailable from asynchronous contexts}} + // expected-swift6-warning@-2 {{global function 'noasyncFn' is unavailable from asynchronous contexts; this will be an error in a future Swift language mode}} + } + noasyncFn() + // expected-swift7-error@-1 {{global function 'noasyncFn' is unavailable from asynchronous contexts}} + // expected-swift6-warning@-2 {{global function 'noasyncFn' is unavailable from asynchronous contexts; this will be an error in a future Swift language mode}} + + unavailableFromAsyncFn() + // expected-swift7-error@-1 {{global function 'unavailableFromAsyncFn' is unavailable from asynchronous contexts}} + // expected-swift6-warning@-2 {{global function 'unavailableFromAsyncFn' is unavailable from asynchronous contexts; this will be an error in a future Swift language mode}} + + // This was always an error. + let _: () async -> Void = { + noasyncFn() + // expected-error@-1 {{global function 'noasyncFn' is unavailable from asynchronous contexts}} + } +} + +// This was always an error. +takesAsyncFn { + noasyncFn() + // expected-error@-1 {{global function 'noasyncFn' is unavailable from asynchronous contexts}} +} + +#asyncMacroDecl { + noasyncFn() + // expected-swift7-error@-1 {{global function 'noasyncFn' is unavailable from asynchronous contexts}} + // expected-swift6-warning@-2 {{global function 'noasyncFn' is unavailable from asynchronous contexts; this will be an error in a future Swift language mode}} +} + +typealias Magic = T + +// expected-swift7-error@+2 {{global function 'noasyncFn' is unavailable from asynchronous contexts}} +// expected-swift6-warning@+1 {{global function 'noasyncFn' is unavailable from asynchronous contexts; this will be an error in a future Swift language mode}} +@AsyncMacro({ noasyncFn() }) +func foo() { + #asyncMacro(({ + noasyncFn() + // expected-swift7-error@-1 {{global function 'noasyncFn' is unavailable from asynchronous contexts}} + // expected-swift6-warning@-2 {{global function 'noasyncFn' is unavailable from asynchronous contexts; this will be an error in a future Swift language mode}} + })) + + #asyncMacroDecl(({ + noasyncFn() + // expected-swift7-error@-1 {{global function 'noasyncFn' is unavailable from asynchronous contexts}} + // expected-swift6-warning@-2 {{global function 'noasyncFn' is unavailable from asynchronous contexts; this will be an error in a future Swift language mode}} + })) + + // This was never treated as async. + #asyncMacro({ + noasyncFn() + } as Magic) +}