Skip to content

Commit 3c30234

Browse files
authored
Merge pull request #80853 from hamishknight/macro-async-warning
2 parents 1e92156 + b36eb57 commit 3c30234

14 files changed

+289
-39
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,17 @@ namespace swift {
390390
return limitBehaviorUntilSwiftVersion(limit, languageMode);
391391
}
392392

393+
/// Limit the diagnostic behavior to warning until the next future
394+
/// language mode.
395+
///
396+
/// This should be preferred over passing the next major version to
397+
/// `warnUntilSwiftVersion` to make it easier to find and update clients
398+
/// when a new language mode is introduced.
399+
///
400+
/// This helps stage in fixes for stricter diagnostics as warnings
401+
/// until the next major language version.
402+
InFlightDiagnostic &warnUntilFutureSwiftVersion();
403+
393404
/// Limit the diagnostic behavior to warning until the specified version.
394405
///
395406
/// This helps stage in fixes for stricter diagnostics as warnings

include/swift/AST/Expr.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
267267
Kind : 2
268268
);
269269

270-
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1,
270+
SWIFT_INLINE_BITFIELD(ClosureExpr, AbstractClosureExpr, 1+1+1+1+1+1+1+1,
271271
/// True if closure parameters were synthesized from anonymous closure
272272
/// variables.
273273
HasAnonymousClosureVars : 1,
@@ -295,7 +295,12 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
295295
/// isolation checks when it either specifies or inherits isolation
296296
/// and was passed as an argument to a function that is not fully
297297
/// concurrency checked.
298-
RequiresDynamicIsolationChecking : 1
298+
RequiresDynamicIsolationChecking : 1,
299+
300+
/// Whether this closure was type-checked as an argument to a macro. This
301+
/// is only populated after type-checking, and only exists for diagnostic
302+
/// logic. Do not add more uses of this.
303+
IsMacroArgument : 1
299304
);
300305

301306
SWIFT_INLINE_BITFIELD_FULL(BindOptionalExpr, Expr, 16,
@@ -4316,6 +4321,7 @@ class ClosureExpr : public AbstractClosureExpr {
43164321
Bits.ClosureExpr.IsPassedToSendingParameter = false;
43174322
Bits.ClosureExpr.NoGlobalActorAttribute = false;
43184323
Bits.ClosureExpr.RequiresDynamicIsolationChecking = false;
4324+
Bits.ClosureExpr.IsMacroArgument = false;
43194325
}
43204326

43214327
SourceRange getSourceRange() const;
@@ -4394,6 +4400,17 @@ class ClosureExpr : public AbstractClosureExpr {
43944400
Bits.ClosureExpr.RequiresDynamicIsolationChecking = value;
43954401
}
43964402

4403+
/// Whether this closure was type-checked as an argument to a macro. This
4404+
/// is only populated after type-checking, and only exists for diagnostic
4405+
/// logic. Do not add more uses of this.
4406+
bool isMacroArgument() const {
4407+
return Bits.ClosureExpr.IsMacroArgument;
4408+
}
4409+
4410+
void setIsMacroArgument(bool value = true) {
4411+
Bits.ClosureExpr.IsMacroArgument = value;
4412+
}
4413+
43974414
/// Determine whether this closure expression has an
43984415
/// explicitly-specified result type.
43994416
bool hasExplicitResultType() const { return ArrowLoc.isValid(); }

include/swift/Basic/Version.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,13 @@ class Version {
131131
/// SWIFT_VERSION_MINOR.
132132
static Version getCurrentLanguageVersion();
133133

134+
/// Returns a major version to represent the next future language mode. This
135+
/// exists to make it easier to find and update clients when a new language
136+
/// mode is added.
137+
static constexpr unsigned getFutureMajorLanguageVersion() {
138+
return 7;
139+
}
140+
134141
// List of backward-compatibility versions that we permit passing as
135142
// -swift-version <vers>
136143
static std::array<StringRef, 4> getValidEffectiveVersions() {

lib/AST/DiagnosticEngine.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ InFlightDiagnostic::limitBehaviorUntilSwiftVersion(
452452
// version. We do this before limiting the behavior, because
453453
// wrapIn will result in the behavior of the wrapping diagnostic.
454454
if (limit >= DiagnosticBehavior::Warning) {
455-
if (majorVersion > 6) {
455+
if (majorVersion >= version::Version::getFutureMajorLanguageVersion()) {
456456
wrapIn(diag::error_in_a_future_swift_lang_mode);
457457
} else {
458458
wrapIn(diag::error_in_swift_lang_mode, majorVersion);
@@ -472,6 +472,11 @@ InFlightDiagnostic::limitBehaviorUntilSwiftVersion(
472472
return *this;
473473
}
474474

475+
InFlightDiagnostic &InFlightDiagnostic::warnUntilFutureSwiftVersion() {
476+
using namespace version;
477+
return warnUntilSwiftVersion(Version::getFutureMajorLanguageVersion());
478+
}
479+
475480
InFlightDiagnostic &
476481
InFlightDiagnostic::warnUntilSwiftVersion(unsigned majorVersion) {
477482
return limitBehaviorUntilSwiftVersion(DiagnosticBehavior::Warning,

lib/Basic/Version.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,16 +181,19 @@ std::optional<Version> Version::getEffectiveLanguageVersion() const {
181181
static_assert(SWIFT_VERSION_MAJOR == 6,
182182
"getCurrentLanguageVersion is no longer correct here");
183183
return Version::getCurrentLanguageVersion();
184-
case 7:
185-
// Allow version '7' in asserts compilers *only* so that we can start
186-
// testing changes planned for after Swift 6. Note that it's still not
187-
// listed in `Version::getValidEffectiveVersions()`.
188-
// FIXME: When Swift 7 becomes real, remove 'REQUIRES: swift7' from tests
189-
// using '-swift-version 7'.
184+
185+
// FIXME: When Swift 7 becomes real, remove 'REQUIRES: swift7' from tests
186+
// using '-swift-version 7'.
187+
188+
case Version::getFutureMajorLanguageVersion():
189+
// Allow the future language mode version in asserts compilers *only* so
190+
// that we can start testing changes planned for after the current latest
191+
// language mode. Note that it'll not be listed in
192+
// `Version::getValidEffectiveVersions()`.
190193
#ifdef NDEBUG
191194
LLVM_FALLTHROUGH;
192195
#else
193-
return Version{7};
196+
return Version{Version::getFutureMajorLanguageVersion()};
194197
#endif
195198
default:
196199
return std::nullopt;

lib/Sema/CSApply.cpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5612,6 +5612,18 @@ namespace {
56125612
E->setMacroRef(macroRef);
56135613
E->setType(expandedType);
56145614

5615+
auto fnType =
5616+
simplifyType(overload.adjustedOpenedType)->castTo<FunctionType>();
5617+
5618+
auto newArgs = coerceCallArguments(
5619+
E->getArgs(), fnType, macroRef, /*applyExpr=*/nullptr,
5620+
cs.getConstraintLocator(E, ConstraintLocator::ApplyArgument),
5621+
solution.getAppliedPropertyWrappers(E));
5622+
if (!newArgs)
5623+
return nullptr;
5624+
5625+
E->setArgs(newArgs);
5626+
56155627
// FIXME: Expansion should be lazy.
56165628
// i.e. 'ExpandMacroExpansionExprRequest' should be sinked into
56175629
// 'getRewritten()', and performed on-demand. Unfortunately that requires
@@ -6073,28 +6085,32 @@ static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee,
60736085
static void applyContextualClosureFlags(Expr *expr, bool implicitSelfCapture,
60746086
bool inheritActorContext,
60756087
bool isPassedToSendingParameter,
6076-
bool requiresDynamicIsolationChecking) {
6088+
bool requiresDynamicIsolationChecking,
6089+
bool isMacroArg) {
60776090
if (auto closure = dyn_cast<ClosureExpr>(expr)) {
60786091
closure->setAllowsImplicitSelfCapture(implicitSelfCapture);
60796092
closure->setInheritsActorContext(inheritActorContext);
60806093
closure->setIsPassedToSendingParameter(isPassedToSendingParameter);
60816094
closure->setRequiresDynamicIsolationChecking(
60826095
requiresDynamicIsolationChecking);
6096+
closure->setIsMacroArgument(isMacroArg);
60836097
return;
60846098
}
60856099

60866100
if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
60876101
applyContextualClosureFlags(captureList->getClosureBody(),
60886102
implicitSelfCapture, inheritActorContext,
60896103
isPassedToSendingParameter,
6090-
requiresDynamicIsolationChecking);
6104+
requiresDynamicIsolationChecking,
6105+
isMacroArg);
60916106
}
60926107

60936108
if (auto identity = dyn_cast<IdentityExpr>(expr)) {
60946109
applyContextualClosureFlags(identity->getSubExpr(), implicitSelfCapture,
60956110
inheritActorContext,
60966111
isPassedToSendingParameter,
6097-
requiresDynamicIsolationChecking);
6112+
requiresDynamicIsolationChecking,
6113+
isMacroArg);
60986114
}
60996115
}
61006116

@@ -6219,19 +6235,22 @@ ArgumentList *ExprRewriter::coerceCallArguments(
62196235
}();
62206236

62216237
auto applyFlagsToArgument = [&paramInfo,
6222-
&closuresRequireDynamicIsolationChecking](
6238+
&closuresRequireDynamicIsolationChecking,
6239+
&locator](
62236240
unsigned paramIdx, Expr *argument) {
62246241
if (!isClosureLiteralExpr(argument))
62256242
return;
62266243

62276244
bool isImplicitSelfCapture = paramInfo.isImplicitSelfCapture(paramIdx);
62286245
bool inheritsActorContext = paramInfo.inheritsActorContext(paramIdx);
62296246
bool isPassedToSendingParameter = paramInfo.isSendingParameter(paramIdx);
6247+
bool isMacroArg = isExpr<MacroExpansionExpr>(locator.getAnchor());
62306248

62316249
applyContextualClosureFlags(argument, isImplicitSelfCapture,
62326250
inheritsActorContext,
62336251
isPassedToSendingParameter,
6234-
closuresRequireDynamicIsolationChecking);
6252+
closuresRequireDynamicIsolationChecking,
6253+
isMacroArg);
62356254
};
62366255

62376256
// Quickly test if any further fix-ups for the argument types are necessary.

lib/Sema/MiscDiagnostics.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2239,11 +2239,12 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker {
22392239
invalidImplicitSelfShouldOnlyWarn510(base, closure)) {
22402240
warnUntilVersion.emplace(6);
22412241
}
2242-
// Prior to Swift 7, downgrade to a warning if we're in a macro to preserve
2243-
// compatibility with the Swift 6 diagnostic behavior where we previously
2244-
// skipped diagnosing.
2245-
if (!Ctx.isSwiftVersionAtLeast(7) && isInMacro())
2246-
warnUntilVersion.emplace(7);
2242+
// Prior to the next language mode, downgrade to a warning if we're in a
2243+
// macro to preserve compatibility with the Swift 6 diagnostic behavior
2244+
// where we previously skipped diagnosing.
2245+
auto futureVersion = version::Version::getFutureMajorLanguageVersion();
2246+
if (!Ctx.isSwiftVersionAtLeast(futureVersion) && isInMacro())
2247+
warnUntilVersion.emplace(futureVersion);
22472248

22482249
auto diag = Ctx.Diags.diagnose(loc, ID, std::move(Args)...);
22492250
if (warnUntilVersion)

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1260,7 +1260,7 @@ void AttributeChecker::visitAccessControlAttr(AccessControlAttr *attr) {
12601260
diagnose(attr->getLocation(),
12611261
diag::access_control_non_objc_open_member, VD)
12621262
.fixItReplace(attr->getRange(), "public")
1263-
.warnUntilSwiftVersion(7);
1263+
.warnUntilFutureSwiftVersion();
12641264
}
12651265
}
12661266
}

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -276,16 +276,18 @@ static bool shouldAllowReferenceToUnavailableInSwiftDeclaration(
276276
return false;
277277
}
278278

279-
// Utility function to help determine if noasync diagnostics are still
280-
// appropriate even if a `DeclContext` returns `false` from `isAsyncContext()`.
281-
static bool shouldTreatDeclContextAsAsyncForDiagnostics(const DeclContext *DC) {
282-
if (auto *D = DC->getAsDecl())
283-
if (auto *FD = dyn_cast<FuncDecl>(D))
279+
/// Retrieve the innermost DeclContext that should be consulted for noasync
280+
/// checking.
281+
static const DeclContext *
282+
getInnermostDeclContextForNoAsync(const DeclContext *DC) {
283+
if (auto *D = DC->getAsDecl()) {
284+
if (auto *FD = dyn_cast<FuncDecl>(D)) {
284285
if (FD->isDeferBody())
285286
// If this is a defer body, we should delegate to its parent.
286-
return shouldTreatDeclContextAsAsyncForDiagnostics(DC->getParent());
287-
288-
return DC->isAsyncContext();
287+
return getInnermostDeclContextForNoAsync(DC->getParent());
288+
}
289+
}
290+
return DC;
289291
}
290292

291293
/// A class that walks the AST to find the innermost (i.e., deepest) node that
@@ -2758,7 +2760,8 @@ static bool
27582760
diagnoseDeclAsyncAvailability(const ValueDecl *D, SourceRange R,
27592761
const Expr *call, const ExportContext &Where) {
27602762
// If we are not in an (effective) async context, don't check it
2761-
if (!shouldTreatDeclContextAsAsyncForDiagnostics(Where.getDeclContext()))
2763+
auto *noAsyncDC = getInnermostDeclContextForNoAsync(Where.getDeclContext());
2764+
if (!noAsyncDC->isAsyncContext())
27622765
return false;
27632766

27642767
ASTContext &ctx = Where.getDeclContext()->getASTContext();
@@ -2774,14 +2777,27 @@ diagnoseDeclAsyncAvailability(const ValueDecl *D, SourceRange R,
27742777
}
27752778
}
27762779

2780+
// In Swift 6 we previously didn't coerce macro arguments to parameter types,
2781+
// so closure arguments may be treated as async in cases where they weren't in
2782+
// Swift 6. As such we need to warn if the use is within a closure macro
2783+
// argument until the next language mode.
2784+
auto shouldWarnUntilFutureVersion = [&]() {
2785+
auto *CE = dyn_cast<ClosureExpr>(noAsyncDC);
2786+
return CE && CE->isMacroArgument();
2787+
};
2788+
27772789
// @available(noasync) spelling
27782790
if (auto attr = D->getNoAsyncAttr()) {
27792791
SourceLoc diagLoc = call ? call->getLoc() : R.Start;
27802792
auto diag = ctx.Diags.diagnose(diagLoc, diag::async_unavailable_decl, D,
27812793
attr->getMessage());
2782-
diag.warnUntilSwiftVersion(6);
2783-
diag.limitBehaviorWithPreconcurrency(DiagnosticBehavior::Warning,
2784-
D->preconcurrency());
2794+
if (D->preconcurrency()) {
2795+
diag.limitBehavior(DiagnosticBehavior::Warning);
2796+
} else if (shouldWarnUntilFutureVersion()) {
2797+
diag.warnUntilFutureSwiftVersion();
2798+
} else {
2799+
diag.warnUntilSwiftVersion(6);
2800+
}
27852801

27862802
if (!attr->getRename().empty()) {
27872803
fixItAvailableAttrRename(diag, R, D, attr->getRename(), call);
@@ -2797,10 +2813,16 @@ diagnoseDeclAsyncAvailability(const ValueDecl *D, SourceRange R,
27972813
// @_unavailableFromAsync spelling
27982814
const UnavailableFromAsyncAttr *attr =
27992815
D->getAttrs().getAttribute<UnavailableFromAsyncAttr>();
2800-
SourceLoc diagLoc = call ? call->getLoc() : R.Start;
2801-
ctx.Diags
2802-
.diagnose(diagLoc, diag::async_unavailable_decl, D, attr->Message)
2803-
.warnUntilSwiftVersion(6);
2816+
{
2817+
SourceLoc diagLoc = call ? call->getLoc() : R.Start;
2818+
auto diag = ctx.Diags.diagnose(diagLoc, diag::async_unavailable_decl, D,
2819+
attr->Message);
2820+
if (shouldWarnUntilFutureVersion()) {
2821+
diag.warnUntilFutureSwiftVersion();
2822+
} else {
2823+
diag.warnUntilSwiftVersion(6);
2824+
}
2825+
}
28042826
D->diagnose(diag::decl_declared_here, D);
28052827
return true;
28062828
}

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2675,7 +2675,7 @@ namespace {
26752675
fromType, toType);
26762676

26772677
if (downgradeToWarning)
2678-
diag.warnUntilSwiftVersion(7);
2678+
diag.warnUntilFutureSwiftVersion();
26792679
}
26802680

26812681
for (auto type : nonSendableTypes) {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5185,7 +5185,8 @@ static bool diagnoseTypeWitnessAvailability(
51855185
return false;
51865186

51875187
// In Swift 6 and earlier type witness availability diagnostics are warnings.
5188-
const unsigned warnBeforeVersion = 7;
5188+
using namespace version;
5189+
const unsigned warnBeforeVersion = Version::getFutureMajorLanguageVersion();
51895190
bool shouldError =
51905191
ctx.LangOpts.EffectiveLanguageVersion.isVersionAtLeast(warnBeforeVersion);
51915192

0 commit comments

Comments
 (0)