Skip to content

Commit ca723e6

Browse files
authored
Merge pull request #70826 from hborla/5.10-downgrade-if-switch-effects-errors
[5.10][TypeCheckEffects] Stage in new effects checker errors for statement expressions as warnings until Swift 6.
2 parents 8be2c83 + 5742388 commit ca723e6

File tree

4 files changed

+211
-171
lines changed

4 files changed

+211
-171
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,8 +1244,8 @@ ERROR(single_value_stmt_branch_must_end_in_result,none,
12441244
ERROR(cannot_jump_in_single_value_stmt,none,
12451245
"cannot '%0' in '%1' when used as expression",
12461246
(StmtKind, StmtKind))
1247-
ERROR(effect_marker_on_single_value_stmt,none,
1248-
"'%0' may not be used on '%1' expression", (StringRef, StmtKind))
1247+
WARNING(effect_marker_on_single_value_stmt,none,
1248+
"'%0' has no effect on '%1' expression", (StringRef, StmtKind))
12491249
ERROR(out_of_place_then_stmt,none,
12501250
"'then' may only appear as the last statement in an 'if' or 'switch' "
12511251
"expression", ())

lib/Sema/TypeCheckEffects.cpp

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,9 +1709,10 @@ class Context {
17091709
}
17101710

17111711
void diagnoseUncoveredThrowSite(ASTContext &ctx, ASTNode E,
1712-
const PotentialEffectReason &reason) {
1712+
const Classification &classification) {
17131713
auto &Diags = ctx.Diags;
17141714
auto message = diag::throwing_call_without_try;
1715+
const auto &reason = classification.getThrowReason();
17151716
auto reasonKind = reason.getKind();
17161717

17171718
bool suggestTryFixIt = reasonKind == PotentialEffectReason::Kind::Apply;
@@ -1751,7 +1752,8 @@ class Context {
17511752
}
17521753
}
17531754

1754-
Diags.diagnose(loc, message).highlight(highlight);
1755+
Diags.diagnose(loc, message).highlight(highlight)
1756+
.warnUntilSwiftVersionIf(classification.shouldDowngradeToWarning(), 6);
17551757
maybeAddRethrowsNote(Diags, loc, reason);
17561758

17571759
// If this is a call without expected 'try[?|!]', like this:
@@ -2051,6 +2053,15 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
20512053

20522054
/// Do we have any 'await's in this context?
20532055
HasAnyAwait = 0x80,
2056+
2057+
/// Are we in an 'async let' initializer context?
2058+
InAsyncLet = 0x100,
2059+
2060+
/// Does an enclosing 'if' or 'switch' expr have a 'try'?
2061+
StmtExprCoversTry = 0x200,
2062+
2063+
/// Does an enclosing 'if' or 'switch' expr have an 'await'?
2064+
StmtExprCoversAwait = 0x400,
20542065
};
20552066
private:
20562067
unsigned Bits;
@@ -2196,8 +2207,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
21962207
}
21972208

21982209
void enterAsyncLet() {
2199-
Self.Flags.set(ContextFlags::IsTryCovered);
2200-
Self.Flags.set(ContextFlags::IsAsyncCovered);
2210+
Self.Flags.set(ContextFlags::InAsyncLet);
22012211
}
22022212

22032213
void refineLocalContext(Context newContext) {
@@ -2219,6 +2229,9 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
22192229
Self.Flags.reset();
22202230
Self.MaxThrowingKind = ConditionalEffectKind::None;
22212231

2232+
Self.Flags.mergeFrom(ContextFlags::StmtExprCoversTry, OldFlags);
2233+
Self.Flags.mergeFrom(ContextFlags::StmtExprCoversAwait, OldFlags);
2234+
22222235
// Suppress 'try' coverage checking within a single level of
22232236
// do/catch in debugger functions.
22242237
if (OldFlags.has(ContextFlags::IsTopLevelDebuggerFunction))
@@ -2236,6 +2249,17 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
22362249
// 'async'.
22372250
}
22382251

2252+
void setCoverageForSingleValueStmtExpr() {
2253+
resetCoverage();
2254+
Self.Flags.mergeFrom(ContextFlags::InAsyncLet, OldFlags);
2255+
2256+
if (OldFlags.has(ContextFlags::IsTryCovered))
2257+
Self.Flags.set(ContextFlags::StmtExprCoversTry);
2258+
2259+
if (OldFlags.has(ContextFlags::IsAsyncCovered))
2260+
Self.Flags.set(ContextFlags::StmtExprCoversAwait);
2261+
}
2262+
22392263
void preserveCoverageFromSingleValueStmtExpr() {
22402264
// We need to preserve whether we saw any throwing sites, to avoid warning
22412265
// on 'do { let x = if .random() { try ... } else { ... } } catch { ... }'
@@ -2393,7 +2417,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
23932417
// For an if/switch expression, we reset coverage such that a 'try'/'await'
23942418
// does not cover the branches.
23952419
ContextScope scope(*this, /*newContext*/ llvm::None);
2396-
scope.resetCoverage();
2420+
scope.setCoverageForSingleValueStmtExpr();
23972421
SVE->getStmt()->walk(*this);
23982422
scope.preserveCoverageFromSingleValueStmtExpr();
23992423
return ShouldNotRecurse;
@@ -2549,11 +2573,11 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
25492573
effects.push_back(EffectKind::Async);
25502574
}
25512575

2576+
auto classification = Classification::forEffect(effects,
2577+
ConditionalEffectKind::Always,
2578+
getKindOfEffectfulProp(member));
25522579
bool requiresTry = getter->hasThrows();
2553-
checkThrowAsyncSite(E, requiresTry,
2554-
Classification::forEffect(effects,
2555-
ConditionalEffectKind::Always,
2556-
getKindOfEffectfulProp(member)));
2580+
checkThrowAsyncSite(E, requiresTry, classification);
25572581

25582582
} else {
25592583
EffectList effects;
@@ -2593,10 +2617,10 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
25932617
effects.push_back(EffectKind::Async);
25942618
}
25952619

2596-
checkThrowAsyncSite(E, getter->hasThrows(),
2597-
Classification::forEffect(effects,
2598-
ConditionalEffectKind::Always,
2599-
PotentialEffectReason::forPropertyAccess()));
2620+
auto classification = Classification::forEffect(effects,
2621+
ConditionalEffectKind::Always,
2622+
PotentialEffectReason::forPropertyAccess());
2623+
checkThrowAsyncSite(E, getter->hasThrows(), classification);
26002624

26012625
} else if (E->isImplicitlyAsync()) {
26022626
auto classification = Classification::forUnconditional(
@@ -2706,7 +2730,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
27062730
}
27072731

27082732
void checkThrowAsyncSite(ASTNode E, bool requiresTry,
2709-
const Classification &classification) {
2733+
Classification &classification) {
27102734
// Suppress all diagnostics when there's an un-analyzable throw/async site.
27112735
if (classification.isInvalid()) {
27122736
Flags.set(ContextFlags::HasAnyThrowSite);
@@ -2734,10 +2758,13 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
27342758
classification.getAsyncReason());
27352759
}
27362760
// Diagnose async calls that are outside of an await context.
2737-
else if (!Flags.has(ContextFlags::IsAsyncCovered)) {
2761+
else if (!(Flags.has(ContextFlags::IsAsyncCovered) ||
2762+
Flags.has(ContextFlags::InAsyncLet))) {
27382763
Expr *expr = E.dyn_cast<Expr*>();
27392764
Expr *anchor = walkToAnchor(expr, parentMap,
27402765
CurContext.isWithinInterpolatedString());
2766+
if (Flags.has(ContextFlags::StmtExprCoversAwait))
2767+
classification.setDowngradeToWarning(true);
27412768
if (uncoveredAsync.find(anchor) == uncoveredAsync.end())
27422769
errorOrder.push_back(anchor);
27432770
uncoveredAsync[anchor].emplace_back(
@@ -2770,13 +2797,16 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
27702797
break;
27712798

27722799
bool isTryCovered =
2773-
(!requiresTry || Flags.has(ContextFlags::IsTryCovered));
2800+
(!requiresTry || Flags.has(ContextFlags::IsTryCovered) ||
2801+
Flags.has(ContextFlags::InAsyncLet));
27742802
if (!CurContext.handlesThrows(throwsKind)) {
27752803
CurContext.diagnoseUnhandledThrowSite(Ctx.Diags, E, isTryCovered,
27762804
classification.getThrowReason());
27772805
} else if (!isTryCovered) {
2806+
if (Flags.has(ContextFlags::StmtExprCoversTry))
2807+
classification.setDowngradeToWarning(true);
27782808
CurContext.diagnoseUncoveredThrowSite(Ctx, E, // we want this one to trigger
2779-
classification.getThrowReason());
2809+
classification);
27802810
}
27812811
break;
27822812
}
@@ -2893,7 +2923,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
28932923

28942924
void diagnoseRedundantTry(AnyTryExpr *E) const {
28952925
if (auto *SVE = SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(E)) {
2896-
// For an if/switch expression, produce an error instead of a warning.
2926+
// For an if/switch expression, produce a tailored warning.
28972927
Ctx.Diags.diagnose(E->getTryLoc(),
28982928
diag::effect_marker_on_single_value_stmt,
28992929
"try", SVE->getStmt()->getKind())
@@ -2905,7 +2935,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
29052935

29062936
void diagnoseRedundantAwait(AwaitExpr *E) const {
29072937
if (auto *SVE = SingleValueStmtExpr::tryDigOutSingleValueStmtExpr(E)) {
2908-
// For an if/switch expression, produce an error instead of a warning.
2938+
// For an if/switch expression, produce a tailored warning.
29092939
Ctx.Diags.diagnose(E->getAwaitLoc(),
29102940
diag::effect_marker_on_single_value_stmt,
29112941
"await", SVE->getStmt()->getKind())

0 commit comments

Comments
 (0)