Skip to content

Commit f2610cf

Browse files
authored
Merge pull request #73609 from DougGregor/instance-let-not-inout-in-initializer
2 parents 359afa8 + 2ffe9d5 commit f2610cf

18 files changed

+364
-142
lines changed

include/swift/AST/Decl.h

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5574,6 +5574,21 @@ class BuiltinTupleDecl final : public NominalTypeDecl {
55745574
}
55755575
};
55765576

5577+
/// Describes whether a particular storage declaration is mutable.
5578+
enum class StorageMutability {
5579+
/// The storage is immutable, meaning that it can neither be assigned
5580+
/// to nor passed inout.
5581+
Immutable,
5582+
5583+
/// The storage is mutable, meaning that it can be assigned and pased
5584+
/// inout.
5585+
Mutable,
5586+
5587+
/// The storage is immutable, but can be asigned for the purposes of
5588+
/// initialization.
5589+
Initializable
5590+
};
5591+
55775592
/// AbstractStorageDecl - This is the common superclass for VarDecl and
55785593
/// SubscriptDecl, representing potentially settable memory locations.
55795594
class AbstractStorageDecl : public ValueDecl {
@@ -5745,8 +5760,31 @@ class AbstractStorageDecl : public ValueDecl {
57455760
/// Determine whether references to this storage declaration may appear
57465761
/// on the left-hand side of an assignment, as the operand of a
57475762
/// `&` or 'inout' operator, or as a component in a writable key path.
5748-
bool isSettable(const DeclContext *UseDC,
5749-
const DeclRefExpr *base = nullptr) const;
5763+
bool isSettable(const DeclContext *useDC,
5764+
const DeclRefExpr *base = nullptr) const {
5765+
switch (mutability(useDC, base)) {
5766+
case StorageMutability::Immutable:
5767+
return false;
5768+
case StorageMutability::Mutable:
5769+
case StorageMutability::Initializable:
5770+
return true;
5771+
}
5772+
}
5773+
5774+
/// Determine the mutability of this storage declaration when
5775+
/// accessed from a given declaration context.
5776+
StorageMutability mutability(const DeclContext *useDC,
5777+
const DeclRefExpr *base = nullptr) const;
5778+
5779+
/// Determine the mutability of this storage declaration when
5780+
/// accessed from a given declaration context in Swift.
5781+
///
5782+
/// This method differs only from 'mutability()' in its handling of
5783+
/// 'optional' storage requirements, which lack support for direct
5784+
/// writes in Swift.
5785+
StorageMutability mutabilityInSwift(
5786+
const DeclContext *useDC,
5787+
const DeclRefExpr *base = nullptr) const;
57505788

57515789
/// Determine whether references to this storage declaration in Swift may
57525790
/// appear on the left-hand side of an assignment, as the operand of a
@@ -5755,8 +5793,16 @@ class AbstractStorageDecl : public ValueDecl {
57555793
/// This method is equivalent to \c isSettable with the exception of
57565794
/// 'optional' storage requirements, which lack support for direct writes
57575795
/// in Swift.
5758-
bool isSettableInSwift(const DeclContext *UseDC,
5759-
const DeclRefExpr *base = nullptr) const;
5796+
bool isSettableInSwift(const DeclContext *useDC,
5797+
const DeclRefExpr *base = nullptr) const {
5798+
switch (mutabilityInSwift(useDC, base)) {
5799+
case StorageMutability::Immutable:
5800+
return false;
5801+
case StorageMutability::Mutable:
5802+
case StorageMutability::Initializable:
5803+
return true;
5804+
}
5805+
}
57605806

57615807
/// Does this storage declaration have explicitly-defined accessors
57625808
/// written in the source?
@@ -6069,13 +6115,10 @@ class VarDecl : public AbstractStorageDecl {
60696115
/// precisely point to the variable type because of type aliases.
60706116
SourceRange getTypeSourceRangeForDiagnostics() const;
60716117

6072-
/// Returns whether the var is settable in the specified context: this
6073-
/// is either because it is a stored var, because it has a custom setter, or
6074-
/// is a let member in an initializer.
6075-
///
6076-
/// Pass a null context and null base to check if it's always settable.
6077-
bool isSettable(const DeclContext *UseDC,
6078-
const DeclRefExpr *base = nullptr) const;
6118+
/// Determine the mutability of this variable declaration when
6119+
/// accessed from a given declaration context.
6120+
StorageMutability mutability(const DeclContext *useDC,
6121+
const DeclRefExpr *base = nullptr) const;
60796122

60806123
/// Return the parent pattern binding that may provide an initializer for this
60816124
/// VarDecl. This returns null if there is none associated with the VarDecl.
@@ -9337,26 +9380,6 @@ findGenericParameterReferences(const ValueDecl *value, CanGenericSignature sig,
93379380
bool treatNonResultCovarianceAsInvariant,
93389381
std::optional<unsigned> skipParamIndex);
93399382

9340-
inline bool AbstractStorageDecl::isSettable(const DeclContext *UseDC,
9341-
const DeclRefExpr *base) const {
9342-
if (auto vd = dyn_cast<VarDecl>(this))
9343-
return vd->isSettable(UseDC, base);
9344-
9345-
auto sd = cast<SubscriptDecl>(this);
9346-
return sd->supportsMutation();
9347-
}
9348-
9349-
inline bool
9350-
AbstractStorageDecl::isSettableInSwift(const DeclContext *UseDC,
9351-
const DeclRefExpr *base) const {
9352-
// TODO: Writing to an optional storage requirement is not supported in Swift.
9353-
if (getAttrs().hasAttribute<OptionalAttr>()) {
9354-
return false;
9355-
}
9356-
9357-
return isSettable(UseDC, base);
9358-
}
9359-
93609383
inline void
93619384
AbstractStorageDecl::overwriteSetterAccess(AccessLevel accessLevel) {
93629385
Accessors.setInt(accessLevel);

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/AST/Decl.cpp

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3091,6 +3091,34 @@ bool AbstractStorageDecl::isSetterMutating() const {
30913091
IsSetterMutatingRequest{const_cast<AbstractStorageDecl *>(this)}, {});
30923092
}
30933093

3094+
StorageMutability
3095+
AbstractStorageDecl::mutability(const DeclContext *useDC,
3096+
const DeclRefExpr *base) const {
3097+
if (auto vd = dyn_cast<VarDecl>(this))
3098+
return vd->mutability(useDC, base);
3099+
3100+
auto sd = cast<SubscriptDecl>(this);
3101+
return sd->supportsMutation() ? StorageMutability::Mutable
3102+
: StorageMutability::Immutable;
3103+
}
3104+
3105+
/// Determine the mutability of this storage declaration when
3106+
/// accessed from a given declaration context in Swift.
3107+
///
3108+
/// This method differs only from 'mutability()' in its handling of
3109+
/// 'optional' storage requirements, which lack support for direct
3110+
/// writes in Swift.
3111+
StorageMutability
3112+
AbstractStorageDecl::mutabilityInSwift(const DeclContext *useDC,
3113+
const DeclRefExpr *base) const {
3114+
// TODO: Writing to an optional storage requirement is not supported in Swift.
3115+
if (getAttrs().hasAttribute<OptionalAttr>()) {
3116+
return StorageMutability::Immutable;
3117+
}
3118+
3119+
return mutability(useDC, base);
3120+
}
3121+
30943122
OpaqueReadOwnership AbstractStorageDecl::getOpaqueReadOwnership() const {
30953123
ASTContext &ctx = getASTContext();
30963124
return evaluateOrDefault(ctx.evaluator,
@@ -7263,32 +7291,39 @@ Type VarDecl::getTypeInContext() const {
72637291
return getDeclContext()->mapTypeIntoContext(getInterfaceType());
72647292
}
72657293

7294+
/// Translate an "is mutable" bit into a StorageMutability value.
7295+
static StorageMutability storageIsMutable(bool isMutable) {
7296+
return isMutable ? StorageMutability::Mutable
7297+
: StorageMutability::Immutable;
7298+
}
7299+
72667300
/// Returns whether the var is settable in the specified context: this
72677301
/// is either because it is a stored var, because it has a custom setter, or
72687302
/// is a let member in an initializer.
7269-
bool VarDecl::isSettable(const DeclContext *UseDC,
7270-
const DeclRefExpr *base) const {
7303+
StorageMutability
7304+
VarDecl::mutability(const DeclContext *UseDC,
7305+
const DeclRefExpr *base) const {
72717306
// Parameters are settable or not depending on their ownership convention.
72727307
if (auto *PD = dyn_cast<ParamDecl>(this))
7273-
return !PD->isImmutableInFunctionBody();
7308+
return storageIsMutable(!PD->isImmutableInFunctionBody());
72747309

72757310
// If this is a 'var' decl, then we're settable if we have storage or a
72767311
// setter.
72777312
if (!isLet()) {
72787313
if (hasInitAccessor()) {
72797314
if (auto *ctor = dyn_cast_or_null<ConstructorDecl>(UseDC)) {
72807315
if (base && ctor->getImplicitSelfDecl() != base->getDecl())
7281-
return supportsMutation();
7282-
return true;
7316+
return storageIsMutable(supportsMutation());
7317+
return StorageMutability::Initializable;
72837318
}
72847319
}
72857320

7286-
return supportsMutation();
7321+
return storageIsMutable(supportsMutation());
72877322
}
72887323

72897324
// Static 'let's are always immutable.
72907325
if (isStatic()) {
7291-
return false;
7326+
return StorageMutability::Immutable;
72927327
}
72937328

72947329
//
@@ -7298,11 +7333,11 @@ bool VarDecl::isSettable(const DeclContext *UseDC,
72987333

72997334
// Debugger expression 'let's are initialized through a side-channel.
73007335
if (isDebuggerVar())
7301-
return false;
7336+
return StorageMutability::Immutable;
73027337

73037338
// 'let's are only ever settable from a specific DeclContext.
73047339
if (UseDC == nullptr)
7305-
return false;
7340+
return StorageMutability::Immutable;
73067341

73077342
// 'let' properties in structs/classes are only ever settable in their
73087343
// designated initializer(s) or by init accessors.
@@ -7314,61 +7349,64 @@ bool VarDecl::isSettable(const DeclContext *UseDC,
73147349
// Check whether this property is part of `initializes` list,
73157350
// and allow assignment/mutation if so. DI would be responsible
73167351
// for checking for re-assignment.
7317-
return accessor->isInitAccessor() &&
7352+
if (accessor->isInitAccessor() &&
73187353
llvm::is_contained(accessor->getInitializedProperties(),
7319-
const_cast<VarDecl *>(this));
7354+
const_cast<VarDecl *>(this)))
7355+
return StorageMutability::Initializable;
7356+
7357+
return StorageMutability::Immutable;
73207358
}
73217359

73227360
auto *CD = dyn_cast<ConstructorDecl>(UseDC);
7323-
if (!CD) return false;
7324-
7361+
if (!CD) return StorageMutability::Immutable;
7362+
73257363
auto *CDC = CD->getDeclContext();
73267364

73277365
// 'let' properties are not valid inside protocols.
73287366
if (CDC->getExtendedProtocolDecl())
7329-
return false;
7367+
return StorageMutability::Immutable;
73307368

73317369
// If this init is defined inside of the same type (or in an extension
73327370
// thereof) as the let property, then it is mutable.
73337371
if (CDC->getSelfNominalTypeDecl() !=
73347372
getDeclContext()->getSelfNominalTypeDecl())
7335-
return false;
7373+
return StorageMutability::Immutable;
73367374

73377375
if (base && CD->getImplicitSelfDecl() != base->getDecl())
7338-
return false;
7376+
return StorageMutability::Immutable;
73397377

73407378
// If this is a convenience initializer (i.e. one that calls
73417379
// self.init), then let properties are never mutable in it. They are
73427380
// only mutable in designated initializers.
73437381
auto initKindAndExpr = CD->getDelegatingOrChainedInitKind();
73447382
if (initKindAndExpr.initKind == BodyInitKind::Delegating)
7345-
return false;
7383+
return StorageMutability::Immutable;
73467384

7347-
return true;
7385+
return StorageMutability::Initializable;
73487386
}
73497387

73507388
// If the 'let' has a value bound to it but has no PBD, then it is
73517389
// already initializedand not settable.
73527390
if (getParentPatternBinding() == nullptr)
7353-
return false;
7391+
return StorageMutability::Immutable;
73547392

73557393
// If the 'let' has an explicitly written initializer with a pattern binding,
73567394
// then it isn't settable.
73577395
if (isParentInitialized())
7358-
return false;
7396+
return StorageMutability::Immutable;
73597397

73607398
// Normal lets (e.g. globals) are only mutable in the context of the
73617399
// declaration. To handle top-level code properly, we look through
73627400
// the TopLevelCode decl on the use (if present) since the vardecl may be
73637401
// one level up.
73647402
if (getDeclContext() == UseDC)
7365-
return true;
7403+
return StorageMutability::Initializable;
73667404

73677405
if (isa<TopLevelCodeDecl>(UseDC) &&
73687406
getDeclContext() == UseDC->getParent())
7369-
return true;
7407+
return StorageMutability::Initializable;
73707408

7371-
return false;
7409+
return StorageMutability::Immutable;
73727410
}
73737411

73747412
bool VarDecl::isLazilyInitializedGlobal() const {

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'.

0 commit comments

Comments
 (0)