Skip to content

Commit 8c5d9e2

Browse files
authored
Merge pull request #74475 from ktoso/wip-async-get-if-let
[Concurrency] if let value on async value should be banned (not crash)
2 parents 2fb164b + 0d3a4b4 commit 8c5d9e2

File tree

7 files changed

+89
-17
lines changed

7 files changed

+89
-17
lines changed

lib/ASTGen/Sources/ASTGen/Stmts.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ extension ASTGenVisitor {
129129
} else {
130130
let identifier = pat.boundName
131131
if identifier != nil {
132-
// For `if let foo { }` Create an implicit `foo` expression as the initializer.
132+
// For `if let foo { }` Create a `foo` expression as the initializer.
133133
let ref = BridgedDeclNameRef.createParsed(.createIdentifier(identifier))
134134
let loc = BridgedDeclNameLoc.createParsed(self.generateSourceLoc(node.pattern))
135135
initializer =
@@ -139,7 +139,6 @@ extension ASTGenVisitor {
139139
kind: .ordinary,
140140
loc: loc
141141
).asExpr
142-
initializer.setImplicit()
143142
} else {
144143
// FIXME: Implement.
145144
// For `if let foo.bar {`, diagnose and convert it to `if let _ = foo.bar`

lib/Parse/ParseStmt.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,8 +1793,12 @@ Parser::parseStmtConditionElement(SmallVectorImpl<StmtConditionElement> &result,
17931793
auto declRefExpr = new (Context) UnresolvedDeclRefExpr(bindingName,
17941794
DeclRefKind::Ordinary,
17951795
loc);
1796-
1797-
declRefExpr->setImplicit();
1796+
// We do NOT mark this declRefExpr as implicit because that'd avoid
1797+
// reporting errors if it were used in a synchronous context in
1798+
// 'diagnoseUnhandledAsyncSite'. There may be other ways to resolve the
1799+
// reporting issue that we could explore in the future.
1800+
//
1801+
// Even though implicit, the ast node has correct source location.
17981802
Init = makeParserResult(declRefExpr);
17991803
} else if (BindingKindStr != "case") {
18001804
// If the pattern is present but isn't an identifier, the user wrote

lib/Sema/TypeCheckEffects.cpp

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,7 @@ class ApplyClassifier {
12511251
Classification classification;
12521252
PotentialEffectReason reason = PotentialEffectReason::forPropertyAccess();
12531253
ConcreteDeclRef declRef = E->getDeclRef();
1254+
12541255
if (auto getter = getEffectfulGetOnlyAccessor(declRef)) {
12551256
reason = getKindOfEffectfulProp(declRef);
12561257
classification = Classification::forDeclRef(
@@ -2018,7 +2019,7 @@ class ApplyClassifier {
20182019
/// Given the type of an argument, try to determine if it contains
20192020
/// a throws/async function in a way that is permitted to cause a
20202021
/// rethrows/reasync function to throw/async.
2021-
static Classification
2022+
static Classification
20222023
classifyArgumentByType(Type paramType, SubstitutionMap subs,
20232024
ConditionalEffectKind conditional,
20242025
PotentialEffectReason reason, EffectKind kind) {
@@ -2663,12 +2664,20 @@ class Context {
26632664
}
26642665

26652666
/// providing a \c kind helps tailor the emitted message.
2666-
void
2667-
diagnoseUnhandledAsyncSite(DiagnosticEngine &Diags, ASTNode node,
2667+
void diagnoseUnhandledAsyncSite(DiagnosticEngine &Diags, ASTNode node,
26682668
std::optional<PotentialEffectReason> maybeReason,
26692669
bool forAwait = false) {
2670-
if (node.isImplicit())
2670+
if (node.isImplicit()) {
2671+
// The reason we return early on implicit nodes is that sometimes we
2672+
// inject implicit closures, e.g. in 'async let' and we'd end up
2673+
// "double reporting" some errors, with no great way to make sure the
2674+
// "more specific diagnostic" is emitted. So instead, we avoid emitting
2675+
// about implicit code.
2676+
//
2677+
// Some synthesized code, like macros, are NOT marked implicit, so we will
2678+
// report about errors in them properly.
26712679
return;
2680+
}
26722681

26732682
switch (getKind()) {
26742683
case Kind::PotentiallyHandled: {
@@ -2691,6 +2700,7 @@ class Context {
26912700
return;
26922701
}
26932702
}
2703+
26942704
};
26952705

26962706
/// A class to walk over a local context and validate the correctness
@@ -2723,13 +2733,13 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
27232733

27242734
/// Do we have a throw site using 'try' in this context?
27252735
HasTryThrowSite = 0x10,
2726-
2736+
27272737
/// Are we in the context of an 'await'?
27282738
IsAsyncCovered = 0x20,
2729-
2739+
27302740
/// Do we have any calls to 'async' functions in this context?
27312741
HasAnyAsyncSite = 0x40,
2732-
2742+
27332743
/// Do we have any 'await's in this context?
27342744
HasAnyAwait = 0x80,
27352745

@@ -3293,7 +3303,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
32933303
CEC.Flags.set(ContextFlags::HasAnyThrowSite);
32943304
return Action::Continue(E);
32953305
}
3296-
3306+
32973307
PostWalkResult<Stmt *> walkToStmtPost(Stmt *S) override {
32983308
if (isa<ThrowStmt>(S))
32993309
CEC.Flags.set(ContextFlags::HasAnyThrowSite);
@@ -3305,7 +3315,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
33053315
for (auto &clause : ICD->getClauses()) {
33063316
// Active clauses are handled by the normal AST walk.
33073317
if (clause.isActive) continue;
3308-
3318+
33093319
for (auto elt : clause.Elements)
33103320
elt.walk(ConservativeThrowChecker(*this));
33113321
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: %target-swift-frontend -parse-as-library -emit-sil -verify %s
2+
3+
// REQUIRES: concurrency
4+
5+
@available(SwiftStdlib 5.1, *)
6+
struct Kappa {
7+
var maybeData: Int? {
8+
get async { 12 }
9+
}
10+
11+
func take() -> Int? { // expected-note 3{{add 'async' to function 'take()' to make it asynchronous}}
12+
13+
if let data = maybeData { // expected-error{{'async' property access in a function that does not support concurrency}}
14+
return data
15+
}
16+
17+
let x = maybeData // expected-error{{'async' property access in a function that does not support concurrency}}
18+
_ = x
19+
20+
if let maybeData { // expected-error{{'async' property access in a function that does not support concurrency}}
21+
return maybeData
22+
}
23+
24+
return nil
25+
}
26+
27+
func takeAsync() async -> Int? {
28+
29+
if let data = maybeData { // expected-error{{expression is 'async' but is not marked with 'await'}}
30+
// expected-note@-1{{property access is 'async'}}
31+
return data
32+
}
33+
34+
let x = maybeData // expected-error{{expression is 'async' but is not marked with 'await'}}
35+
// expected-note@-1{{property access is 'async'}}
36+
_ = x
37+
38+
if let maybeData { // expected-error{{expression is 'async' but is not marked with 'await'}}
39+
// expected-note@-1{{property access is 'async'}}
40+
return maybeData
41+
}
42+
43+
return nil
44+
}
45+
46+
func illegalSyntax() async -> Int? {
47+
// This is NOT valid syntax and we reject it properly,
48+
// though we could try to could give a better message
49+
if let await maybeData { // expected-error{{unwrap condition requires a valid identifier}}
50+
// expected-error@-1{{pattern variable binding cannot appear in an expression}}
51+
// expected-warning@-2{{no 'async' operations occur within 'await' expression}}
52+
return maybeData // expected-error{{expression is 'async' but is not marked with 'await'}}
53+
// expected-note@-1{{property access is 'async'}}
54+
}
55+
56+
}
57+
}

test/DebugInfo/guard-let-scope3.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ public class S {
1111
// CHECK: sil_scope [[X1_RHS:[0-9]+]] { loc "{{.*}}":9:19 parent [[P]]
1212
// CHECK: sil_scope [[X1:[0-9]+]] { loc "{{.*}}":9:19 parent [[P]]
1313
// CHECK: sil_scope [[X2:[0-9]+]] { loc "{{.*}}":9:29 parent [[X1]]
14+
// CHECK: sil_scope [[X3:[0-9]+]] { loc "{{.*}}":9:29 parent [[X1]]
1415
// CHECK: sil_scope [[GUARD:[0-9]+]] { loc "{{.*}}":9:36 parent [[P]]
1516
// CHECK: debug_value {{.*}} : $Optional<C>, let, name "x", {{.*}}, scope [[X1]]
16-
// CHECK: debug_value {{.*}} : $C, let, name "x", {{.*}}, scope [[X2]]
17+
// CHECK: debug_value {{.*}} : $C, let, name "x", {{.*}}, scope [[X3]]
1718
// FIXME: This source location is a little wild.
18-
// CHECK-NEXT: release_value{{.*}}:[[@LINE+5]]:3, scope [[X2]]
19+
// CHECK-NEXT: release_value{{.*}}:[[@LINE+5]]:3, scope [[X3]]
1920
throw MyError()
2021
// CHECK: function_ref {{.*}}MyError{{.*}}:[[@LINE-1]]:13, scope [[GUARD]]
2122
}

test/DebugInfo/if-let-scope.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ public func f(value: String?) {
55
if let value, let value = Int(value) {
66
// CHECK: sil_scope [[S1:[0-9]+]] { loc "{{.*}}":5:3
77
// CHECK: sil_scope [[S2:[0-9]+]] { loc "{{.*}}":5:10
8+
// CHECK: sil_scope [[S2:[0-9]+]] { loc "{{.*}}":5:10
89
// CHECK: sil_scope [[S3:[0-9]+]] { loc "{{.*}}":5:29 parent [[S2]] }
910
// CHECK: sil_scope [[S4:[0-9]+]] { loc "{{.*}}":5:29 parent [[S2]] }
1011
// CHECK: sil_scope [[S5:[0-9]+]] { loc "{{.*}}":5:40 parent [[S4]] }

test/expr/unary/async_await.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,13 +226,13 @@ func testAsyncExprWithoutAwait() async {
226226
if let result: A = result {} // expected-error {{expression is 'async' but is not marked with 'await'}} {{22-22=await }}
227227
// expected-warning@-1 {{immutable value 'result' was never used; consider replacing with '_' or removing it}}
228228
// expected-note@-2 {{reference to async let 'result' is 'async'}}
229-
if let result: A {} // expected-error {{expression is 'async' but is not marked with 'await'}} {{19-19= = await result}}
229+
if let result: A {} // expected-error {{expression is 'async' but is not marked with 'await'}} {{18-18=await }}
230230
// expected-warning@-1 {{immutable value 'result' was never used; consider replacing with '_' or removing it}}
231231
// expected-note@-2 {{reference to async let 'result' is 'async'}}
232232
if let result = result {} // expected-error {{expression is 'async' but is not marked with 'await'}} {{19-19=await }}
233233
// expected-warning@-1 {{value 'result' was defined but never used; consider replacing with boolean test}}
234234
// expected-note@-2 {{reference to async let 'result' is 'async'}}
235-
if let result {} // expected-error {{expression is 'async' but is not marked with 'await'}} {{16-16= = await result}}
235+
if let result {} // expected-error {{expression is 'async' but is not marked with 'await'}} {{10-10=await }}
236236
// expected-warning@-1 {{value 'result' was defined but never used; consider replacing with boolean test}}
237237
// expected-note@-2 {{reference to async let 'result' is 'async'}}
238238
let a = f("a") // expected-error {{expression is 'async' but is not marked with 'await'}} {{11-11=await }}

0 commit comments

Comments
 (0)