Skip to content

Commit 3fb786b

Browse files
authored
Merge pull request #74666 from slavapestov/diagnose-lazy-with-effect-6.0
[6.0] Sema: Diagnose 'lazy' variable initializer with effect
2 parents 5172b72 + e83689c commit 3fb786b

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
@@ -5261,14 +5261,22 @@ ERROR(tryless_throwing_call_in_nonexhaustive_catch,none,
52615261
ERROR(throw_in_nonexhaustive_catch,none,
52625262
"error is not handled because the enclosing catch is not exhaustive", ())
52635263

5264+
#define EFFECTS_CONTEXT_KIND \
5265+
"%select{<<ERROR>>|" \
5266+
"a default argument|" \
5267+
"a property wrapper initializer|" \
5268+
"a property initializer|" \
5269+
"a global variable initializer|" \
5270+
"a lazy variable initializer|" \
5271+
"an enum case raw value|" \
5272+
"a catch pattern|" \
5273+
"a catch guard expression|" \
5274+
"a defer body}" \
5275+
52645276
ERROR(throwing_op_in_illegal_context,none,
5265-
"%1 can throw, but errors cannot be thrown out of "
5266-
"%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",
5267-
(unsigned, StringRef))
5277+
"%1 can throw, but errors cannot be thrown out of " EFFECTS_CONTEXT_KIND "0", (unsigned, StringRef))
52685278
ERROR(throw_in_illegal_context,none,
5269-
"errors cannot be thrown out of "
5270-
"%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",
5271-
(unsigned))
5279+
"errors cannot be thrown out of " EFFECTS_CONTEXT_KIND "0", (unsigned))
52725280

52735281
ERROR(throwing_operator_without_try,none,
52745282
"operator can throw but expression is not marked with 'try'", ())
@@ -5364,17 +5372,6 @@ ERROR(async_let_no_variables,none,
53645372
NOTE(async_let_without_await,none,
53655373
"reference to async let %0 is 'async'", (const ValueDecl *))
53665374

5367-
#define EFFECTS_CONTEXT_KIND \
5368-
"%select{<<ERROR>>|" \
5369-
"a default argument|" \
5370-
"a property wrapper initializer|" \
5371-
"a property initializer|" \
5372-
"a global variable initializer|" \
5373-
"an enum case raw value|" \
5374-
"a catch pattern|" \
5375-
"a catch guard expression|" \
5376-
"a defer body}" \
5377-
53785375
ERROR(async_call_in_illegal_context,none,
53795376
"'async' call cannot occur in " EFFECTS_CONTEXT_KIND "0",
53805377
(unsigned))

lib/AST/ASTVerifier.cpp

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

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

lib/AST/ASTWalker.cpp

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

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

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

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

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

lib/Sema/TypeCheckEffects.cpp

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

945949
assert(!thrownError->hasError());
@@ -2085,6 +2089,9 @@ class Context {
20852089
/// The initializer for a global variable.
20862090
GlobalVarInitializer,
20872091

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

@@ -2100,8 +2107,12 @@ class Context {
21002107

21012108
private:
21022109
static Context getContextForPatternBinding(PatternBindingDecl *pbd) {
2110+
auto *var = pbd->getSingleVar();
2111+
21032112
if (!pbd->isStatic() && pbd->getDeclContext()->isTypeContext()) {
21042113
return Context(Kind::IVarInitializer, pbd->getDeclContext());
2114+
} else if (var && var->getAttrs().hasAttribute<LazyAttr>()) {
2115+
return Context(Kind::LazyVarInitializer, pbd->getDeclContext());
21052116
} else {
21062117
return Context(Kind::GlobalVarInitializer, pbd->getDeclContext());
21072118
}
@@ -2519,6 +2530,7 @@ class Context {
25192530

25202531
case Kind::EnumElementInitializer:
25212532
case Kind::GlobalVarInitializer:
2533+
case Kind::LazyVarInitializer:
25222534
case Kind::IVarInitializer:
25232535
case Kind::DefaultArgument:
25242536
case Kind::PropertyWrapper:
@@ -2555,6 +2567,7 @@ class Context {
25552567

25562568
case Kind::EnumElementInitializer:
25572569
case Kind::GlobalVarInitializer:
2570+
case Kind::LazyVarInitializer:
25582571
case Kind::IVarInitializer:
25592572
case Kind::DefaultArgument:
25602573
case Kind::PropertyWrapper:
@@ -2581,6 +2594,7 @@ class Context {
25812594

25822595
case Kind::EnumElementInitializer:
25832596
case Kind::GlobalVarInitializer:
2597+
case Kind::LazyVarInitializer:
25842598
case Kind::IVarInitializer:
25852599
case Kind::DefaultArgument:
25862600
case Kind::PropertyWrapper:
@@ -2690,6 +2704,7 @@ class Context {
26902704

26912705
case Kind::EnumElementInitializer:
26922706
case Kind::GlobalVarInitializer:
2707+
case Kind::LazyVarInitializer:
26932708
case Kind::IVarInitializer:
26942709
case Kind::DefaultArgument:
26952710
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)