Skip to content

[6.0] Revert immutable let changes 6.0 #74056

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 31 additions & 54 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5571,21 +5571,6 @@ class BuiltinTupleDecl final : public NominalTypeDecl {
}
};

/// Describes whether a particular storage declaration is mutable.
enum class StorageMutability {
/// The storage is immutable, meaning that it can neither be assigned
/// to nor passed inout.
Immutable,

/// The storage is mutable, meaning that it can be assigned and pased
/// inout.
Mutable,

/// The storage is immutable, but can be asigned for the purposes of
/// initialization.
Initializable
};

/// AbstractStorageDecl - This is the common superclass for VarDecl and
/// SubscriptDecl, representing potentially settable memory locations.
class AbstractStorageDecl : public ValueDecl {
Expand Down Expand Up @@ -5757,31 +5742,8 @@ class AbstractStorageDecl : public ValueDecl {
/// Determine whether references to this storage declaration may appear
/// on the left-hand side of an assignment, as the operand of a
/// `&` or 'inout' operator, or as a component in a writable key path.
bool isSettable(const DeclContext *useDC) const {
switch (mutability(useDC)) {
case StorageMutability::Immutable:
return false;
case StorageMutability::Mutable:
case StorageMutability::Initializable:
return true;
}
}

/// Determine the mutability of this storage declaration when
/// accessed from a given declaration context.
StorageMutability mutability(
const DeclContext *useDC,
std::optional<const DeclRefExpr *> base = std::nullopt) const;

/// Determine the mutability of this storage declaration when
/// accessed from a given declaration context in Swift.
///
/// This method differs only from 'mutability()' in its handling of
/// 'optional' storage requirements, which lack support for direct
/// writes in Swift.
StorageMutability mutabilityInSwift(
const DeclContext *useDC,
std::optional<const DeclRefExpr *> base = std::nullopt) const;
bool isSettable(const DeclContext *UseDC,
const DeclRefExpr *base = nullptr) const;

/// Determine whether references to this storage declaration in Swift may
/// appear on the left-hand side of an assignment, as the operand of a
Expand All @@ -5790,15 +5752,8 @@ class AbstractStorageDecl : public ValueDecl {
/// This method is equivalent to \c isSettable with the exception of
/// 'optional' storage requirements, which lack support for direct writes
/// in Swift.
bool isSettableInSwift(const DeclContext *useDC) const {
switch (mutabilityInSwift(useDC)) {
case StorageMutability::Immutable:
return false;
case StorageMutability::Mutable:
case StorageMutability::Initializable:
return true;
}
}
bool isSettableInSwift(const DeclContext *UseDC,
const DeclRefExpr *base = nullptr) const;

/// Does this storage declaration have explicitly-defined accessors
/// written in the source?
Expand Down Expand Up @@ -6111,11 +6066,13 @@ class VarDecl : public AbstractStorageDecl {
/// precisely point to the variable type because of type aliases.
SourceRange getTypeSourceRangeForDiagnostics() const;

/// Determine the mutability of this variable declaration when
/// accessed from a given declaration context.
StorageMutability mutability(
const DeclContext *useDC,
std::optional<const DeclRefExpr *> base = std::nullopt) const;
/// Returns whether the var is settable in the specified context: this
/// is either because it is a stored var, because it has a custom setter, or
/// is a let member in an initializer.
///
/// Pass a null context and null base to check if it's always settable.
bool isSettable(const DeclContext *UseDC,
const DeclRefExpr *base = nullptr) const;

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

inline bool AbstractStorageDecl::isSettable(const DeclContext *UseDC,
const DeclRefExpr *base) const {
if (auto vd = dyn_cast<VarDecl>(this))
return vd->isSettable(UseDC, base);

auto sd = cast<SubscriptDecl>(this);
return sd->supportsMutation();
}

inline bool
AbstractStorageDecl::isSettableInSwift(const DeclContext *UseDC,
const DeclRefExpr *base) const {
// TODO: Writing to an optional storage requirement is not supported in Swift.
if (getAttrs().hasAttribute<OptionalAttr>()) {
return false;
}

return isSettable(UseDC, base);
}

inline void
AbstractStorageDecl::overwriteSetterAccess(AccessLevel accessLevel) {
Accessors.setInt(accessLevel);
Expand Down
17 changes: 3 additions & 14 deletions include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -2092,14 +2092,6 @@ struct ClosureIsolatedByPreconcurrency {
bool operator()(const ClosureExpr *expr) const;
};

/// Determine whether the given expression is part of the left-hand side
/// of an assignment expression.
struct IsInLeftHandSideOfAssignment {
ConstraintSystem &cs;

bool operator()(Expr *expr) const;
};

/// Describes the type produced when referencing a declaration.
struct DeclReferenceType {
/// The "opened" type, which is the type of the declaration where any
Expand Down Expand Up @@ -4412,7 +4404,7 @@ class ConstraintSystem {
/// \param wantInterfaceType Whether we want the interface type, if available.
Type getUnopenedTypeOfReference(VarDecl *value, Type baseType,
DeclContext *UseDC,
ConstraintLocator *locator,
ConstraintLocator *memberLocator = nullptr,
bool wantInterfaceType = false,
bool adjustForPreconcurrency = true);

Expand All @@ -4434,7 +4426,7 @@ class ConstraintSystem {
getUnopenedTypeOfReference(
VarDecl *value, Type baseType, DeclContext *UseDC,
llvm::function_ref<Type(VarDecl *)> getType,
ConstraintLocator *locator,
ConstraintLocator *memberLocator = nullptr,
bool wantInterfaceType = false,
bool adjustForPreconcurrency = true,
llvm::function_ref<Type(const AbstractClosureExpr *)> getClosureType =
Expand All @@ -4444,10 +4436,7 @@ class ConstraintSystem {
llvm::function_ref<bool(const ClosureExpr *)> isolatedByPreconcurrency =
[](const ClosureExpr *closure) {
return closure->isIsolatedByPreconcurrency();
},
llvm::function_ref<bool(Expr *)> isAssignTarget = [](Expr *) {
return false;
});
});

/// Given the opened type and a pile of information about a member reference,
/// determine the reference type of the member reference.
Expand Down
96 changes: 26 additions & 70 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3091,36 +3091,6 @@ bool AbstractStorageDecl::isSetterMutating() const {
IsSetterMutatingRequest{const_cast<AbstractStorageDecl *>(this)}, {});
}

StorageMutability
AbstractStorageDecl::mutability(const DeclContext *useDC,
std::optional<const DeclRefExpr *> base ) const {
if (auto vd = dyn_cast<VarDecl>(this))
return vd->mutability(useDC, base);

auto sd = cast<SubscriptDecl>(this);
return sd->supportsMutation() ? StorageMutability::Mutable
: StorageMutability::Immutable;
}

/// Determine the mutability of this storage declaration when
/// accessed from a given declaration context in Swift.
///
/// This method differs only from 'mutability()' in its handling of
/// 'optional' storage requirements, which lack support for direct
/// writes in Swift.
StorageMutability
AbstractStorageDecl::mutabilityInSwift(
const DeclContext *useDC,
std::optional<const DeclRefExpr *> base
) const {
// TODO: Writing to an optional storage requirement is not supported in Swift.
if (getAttrs().hasAttribute<OptionalAttr>()) {
return StorageMutability::Immutable;
}

return mutability(useDC, base);
}

OpaqueReadOwnership AbstractStorageDecl::getOpaqueReadOwnership() const {
ASTContext &ctx = getASTContext();
return evaluateOrDefault(ctx.evaluator,
Expand Down Expand Up @@ -7291,42 +7261,32 @@ Type VarDecl::getTypeInContext() const {
return getDeclContext()->mapTypeIntoContext(getInterfaceType());
}

/// Translate an "is mutable" bit into a StorageMutability value.
static StorageMutability storageIsMutable(bool isMutable) {
return isMutable ? StorageMutability::Mutable
: StorageMutability::Immutable;
}

/// Returns whether the var is settable in the specified context: this
/// is either because it is a stored var, because it has a custom setter, or
/// is a let member in an initializer.
StorageMutability
VarDecl::mutability(const DeclContext *UseDC,
std::optional<const DeclRefExpr *> base) const {
bool VarDecl::isSettable(const DeclContext *UseDC,
const DeclRefExpr *base) const {
// Parameters are settable or not depending on their ownership convention.
if (auto *PD = dyn_cast<ParamDecl>(this))
return storageIsMutable(!PD->isImmutableInFunctionBody());
return !PD->isImmutableInFunctionBody();

// If this is a 'var' decl, then we're settable if we have storage or a
// setter.
if (!isLet()) {
if (hasInitAccessor()) {
if (auto *ctor = dyn_cast_or_null<ConstructorDecl>(UseDC)) {
// If we're referencing 'self.', it's initializable.
if (!base ||
(*base && ctor->getImplicitSelfDecl() == (*base)->getDecl()))
return StorageMutability::Initializable;

return storageIsMutable(supportsMutation());
if (base && ctor->getImplicitSelfDecl() != base->getDecl())
return supportsMutation();
return true;
}
}

return storageIsMutable(supportsMutation());
return supportsMutation();
}

// Static 'let's are always immutable.
if (isStatic()) {
return StorageMutability::Immutable;
return false;
}

//
Expand All @@ -7336,11 +7296,11 @@ VarDecl::mutability(const DeclContext *UseDC,

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

// 'let's are only ever settable from a specific DeclContext.
if (UseDC == nullptr)
return StorageMutability::Immutable;
return false;

// 'let' properties in structs/classes are only ever settable in their
// designated initializer(s) or by init accessors.
Expand All @@ -7352,65 +7312,61 @@ VarDecl::mutability(const DeclContext *UseDC,
// Check whether this property is part of `initializes` list,
// and allow assignment/mutation if so. DI would be responsible
// for checking for re-assignment.
if (accessor->isInitAccessor() &&
return accessor->isInitAccessor() &&
llvm::is_contained(accessor->getInitializedProperties(),
const_cast<VarDecl *>(this)))
return StorageMutability::Initializable;

return StorageMutability::Immutable;
const_cast<VarDecl *>(this));
}

auto *CD = dyn_cast<ConstructorDecl>(UseDC);
if (!CD) return StorageMutability::Immutable;

if (!CD) return false;
auto *CDC = CD->getDeclContext();

// 'let' properties are not valid inside protocols.
if (CDC->getExtendedProtocolDecl())
return StorageMutability::Immutable;
return false;

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

if (base && CD->getImplicitSelfDecl() != base->getDecl())
return false;

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

// If we were given a base and it is 'self', it's initializable.
if (!base || (*base && CD->getImplicitSelfDecl() == (*base)->getDecl()))
return StorageMutability::Initializable;
return false;

return StorageMutability::Immutable;
return true;
}

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

// If the 'let' has an explicitly written initializer with a pattern binding,
// then it isn't settable.
if (isParentInitialized())
return StorageMutability::Immutable;
return false;

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

if (isa<TopLevelCodeDecl>(UseDC) &&
getDeclContext() == UseDC->getParent())
return StorageMutability::Initializable;
return true;

return StorageMutability::Immutable;
return false;
}

bool VarDecl::isLazilyInitializedGlobal() const {
Expand Down
3 changes: 1 addition & 2 deletions lib/SILGen/SILGenConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -648,8 +648,7 @@ bool SILGenFunction::unsafelyInheritsExecutor() {

void ExecutorBreadcrumb::emit(SILGenFunction &SGF, SILLocation loc) {
if (mustReturnToExecutor) {
assert(SGF.ExpectedExecutor || SGF.unsafelyInheritsExecutor() ||
SGF.isCtorWithHopsInjectedByDefiniteInit());
assert(SGF.ExpectedExecutor || SGF.unsafelyInheritsExecutor());
if (auto executor = SGF.ExpectedExecutor)
SGF.B.createHopToExecutor(
RegularLocation::getDebugOnlyLocation(loc, SGF.getModule()), executor,
Expand Down
13 changes: 0 additions & 13 deletions lib/SILGen/SILGenConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,19 +618,6 @@ static bool ctorHopsInjectedByDefiniteInit(ConstructorDecl *ctor,
}
}

bool SILGenFunction::isCtorWithHopsInjectedByDefiniteInit() {
auto declRef = F.getDeclRef();
if (!declRef || !declRef.isConstructor())
return false;

auto ctor = dyn_cast_or_null<ConstructorDecl>(declRef.getDecl());
if (!ctor)
return false;

auto isolation = getActorIsolation(ctor);
return ctorHopsInjectedByDefiniteInit(ctor, isolation);
}

void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) {
MagicFunctionName = SILGenModule::getMagicFunctionName(ctor);

Expand Down
4 changes: 0 additions & 4 deletions lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -820,10 +820,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
/// the fields.
void emitDeallocatingMoveOnlyDestructor(DestructorDecl *dd);

/// Whether we are inside a constructor whose hops are injected by
/// definite initialization.
bool isCtorWithHopsInjectedByDefiniteInit();

/// Generates code for a struct constructor.
/// This allocates the new 'self' value, emits the
/// body code, then returns the final initialized 'self'.
Expand Down
Loading