Skip to content

Commit 39f4e38

Browse files
committed
Diagnose inout uses of 'lets' in constructors in the type checker.
Stored `let` properties of a struct, class, or actor permit 'inout' modification within the constructor body after they have been initialized. Tentatively remove this rule, only allowing such `let` properties to be initialized (assigned to) and not treated as `inout`. Fixes rdar://127258363.
1 parent b641c54 commit 39f4e38

15 files changed

+214
-86
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2092,6 +2092,14 @@ struct ClosureIsolatedByPreconcurrency {
20922092
bool operator()(const ClosureExpr *expr) const;
20932093
};
20942094

2095+
/// Determine whether the given expression is part of the left-hand side
2096+
/// of an assignment expression.
2097+
struct IsInLeftHandSideOfAssignment {
2098+
ConstraintSystem &cs;
2099+
2100+
bool operator()(Expr *expr) const;
2101+
};
2102+
20952103
/// Describes the type produced when referencing a declaration.
20962104
struct DeclReferenceType {
20972105
/// The "opened" type, which is the type of the declaration where any
@@ -4421,7 +4429,7 @@ class ConstraintSystem {
44214429
/// \param wantInterfaceType Whether we want the interface type, if available.
44224430
Type getUnopenedTypeOfReference(VarDecl *value, Type baseType,
44234431
DeclContext *UseDC,
4424-
ConstraintLocator *memberLocator = nullptr,
4432+
ConstraintLocator *locator,
44254433
bool wantInterfaceType = false,
44264434
bool adjustForPreconcurrency = true);
44274435

@@ -4443,7 +4451,7 @@ class ConstraintSystem {
44434451
getUnopenedTypeOfReference(
44444452
VarDecl *value, Type baseType, DeclContext *UseDC,
44454453
llvm::function_ref<Type(VarDecl *)> getType,
4446-
ConstraintLocator *memberLocator = nullptr,
4454+
ConstraintLocator *locator,
44474455
bool wantInterfaceType = false,
44484456
bool adjustForPreconcurrency = true,
44494457
llvm::function_ref<Type(const AbstractClosureExpr *)> getClosureType =
@@ -4453,7 +4461,10 @@ class ConstraintSystem {
44534461
llvm::function_ref<bool(const ClosureExpr *)> isolatedByPreconcurrency =
44544462
[](const ClosureExpr *closure) {
44554463
return closure->isIsolatedByPreconcurrency();
4456-
});
4464+
},
4465+
llvm::function_ref<bool(Expr *)> isAssignTarget = [](Expr *) {
4466+
return false;
4467+
});
44574468

44584469
/// Given the opened type and a pile of information about a member reference,
44594470
/// determine the reference type of the member reference.

lib/SILGen/SILGenConcurrency.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,8 @@ bool SILGenFunction::unsafelyInheritsExecutor() {
647647

648648
void ExecutorBreadcrumb::emit(SILGenFunction &SGF, SILLocation loc) {
649649
if (mustReturnToExecutor) {
650-
assert(SGF.ExpectedExecutor || SGF.unsafelyInheritsExecutor());
650+
assert(SGF.ExpectedExecutor || SGF.unsafelyInheritsExecutor() ||
651+
SGF.isCtorWithHopsInjectedByDefiniteInit());
651652
if (auto executor = SGF.ExpectedExecutor)
652653
SGF.B.createHopToExecutor(
653654
RegularLocation::getDebugOnlyLocation(loc, SGF.getModule()), executor,

lib/SILGen/SILGenConstructor.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,19 @@ static bool ctorHopsInjectedByDefiniteInit(ConstructorDecl *ctor,
618618
}
619619
}
620620

621+
bool SILGenFunction::isCtorWithHopsInjectedByDefiniteInit() {
622+
auto declRef = F.getDeclRef();
623+
if (!declRef || !declRef.isConstructor())
624+
return false;
625+
626+
auto ctor = dyn_cast_or_null<ConstructorDecl>(declRef.getDecl());
627+
if (!ctor)
628+
return false;
629+
630+
auto isolation = getActorIsolation(ctor);
631+
return ctorHopsInjectedByDefiniteInit(ctor, isolation);
632+
}
633+
621634
void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) {
622635
MagicFunctionName = SILGenModule::getMagicFunctionName(ctor);
623636

lib/SILGen/SILGenFunction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
820820
/// the fields.
821821
void emitDeallocatingMoveOnlyDestructor(DestructorDecl *dd);
822822

823+
/// Whether we are inside a constructor whose hops are injected by
824+
/// definite initialization.
825+
bool isCtorWithHopsInjectedByDefiniteInit();
826+
823827
/// Generates code for a struct constructor.
824828
/// This allocates the new 'self' value, emits the
825829
/// body code, then returns the final initialized 'self'.

lib/Sema/CSApply.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,12 +655,33 @@ namespace {
655655
}
656656

657657
auto ref = resolveConcreteDeclRef(decl, locator);
658+
659+
// If we have a variable that's treated as an rvalue but allows
660+
// assignment (for initialization) in the current context,
661+
// treat it as an rvalue that we immediately load. This is
662+
// the AST that's expected by SILGen.
663+
bool loadImmediately = false;
664+
if (auto var = dyn_cast_or_null<VarDecl>(ref.getDecl())) {
665+
if (!fullType->hasLValueType()) {
666+
if (var->mutability(dc) == StorageMutability::Initializable) {
667+
fullType = LValueType::get(fullType);
668+
adjustedFullType = LValueType::get(adjustedFullType);
669+
loadImmediately = true;
670+
}
671+
}
672+
}
673+
674+
658675
auto declRefExpr =
659676
new (ctx) DeclRefExpr(ref, loc, implicit, semantics, fullType);
660677
cs.cacheType(declRefExpr);
661678
declRefExpr->setFunctionRefKind(choice.getFunctionRefKind());
662679
Expr *result = adjustTypeForDeclReference(
663680
declRefExpr, fullType, adjustedFullType, locator);
681+
// If we have to load, do so now.
682+
if (loadImmediately)
683+
result = cs.addImplicitLoadExpr(result);
684+
664685
result = forceUnwrapIfExpected(result, locator);
665686

666687
if (auto *fnDecl = dyn_cast<AbstractFunctionDecl>(decl)) {

lib/Sema/ConstraintSystem.cpp

Lines changed: 97 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,12 +1434,14 @@ ConstraintSystem::getWrappedPropertyInformation(
14341434
/// is being accessed; must be null if and only if this is not
14351435
/// a type member
14361436
static bool
1437-
doesStorageProduceLValue(AbstractStorageDecl *storage, Type baseType,
1438-
DeclContext *useDC,
1439-
ConstraintLocator *memberLocator = nullptr) {
1437+
doesStorageProduceLValue(
1438+
AbstractStorageDecl *storage, Type baseType,
1439+
DeclContext *useDC,
1440+
llvm::function_ref<bool(Expr *)> isAssignTarget,
1441+
ConstraintLocator *locator) {
14401442
const DeclRefExpr *base = nullptr;
1441-
if (memberLocator) {
1442-
if (auto *const E = getAsExpr(memberLocator->getAnchor())) {
1443+
if (locator) {
1444+
if (auto *const E = getAsExpr(locator->getAnchor())) {
14431445
if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
14441446
base = dyn_cast<DeclRefExpr>(MRE->getBase());
14451447
} else if (auto *UDE = dyn_cast<UnresolvedDotExpr>(E)) {
@@ -1448,9 +1450,33 @@ doesStorageProduceLValue(AbstractStorageDecl *storage, Type baseType,
14481450
}
14491451
}
14501452

1451-
// Unsettable storage decls always produce rvalues.
1452-
if (!storage->isSettableInSwift(useDC, base))
1453-
return false;
1453+
switch (storage->mutabilityInSwift(useDC, base)) {
1454+
case StorageMutability::Immutable:
1455+
// Immutable storage decls always produce rvalues.
1456+
return false;
1457+
1458+
case StorageMutability::Mutable:
1459+
break;
1460+
1461+
case StorageMutability::Initializable: {
1462+
// If the member is not known to be on the left-hand side of
1463+
// an assignment, treat it as an rvalue so we can't pass it
1464+
// inout.
1465+
if (!locator)
1466+
return false;
1467+
1468+
auto *anchor = getAsExpr(simplifyLocatorToAnchor(locator));
1469+
if (!anchor)
1470+
return false;
1471+
1472+
// If the anchor isn't known to be the target of an assignment,
1473+
// treat as immutable.
1474+
if (!isAssignTarget(anchor))
1475+
return false;
1476+
1477+
break;
1478+
}
1479+
}
14541480

14551481
if (!storage->isSetterAccessibleFrom(useDC))
14561482
return false;
@@ -1490,7 +1516,7 @@ ClosureIsolatedByPreconcurrency::operator()(const ClosureExpr *expr) const {
14901516

14911517
Type ConstraintSystem::getUnopenedTypeOfReference(
14921518
VarDecl *value, Type baseType, DeclContext *UseDC,
1493-
ConstraintLocator *memberLocator, bool wantInterfaceType,
1519+
ConstraintLocator *locator, bool wantInterfaceType,
14941520
bool adjustForPreconcurrency) {
14951521
return ConstraintSystem::getUnopenedTypeOfReference(
14961522
value, baseType, UseDC,
@@ -1504,18 +1530,20 @@ Type ConstraintSystem::getUnopenedTypeOfReference(
15041530

15051531
return wantInterfaceType ? var->getInterfaceType() : var->getTypeInContext();
15061532
},
1507-
memberLocator, wantInterfaceType, adjustForPreconcurrency,
1533+
locator, wantInterfaceType, adjustForPreconcurrency,
15081534
GetClosureType{*this},
1509-
ClosureIsolatedByPreconcurrency{*this});
1535+
ClosureIsolatedByPreconcurrency{*this},
1536+
IsInLeftHandSideOfAssignment{*this});
15101537
}
15111538

15121539
Type ConstraintSystem::getUnopenedTypeOfReference(
15131540
VarDecl *value, Type baseType, DeclContext *UseDC,
15141541
llvm::function_ref<Type(VarDecl *)> getType,
1515-
ConstraintLocator *memberLocator,
1542+
ConstraintLocator *locator,
15161543
bool wantInterfaceType, bool adjustForPreconcurrency,
15171544
llvm::function_ref<Type(const AbstractClosureExpr *)> getClosureType,
1518-
llvm::function_ref<bool(const ClosureExpr *)> isolatedByPreconcurrency) {
1545+
llvm::function_ref<bool(const ClosureExpr *)> isolatedByPreconcurrency,
1546+
llvm::function_ref<bool(Expr *)> isAssignTarget) {
15191547
Type requestedType =
15201548
getType(value)->getWithoutSpecifierType()->getReferenceStorageReferent();
15211549

@@ -1540,7 +1568,8 @@ Type ConstraintSystem::getUnopenedTypeOfReference(
15401568

15411569
// Qualify storage declarations with an lvalue when appropriate.
15421570
// Otherwise, they yield rvalues (and the access must be a load).
1543-
if (doesStorageProduceLValue(value, baseType, UseDC, memberLocator) &&
1571+
if (doesStorageProduceLValue(value, baseType, UseDC, isAssignTarget,
1572+
locator) &&
15441573
!requestedType->hasError()) {
15451574
return LValueType::get(requestedType);
15461575
}
@@ -1913,7 +1942,8 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value,
19131942
// FIXME: @preconcurrency
19141943
bool wantInterfaceType = !varDecl->getDeclContext()->isLocalContext();
19151944
Type valueType =
1916-
getUnopenedTypeOfReference(varDecl, Type(), useDC, /*base=*/nullptr,
1945+
getUnopenedTypeOfReference(varDecl, Type(), useDC,
1946+
getConstraintLocator(locator),
19171947
wantInterfaceType);
19181948

19191949
Type thrownErrorType;
@@ -2608,6 +2638,48 @@ bool ConstraintSystem::isPartialApplication(ConstraintLocator *locator) {
26082638
return level < (baseTy->is<MetatypeType>() ? 1 : 2);
26092639
}
26102640

2641+
bool IsInLeftHandSideOfAssignment::operator()(Expr *expr) const {
2642+
// Walk up the parent tree.
2643+
auto parent = cs.getParentExpr(expr);
2644+
if (!parent)
2645+
return false;
2646+
2647+
// If the parent is an assignment expression, check whether we are
2648+
// the left-hand side.
2649+
if (auto assignParent = dyn_cast<AssignExpr>(parent)) {
2650+
return expr == assignParent->getDest();
2651+
}
2652+
2653+
// Always look up through these parent kinds.
2654+
if (isa<TupleExpr>(parent) || isa<IdentityExpr>(parent) ||
2655+
isa<AnyTryExpr>(parent) || isa<BindOptionalExpr>(parent)) {
2656+
return (*this)(parent);
2657+
}
2658+
2659+
// If the parent is a lookup expression, only follow from the base.
2660+
if (auto lookupParent = dyn_cast<LookupExpr>(parent)) {
2661+
if (lookupParent->getBase() == expr)
2662+
return (*this)(parent);
2663+
2664+
// The expression wasn't in the base, so this isn't part of the
2665+
// left-hand side.
2666+
return false;
2667+
}
2668+
2669+
// If the parent is an unresolved member reference a.b, only follow
2670+
// from the base.
2671+
if (auto dotParent = dyn_cast<UnresolvedDotExpr>(parent)) {
2672+
if (dotParent->getBase() == expr)
2673+
return (*this)(parent);
2674+
2675+
// The expression wasn't in the base, so this isn't part of the
2676+
// left-hand side.
2677+
return false;
2678+
}
2679+
2680+
return false;
2681+
}
2682+
26112683
DeclReferenceType ConstraintSystem::getTypeOfMemberReference(
26122684
Type baseTy, ValueDecl *value, DeclContext *useDC, bool isDynamicLookup,
26132685
FunctionRefKind functionRefKind, ConstraintLocator *locator,
@@ -2711,7 +2783,9 @@ DeclReferenceType ConstraintSystem::getTypeOfMemberReference(
27112783
if (auto *subscript = dyn_cast<SubscriptDecl>(value)) {
27122784
auto elementTy = subscript->getElementInterfaceType();
27132785

2714-
if (doesStorageProduceLValue(subscript, baseTy, useDC, locator))
2786+
if (doesStorageProduceLValue(
2787+
subscript, baseTy, useDC,
2788+
IsInLeftHandSideOfAssignment{*this}, locator))
27152789
elementTy = LValueType::get(elementTy);
27162790

27172791
auto indices = subscript->getInterfaceType()
@@ -2966,7 +3040,10 @@ Type ConstraintSystem::getEffectiveOverloadType(ConstraintLocator *locator,
29663040
if (auto subscript = dyn_cast<SubscriptDecl>(decl)) {
29673041
auto elementTy = subscript->getElementInterfaceType();
29683042

2969-
if (doesStorageProduceLValue(subscript, overload.getBaseType(), useDC))
3043+
if (doesStorageProduceLValue(subscript, overload.getBaseType(),
3044+
useDC,
3045+
IsInLeftHandSideOfAssignment{*this},
3046+
locator))
29703047
elementTy = LValueType::get(elementTy);
29713048
else if (elementTy->hasDynamicSelfType()) {
29723049
elementTy = withDynamicSelfResultReplaced(elementTy,
@@ -2990,7 +3067,9 @@ Type ConstraintSystem::getEffectiveOverloadType(ConstraintLocator *locator,
29903067
locator);
29913068
} else if (auto var = dyn_cast<VarDecl>(decl)) {
29923069
type = var->getValueInterfaceType();
2993-
if (doesStorageProduceLValue(var, overload.getBaseType(), useDC)) {
3070+
if (doesStorageProduceLValue(
3071+
var, overload.getBaseType(), useDC,
3072+
IsInLeftHandSideOfAssignment{*this}, locator)) {
29943073
type = LValueType::get(type);
29953074
} else if (type->hasDynamicSelfType()) {
29963075
type = withDynamicSelfResultReplaced(type, /*uncurryLevel=*/0);

lib/Sema/TypeCheckExpr.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -581,17 +581,6 @@ bool TypeChecker::requireArrayLiteralIntrinsics(ASTContext &ctx,
581581
return true;
582582
}
583583

584-
Expr *TypeChecker::buildCheckedRefExpr(VarDecl *value, DeclContext *UseDC,
585-
DeclNameLoc loc, bool Implicit) {
586-
auto type = constraints::ConstraintSystem::getUnopenedTypeOfReference(
587-
value, Type(), UseDC,
588-
[&](VarDecl *var) -> Type { return value->getTypeInContext(); });
589-
auto semantics = value->getAccessSemanticsFromContext(UseDC,
590-
/*isAccessOnSelf*/false);
591-
return new (value->getASTContext())
592-
DeclRefExpr(value, loc, Implicit, semantics, type);
593-
}
594-
595584
Expr *TypeChecker::buildRefExpr(ArrayRef<ValueDecl *> Decls,
596585
DeclContext *UseDC, DeclNameLoc NameLoc,
597586
bool Implicit, FunctionRefKind functionRefKind) {

lib/Sema/TypeChecker.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -944,10 +944,6 @@ Comparison compareDeclarations(DeclContext *dc, ValueDecl *decl1,
944944
/// of the first one and the expression will still type check.
945945
bool isDeclRefinementOf(ValueDecl *declA, ValueDecl *declB);
946946

947-
/// Build a type-checked reference to the given value.
948-
Expr *buildCheckedRefExpr(VarDecl *D, DeclContext *UseDC, DeclNameLoc nameLoc,
949-
bool Implicit);
950-
951947
/// Build a reference to a declaration, where name lookup returned
952948
/// the given set of declarations.
953949
Expr *buildRefExpr(ArrayRef<ValueDecl *> Decls, DeclContext *UseDC,

test/Constraints/diagnostics.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,7 +1396,9 @@ do {
13961396
func generic<T>(_ value: inout T, _ closure: (S<T>) -> Void) {}
13971397

13981398
let arg: Int
1399+
// expected-note@-1{{change 'let' to 'var' to make it mutable}}
13991400
generic(&arg) { (g: S<Double>) -> Void in } // expected-error {{cannot convert value of type '(S<Double>) -> Void' to expected argument type '(S<Int>) -> Void'}}
1401+
// expected-error@-1{{cannot pass immutable value as inout argument: 'arg' is a 'let' constant}}
14001402
}
14011403

14021404
// rdar://problem/62428353 - bad error message for passing `T` where `inout T` was expected
@@ -1556,18 +1558,17 @@ func testNilCoalescingOperatorRemoveFix() {
15561558
let _ = "" /* This is a comment */ ?? "" // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{13-43=}}
15571559

15581560
let _ = "" // This is a comment
1559-
?? "" // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{1558:13-1559:10=}}
1561+
?? "" // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{1560:13-1561:10=}}
15601562

15611563
let _ = "" // This is a comment
15621564
/*
15631565
* The blank line below is part of the test case, do not delete it
15641566
*/
1567+
?? "" // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{1563:13-1567:10=}}
15651568

1566-
?? "" // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{1561:13-1566:10=}}
1567-
1568-
if ("" ?? // This is a comment // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{9-1569:9=}}
1569+
if ("" ?? // This is a comment // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{9-1570:9=}}
15691570
"").isEmpty {}
15701571

15711572
if ("" // This is a comment
1572-
?? "").isEmpty {} // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{1571:9-1572:12=}}
1573+
?? "").isEmpty {} // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{1572:9-1573:12=}}
15731574
}

0 commit comments

Comments
 (0)