Skip to content

Commit 353fb5d

Browse files
authored
Merge pull request #70820 from hborla/downgrade-if-switch-effects-errors
[TypeCheckEffects] Stage in new effects checker errors for statement expressions as warnings until Swift 6.
2 parents b138f22 + cc79dea commit 353fb5d

File tree

5 files changed

+274
-234
lines changed

5 files changed

+274
-234
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,8 +1288,8 @@ ERROR(single_value_stmt_branch_must_end_in_result,none,
12881288
ERROR(cannot_jump_in_single_value_stmt,none,
12891289
"cannot '%0' in '%1' when used as expression",
12901290
(StmtKind, StmtKind))
1291-
ERROR(effect_marker_on_single_value_stmt,none,
1292-
"'%0' may not be used on '%1' expression", (StringRef, StmtKind))
1291+
WARNING(effect_marker_on_single_value_stmt,none,
1292+
"'%0' has no effect on '%1' expression", (StringRef, StmtKind))
12931293
ERROR(out_of_place_then_stmt,none,
12941294
"'then' may only appear as the last statement in an 'if', 'switch', or "
12951295
"'do' expression", ())

lib/Sema/TypeCheckEffects.cpp

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2220,9 +2220,10 @@ class Context {
22202220
}
22212221

22222222
void diagnoseUncoveredThrowSite(ASTContext &ctx, ASTNode E,
2223-
const PotentialEffectReason &reason) {
2223+
const Classification &classification) {
22242224
auto &Diags = ctx.Diags;
22252225
auto message = diag::throwing_call_without_try;
2226+
const auto &reason = classification.getThrowReason();
22262227
auto reasonKind = reason.getKind();
22272228

22282229
bool suggestTryFixIt = reasonKind == PotentialEffectReason::Kind::Apply;
@@ -2262,7 +2263,8 @@ class Context {
22622263
}
22632264
}
22642265

2265-
Diags.diagnose(loc, message).highlight(highlight);
2266+
Diags.diagnose(loc, message).highlight(highlight)
2267+
.warnUntilSwiftVersionIf(classification.shouldDowngradeToWarning(), 6);
22662268
maybeAddRethrowsNote(Diags, loc, reason);
22672269

22682270
// If this is a call without expected 'try[?|!]', like this:
@@ -2562,6 +2564,15 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
25622564

25632565
/// Do we have any 'await's in this context?
25642566
HasAnyAwait = 0x80,
2567+
2568+
/// Are we in an 'async let' initializer context?
2569+
InAsyncLet = 0x100,
2570+
2571+
/// Does an enclosing 'if' or 'switch' expr have a 'try'?
2572+
StmtExprCoversTry = 0x200,
2573+
2574+
/// Does an enclosing 'if' or 'switch' expr have an 'await'?
2575+
StmtExprCoversAwait = 0x400,
25652576
};
25662577
private:
25672578
unsigned Bits;
@@ -2707,8 +2718,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
27072718
}
27082719

27092720
void enterAsyncLet() {
2710-
Self.Flags.set(ContextFlags::IsTryCovered);
2711-
Self.Flags.set(ContextFlags::IsAsyncCovered);
2721+
Self.Flags.set(ContextFlags::InAsyncLet);
27122722
}
27132723

27142724
void refineLocalContext(Context newContext) {
@@ -2730,6 +2740,9 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
27302740
Self.Flags.reset();
27312741
Self.MaxThrowingKind = ConditionalEffectKind::None;
27322742

2743+
Self.Flags.mergeFrom(ContextFlags::StmtExprCoversTry, OldFlags);
2744+
Self.Flags.mergeFrom(ContextFlags::StmtExprCoversAwait, OldFlags);
2745+
27332746
// Suppress 'try' coverage checking within a single level of
27342747
// do/catch in debugger functions.
27352748
if (OldFlags.has(ContextFlags::IsTopLevelDebuggerFunction))
@@ -2747,6 +2760,17 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
27472760
// 'async'.
27482761
}
27492762

2763+
void setCoverageForSingleValueStmtExpr() {
2764+
resetCoverage();
2765+
Self.Flags.mergeFrom(ContextFlags::InAsyncLet, OldFlags);
2766+
2767+
if (OldFlags.has(ContextFlags::IsTryCovered))
2768+
Self.Flags.set(ContextFlags::StmtExprCoversTry);
2769+
2770+
if (OldFlags.has(ContextFlags::IsAsyncCovered))
2771+
Self.Flags.set(ContextFlags::StmtExprCoversAwait);
2772+
}
2773+
27502774
void preserveCoverageFromSingleValueStmtExpr() {
27512775
// We need to preserve whether we saw any throwing sites, to avoid warning
27522776
// on 'do { let x = if .random() { try ... } else { ... } } catch { ... }'
@@ -2918,7 +2942,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
29182942
// For an if/switch expression, we reset coverage such that a 'try'/'await'
29192943
// does not cover the branches.
29202944
ContextScope scope(*this, /*newContext*/ llvm::None);
2921-
scope.resetCoverage();
2945+
scope.setCoverageForSingleValueStmtExpr();
29222946
SVE->getStmt()->walk(*this);
29232947
scope.preserveCoverageFromSingleValueStmtExpr();
29242948
return ShouldNotRecurse;
@@ -3145,7 +3169,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
31453169

31463170
ThrownErrorDestination
31473171
checkThrowAsyncSite(ASTNode E, bool requiresTry,
3148-
const Classification &classification) {
3172+
Classification &classification) {
31493173
// Suppress all diagnostics when there's an un-analyzable throw/async site.
31503174
if (classification.isInvalid()) {
31513175
Flags.set(ContextFlags::HasAnyThrowSite);
@@ -3173,10 +3197,13 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
31733197
classification.getAsyncReason());
31743198
}
31753199
// Diagnose async calls that are outside of an await context.
3176-
else if (!Flags.has(ContextFlags::IsAsyncCovered)) {
3200+
else if (!(Flags.has(ContextFlags::IsAsyncCovered) ||
3201+
Flags.has(ContextFlags::InAsyncLet))) {
31773202
Expr *expr = E.dyn_cast<Expr*>();
31783203
Expr *anchor = walkToAnchor(expr, parentMap,
31793204
CurContext.isWithinInterpolatedString());
3205+
if (Flags.has(ContextFlags::StmtExprCoversAwait))
3206+
classification.setDowngradeToWarning(true);
31803207
if (uncoveredAsync.find(anchor) == uncoveredAsync.end())
31813208
errorOrder.push_back(anchor);
31823209
uncoveredAsync[anchor].emplace_back(
@@ -3209,13 +3236,16 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
32093236
break;
32103237

32113238
bool isTryCovered =
3212-
(!requiresTry || Flags.has(ContextFlags::IsTryCovered));
3239+
(!requiresTry || Flags.has(ContextFlags::IsTryCovered) ||
3240+
Flags.has(ContextFlags::InAsyncLet));
32133241
if (!CurContext.handlesThrows(throwsKind)) {
32143242
CurContext.diagnoseUnhandledThrowSite(Ctx.Diags, E, isTryCovered,
32153243
classification.getThrowReason());
32163244
} else if (!isTryCovered) {
3245+
if (Flags.has(ContextFlags::StmtExprCoversTry))
3246+
classification.setDowngradeToWarning(true);
32173247
CurContext.diagnoseUncoveredThrowSite(Ctx, E, // we want this one to trigger
3218-
classification.getThrowReason());
3248+
classification);
32193249
} else {
32203250
return checkThrownErrorType(
32213251
E.getStartLoc(), classification.getThrownError());
@@ -3367,7 +3397,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
33673397

33683398
void diagnoseRedundantTry(AnyTryExpr *E) const {
33693399
if (auto *SVE = SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(E)) {
3370-
// For an if/switch expression, produce an error instead of a warning.
3400+
// For an if/switch expression, produce a tailored warning.
33713401
Ctx.Diags.diagnose(E->getTryLoc(),
33723402
diag::effect_marker_on_single_value_stmt,
33733403
"try", SVE->getStmt()->getKind())
@@ -3379,7 +3409,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
33793409

33803410
void diagnoseRedundantAwait(AwaitExpr *E) const {
33813411
if (auto *SVE = SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(E)) {
3382-
// For an if/switch expression, produce an error instead of a warning.
3412+
// For an if/switch expression, produce a tailored warning.
33833413
Ctx.Diags.diagnose(E->getAwaitLoc(),
33843414
diag::effect_marker_on_single_value_stmt,
33853415
"await", SVE->getStmt()->getKind())

0 commit comments

Comments
 (0)