Skip to content

Commit 41a00e5

Browse files
committed
Sema: Fix crash-on-invalid with 'enclosing self' property wrappers
The diagnostic is still terrible, but at least we shouldn't crash. Fixes one manifestation of <rdar://problem/57726880>. I also filed <https://bugs.swift.org/browse/SR-11989> to track improving the diagnostic.
1 parent 1337925 commit 41a00e5

File tree

2 files changed

+76
-24
lines changed

2 files changed

+76
-24
lines changed

lib/Sema/TypeCheckStorage.cpp

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,9 @@ getEnclosingSelfPropertyWrapperAccess(VarDecl *property, bool forProjected) {
587587
return result;
588588
}
589589

590-
/// Build an l-value for the storage of a declaration.
590+
/// Build an l-value for the storage of a declaration. Returns nullptr if there
591+
/// was an error. This should only occur if an invalid declaration was type
592+
/// checked; another diagnostic should have been emitted already.
591593
static Expr *buildStorageReference(AccessorDecl *accessor,
592594
AbstractStorageDecl *storage,
593595
TargetImpl target,
@@ -674,12 +676,8 @@ static Expr *buildStorageReference(AccessorDecl *accessor,
674676
auto *backing = var->getPropertyWrapperBackingProperty();
675677

676678
// Error recovery.
677-
if (!backing) {
678-
auto type = storage->getValueInterfaceType();
679-
if (isLValue)
680-
type = LValueType::get(type);
681-
return new (ctx) ErrorExpr(SourceRange(), type);
682-
}
679+
if (!backing)
680+
return nullptr;
683681

684682
storage = backing;
685683

@@ -721,12 +719,8 @@ static Expr *buildStorageReference(AccessorDecl *accessor,
721719
auto *backing = var->getPropertyWrapperBackingProperty();
722720

723721
// Error recovery.
724-
if (!backing) {
725-
auto type = storage->getValueInterfaceType();
726-
if (isLValue)
727-
type = LValueType::get(type);
728-
return new (ctx) ErrorExpr(SourceRange(), type);
729-
}
722+
if (!backing)
723+
return nullptr;
730724

731725
storage = backing;
732726

@@ -836,7 +830,12 @@ static Expr *buildStorageReference(AccessorDecl *accessor,
836830
ctx, wrapperMetatype, SourceLoc(), args,
837831
subscriptDecl->getFullName().getArgumentNames(), { }, SourceLoc(),
838832
nullptr, subscriptDecl, /*Implicit=*/true);
839-
TypeChecker::typeCheckExpression(lookupExpr, accessor);
833+
834+
// FIXME: Since we're not resolving overloads or anything, we should be
835+
// building fully type-checked AST above; we already have all the
836+
// information that we need.
837+
if (!TypeChecker::typeCheckExpression(lookupExpr, accessor))
838+
return nullptr;
840839

841840
// Make sure we produce an lvalue only when desired.
842841
if (isMemberLValue != lookupExpr->getType()->is<LValueType>()) {
@@ -1034,6 +1033,10 @@ void createPropertyStoreOrCallSuperclassSetter(AccessorDecl *accessor,
10341033
Expr *dest = buildStorageReference(accessor, storage, target,
10351034
/*isLValue=*/true, ctx);
10361035

1036+
// Error recovery.
1037+
if (dest == nullptr)
1038+
return;
1039+
10371040
// A lazy property setter will store a value of type T into underlying storage
10381041
// of type T?.
10391042
auto destType = dest->getType()->getWithoutSpecifierType();
@@ -1043,6 +1046,7 @@ void createPropertyStoreOrCallSuperclassSetter(AccessorDecl *accessor,
10431046
return;
10441047

10451048
if (!destType->isEqual(value->getType())) {
1049+
assert(destType->getOptionalObjectType());
10461050
assert(destType->getOptionalObjectType()->isEqual(value->getType()));
10471051
value = new (ctx) InjectIntoOptionalExpr(value, destType);
10481052
}
@@ -1078,10 +1082,15 @@ synthesizeTrivialGetterBody(AccessorDecl *getter, TargetImpl target,
10781082

10791083
Expr *result =
10801084
createPropertyLoadOrCallSuperclassGetter(getter, storage, target, ctx);
1081-
ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result,
1082-
/*IsImplicit=*/true);
10831085

1084-
return { BraceStmt::create(ctx, loc, returnStmt, loc, true),
1086+
SmallVector<ASTNode, 2> body;
1087+
if (result != nullptr) {
1088+
ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result,
1089+
/*IsImplicit=*/true);
1090+
body.push_back(returnStmt);
1091+
}
1092+
1093+
return { BraceStmt::create(ctx, loc, body, loc, true),
10851094
/*isTypeChecked=*/true };
10861095
}
10871096

@@ -1473,7 +1482,13 @@ synthesizeObservedSetterBody(AccessorDecl *Set, TargetImpl target,
14731482
if (VD->getParsedAccessor(AccessorKind::DidSet)) {
14741483
Expr *OldValueExpr
14751484
= buildStorageReference(Set, VD, target, /*isLValue=*/true, Ctx);
1476-
OldValueExpr = new (Ctx) LoadExpr(OldValueExpr, VD->getType());
1485+
1486+
// Error recovery.
1487+
if (OldValueExpr == nullptr) {
1488+
OldValueExpr = new (Ctx) ErrorExpr(SourceRange(), VD->getType());
1489+
} else {
1490+
OldValueExpr = new (Ctx) LoadExpr(OldValueExpr, VD->getType());
1491+
}
14771492

14781493
OldValue = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let,
14791494
/*IsCaptureList*/false, SourceLoc(),
@@ -1601,13 +1616,14 @@ synthesizeCoroutineAccessorBody(AccessorDecl *accessor, ASTContext &ctx) {
16011616

16021617
// Build a reference to the storage.
16031618
Expr *ref = buildStorageReference(accessor, storage, target, isLValue, ctx);
1619+
if (ref != nullptr) {
1620+
// Wrap it with an `&` marker if this is a modify.
1621+
ref = maybeWrapInOutExpr(ref, ctx);
16041622

1605-
// Wrap it with an `&` marker if this is a modify.
1606-
ref = maybeWrapInOutExpr(ref, ctx);
1607-
1608-
// Yield it.
1609-
YieldStmt *yield = YieldStmt::create(ctx, loc, loc, ref, loc, true);
1610-
body.push_back(yield);
1623+
// Yield it.
1624+
YieldStmt *yield = YieldStmt::create(ctx, loc, loc, ref, loc, true);
1625+
body.push_back(yield);
1626+
}
16111627

16121628
return { BraceStmt::create(ctx, loc, body, loc, true),
16131629
/*isTypeChecked=*/true };
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %target-swift-frontend -typecheck %s -verify -verify-ignore-unknown
2+
3+
// FIXME: This should produce a diagnostic with a proper
4+
// source location. Right now, we just get three useless errors:
5+
6+
// <unknown>:0: error: type of expression is ambiguous without more context
7+
// <unknown>:0: error: type of expression is ambiguous without more context
8+
// <unknown>:0: error: type of expression is ambiguous without more context
9+
10+
// The actual problem is the type of the subscript declaration is wrong.
11+
12+
public class Store {
13+
@Published public var state: Any
14+
init() {
15+
self.state = 0
16+
}
17+
}
18+
19+
@propertyWrapper public struct Published<Value> {
20+
public init(wrappedValue: Value) {}
21+
public var wrappedValue: Value {
22+
get { fatalError() }
23+
set {}
24+
}
25+
public static subscript(_enclosingInstance object: Any,
26+
wrapped wrappedKeyPath: Any,
27+
storage storageKeyPath: Any)
28+
-> Value {
29+
get { fatalError() }
30+
set {}
31+
}
32+
public struct Publisher {}
33+
public var projectedValue: Publisher {
34+
mutating get { fatalError() }
35+
}
36+
}

0 commit comments

Comments
 (0)