Skip to content

Commit c686c30

Browse files
authored
Merge pull request #74591 from slavapestov/diagnose-lazy-with-effect
Sema: Diagnose 'lazy' variable initializer with effect
2 parents 5fbf42a + 3a91280 commit c686c30

File tree

5 files changed

+70
-26
lines changed

5 files changed

+70
-26
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5302,14 +5302,22 @@ ERROR(tryless_throwing_call_in_nonexhaustive_catch,none,
53025302
ERROR(throw_in_nonexhaustive_catch,none,
53035303
"error is not handled because the enclosing catch is not exhaustive", ())
53045304

5305+
#define EFFECTS_CONTEXT_KIND \
5306+
"%select{<<ERROR>>|" \
5307+
"a default argument|" \
5308+
"a property wrapper initializer|" \
5309+
"a property initializer|" \
5310+
"a global variable initializer|" \
5311+
"a lazy variable initializer|" \
5312+
"an enum case raw value|" \
5313+
"a catch pattern|" \
5314+
"a catch guard expression|" \
5315+
"a defer body}" \
5316+
53055317
ERROR(throwing_op_in_illegal_context,none,
5306-
"%1 can throw, but errors cannot be thrown out of "
5307-
"%select{<<ERROR>>|a default argument|a property wrapper initializer|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0",
5308-
(unsigned, StringRef))
5318+
"%1 can throw, but errors cannot be thrown out of " EFFECTS_CONTEXT_KIND "0", (unsigned, StringRef))
53095319
ERROR(throw_in_illegal_context,none,
5310-
"errors cannot be thrown out of "
5311-
"%select{<<ERROR>>|a default argument|a property wrapper initializer|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0",
5312-
(unsigned))
5320+
"errors cannot be thrown out of " EFFECTS_CONTEXT_KIND "0", (unsigned))
53135321

53145322
ERROR(throwing_operator_without_try,none,
53155323
"operator can throw but expression is not marked with 'try'", ())
@@ -5405,17 +5413,6 @@ ERROR(async_let_no_variables,none,
54055413
NOTE(async_let_without_await,none,
54065414
"reference to async let %0 is 'async'", (const ValueDecl *))
54075415

5408-
#define EFFECTS_CONTEXT_KIND \
5409-
"%select{<<ERROR>>|" \
5410-
"a default argument|" \
5411-
"a property wrapper initializer|" \
5412-
"a property initializer|" \
5413-
"a global variable initializer|" \
5414-
"an enum case raw value|" \
5415-
"a catch pattern|" \
5416-
"a catch guard expression|" \
5417-
"a defer body}" \
5418-
54195416
ERROR(async_call_in_illegal_context,none,
54205417
"'async' call cannot occur in " EFFECTS_CONTEXT_KIND "0",
54215418
(unsigned))

lib/AST/ASTVerifier.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,21 @@ class Verifier : public ASTWalker {
846846
OpaqueValues.erase(expr->getInterpolationExpr());
847847
}
848848

849+
bool shouldVerify(PropertyWrapperValuePlaceholderExpr *expr) {
850+
if (!shouldVerify(cast<Expr>(expr)))
851+
return false;
852+
853+
assert(expr->getOpaqueValuePlaceholder());
854+
assert(!OpaqueValues.count(expr->getOpaqueValuePlaceholder()));
855+
OpaqueValues[expr->getOpaqueValuePlaceholder()] = 0;
856+
return true;
857+
}
858+
859+
void cleanup(PropertyWrapperValuePlaceholderExpr *expr) {
860+
assert(OpaqueValues.count(expr->getOpaqueValuePlaceholder()));
861+
OpaqueValues.erase(expr->getOpaqueValuePlaceholder());
862+
}
863+
849864
void pushLocalGenerics(GenericEnvironment *env) {
850865
assert(LocalGenerics.count(env)==0);
851866
LocalGenerics.insert(env);
@@ -2288,7 +2303,7 @@ class Verifier : public ASTWalker {
22882303
}
22892304
verifyCheckedBase(E);
22902305
}
2291-
2306+
22922307
void verifyChecked(MakeTemporarilyEscapableExpr *E) {
22932308
PrettyStackTraceExpr debugStack(
22942309
Ctx, "verifying MakeTemporarilyEscapableExpr", E);

lib/AST/ASTWalker.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -195,22 +195,22 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
195195
}
196196

197197
bool visitPatternBindingDecl(PatternBindingDecl *PBD) {
198-
bool isPropertyWrapperBackingProperty = false;
199-
if (auto singleVar = PBD->getSingleVar()) {
200-
isPropertyWrapperBackingProperty =
201-
singleVar->getOriginalWrappedProperty() != nullptr;
202-
}
198+
auto *singleVar = PBD->getSingleVar();
203199

204200
for (auto idx : range(PBD->getNumPatternEntries())) {
205201
if (Pattern *Pat = doIt(PBD->getPattern(idx)))
206202
PBD->setPattern(idx, Pat);
207203
else
208204
return true;
209205

210-
if (!PBD->getInit(idx) || isPropertyWrapperBackingProperty)
206+
if (!PBD->getInit(idx))
207+
continue;
208+
209+
if (singleVar && singleVar->getOriginalWrappedProperty())
211210
continue;
212211

213-
if (PBD->isInitializerSubsumed(idx) &&
212+
if (PBD->isInitializerSubsumed(idx) && singleVar &&
213+
singleVar->getAttrs().hasAttribute<LazyAttr>() &&
214214
Walker.getLazyInitializerWalkingBehavior() !=
215215
LazyInitializerWalking::InPatternBinding) {
216216
break;

lib/Sema/TypeCheckEffects.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,10 @@ template <class Impl>
509509
class EffectsHandlingWalker : public ASTWalker {
510510
Impl &asImpl() { return *static_cast<Impl*>(this); }
511511
public:
512+
LazyInitializerWalking getLazyInitializerWalkingBehavior() override {
513+
return LazyInitializerWalking::InAccessor;
514+
}
515+
512516
/// Only look at the expansions for effects checking.
513517
MacroWalking getMacroWalkingBehavior() const override {
514518
return MacroWalking::Expansion;
@@ -940,7 +944,7 @@ class Classification {
940944
ConditionalEffectKind conditionalKind,
941945
PotentialEffectReason reason) {
942946
Classification result;
943-
if (!thrownError || isNeverThrownError(thrownError))
947+
if (isNeverThrownError(thrownError))
944948
return result;
945949

946950
assert(!thrownError->hasError());
@@ -2086,6 +2090,9 @@ class Context {
20862090
/// The initializer for a global variable.
20872091
GlobalVarInitializer,
20882092

2093+
/// The initializer for a `lazy` variable.
2094+
LazyVarInitializer,
2095+
20892096
/// The initializer for an enum element.
20902097
EnumElementInitializer,
20912098

@@ -2101,8 +2108,12 @@ class Context {
21012108

21022109
private:
21032110
static Context getContextForPatternBinding(PatternBindingDecl *pbd) {
2111+
auto *var = pbd->getSingleVar();
2112+
21042113
if (!pbd->isStatic() && pbd->getDeclContext()->isTypeContext()) {
21052114
return Context(Kind::IVarInitializer, pbd->getDeclContext());
2115+
} else if (var && var->getAttrs().hasAttribute<LazyAttr>()) {
2116+
return Context(Kind::LazyVarInitializer, pbd->getDeclContext());
21062117
} else {
21072118
return Context(Kind::GlobalVarInitializer, pbd->getDeclContext());
21082119
}
@@ -2520,6 +2531,7 @@ class Context {
25202531

25212532
case Kind::EnumElementInitializer:
25222533
case Kind::GlobalVarInitializer:
2534+
case Kind::LazyVarInitializer:
25232535
case Kind::IVarInitializer:
25242536
case Kind::DefaultArgument:
25252537
case Kind::PropertyWrapper:
@@ -2556,6 +2568,7 @@ class Context {
25562568

25572569
case Kind::EnumElementInitializer:
25582570
case Kind::GlobalVarInitializer:
2571+
case Kind::LazyVarInitializer:
25592572
case Kind::IVarInitializer:
25602573
case Kind::DefaultArgument:
25612574
case Kind::PropertyWrapper:
@@ -2582,6 +2595,7 @@ class Context {
25822595

25832596
case Kind::EnumElementInitializer:
25842597
case Kind::GlobalVarInitializer:
2598+
case Kind::LazyVarInitializer:
25852599
case Kind::IVarInitializer:
25862600
case Kind::DefaultArgument:
25872601
case Kind::PropertyWrapper:
@@ -2691,6 +2705,7 @@ class Context {
26912705

26922706
case Kind::EnumElementInitializer:
26932707
case Kind::GlobalVarInitializer:
2708+
case Kind::LazyVarInitializer:
26942709
case Kind::IVarInitializer:
26952710
case Kind::DefaultArgument:
26962711
case Kind::PropertyWrapper:
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking
2+
3+
// We could make this work by having `lazy` synthesize an effectful
4+
// getter, but for now let's reject it instead of crashing.
5+
6+
func throwsFunc() throws -> Int { return 3 }
7+
func asyncFunc() async -> Int { return 3 }
8+
9+
func localLazyWithEffects() {
10+
lazy var x = try throwsFunc() // expected-error {{call can throw, but errors cannot be thrown out of a lazy variable initializer}}
11+
lazy var y = await asyncFunc() // expected-error {{'async' call cannot occur in a lazy variable initializer}}
12+
}
13+
14+
struct InstanceLazyWithEffects {
15+
lazy var x = try throwsFunc() // expected-error {{call can throw, but errors cannot be thrown out of a property initializer}}
16+
lazy var y = await asyncFunc() // expected-error {{'async' call cannot occur in a property initializer}}
17+
}

0 commit comments

Comments
 (0)