Skip to content

Commit 4b59301

Browse files
committed
Ensure that we do not turn rvalues into lvalues
The computation that determined whether an access to a `let` instance property within a constructor should be an initialization conflated the cases of "we don't have a base expression" and "the base expression is not something that could be `self`", and incorrectly identified rvalue bases as being "initializable". Make the interface properly separate out these cases, so we don't turn an lvalue into an rvalue access. Fixes rdar://128661833.
1 parent f8cc074 commit 4b59301

File tree

3 files changed

+50
-22
lines changed

3 files changed

+50
-22
lines changed

include/swift/AST/Decl.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5757,9 +5757,8 @@ class AbstractStorageDecl : public ValueDecl {
57575757
/// Determine whether references to this storage declaration may appear
57585758
/// on the left-hand side of an assignment, as the operand of a
57595759
/// `&` or 'inout' operator, or as a component in a writable key path.
5760-
bool isSettable(const DeclContext *useDC,
5761-
const DeclRefExpr *base = nullptr) const {
5762-
switch (mutability(useDC, base)) {
5760+
bool isSettable(const DeclContext *useDC) const {
5761+
switch (mutability(useDC)) {
57635762
case StorageMutability::Immutable:
57645763
return false;
57655764
case StorageMutability::Mutable:
@@ -5770,8 +5769,9 @@ class AbstractStorageDecl : public ValueDecl {
57705769

57715770
/// Determine the mutability of this storage declaration when
57725771
/// accessed from a given declaration context.
5773-
StorageMutability mutability(const DeclContext *useDC,
5774-
const DeclRefExpr *base = nullptr) const;
5772+
StorageMutability mutability(
5773+
const DeclContext *useDC,
5774+
std::optional<const DeclRefExpr *> base = std::nullopt) const;
57755775

57765776
/// Determine the mutability of this storage declaration when
57775777
/// accessed from a given declaration context in Swift.
@@ -5781,7 +5781,7 @@ class AbstractStorageDecl : public ValueDecl {
57815781
/// writes in Swift.
57825782
StorageMutability mutabilityInSwift(
57835783
const DeclContext *useDC,
5784-
const DeclRefExpr *base = nullptr) const;
5784+
std::optional<const DeclRefExpr *> base = std::nullopt) const;
57855785

57865786
/// Determine whether references to this storage declaration in Swift may
57875787
/// appear on the left-hand side of an assignment, as the operand of a
@@ -5790,9 +5790,8 @@ class AbstractStorageDecl : public ValueDecl {
57905790
/// This method is equivalent to \c isSettable with the exception of
57915791
/// 'optional' storage requirements, which lack support for direct writes
57925792
/// in Swift.
5793-
bool isSettableInSwift(const DeclContext *useDC,
5794-
const DeclRefExpr *base = nullptr) const {
5795-
switch (mutabilityInSwift(useDC, base)) {
5793+
bool isSettableInSwift(const DeclContext *useDC) const {
5794+
switch (mutabilityInSwift(useDC)) {
57965795
case StorageMutability::Immutable:
57975796
return false;
57985797
case StorageMutability::Mutable:
@@ -6114,8 +6113,9 @@ class VarDecl : public AbstractStorageDecl {
61146113

61156114
/// Determine the mutability of this variable declaration when
61166115
/// accessed from a given declaration context.
6117-
StorageMutability mutability(const DeclContext *useDC,
6118-
const DeclRefExpr *base = nullptr) const;
6116+
StorageMutability mutability(
6117+
const DeclContext *useDC,
6118+
std::optional<const DeclRefExpr *> base = std::nullopt) const;
61196119

61206120
/// Return the parent pattern binding that may provide an initializer for this
61216121
/// VarDecl. This returns null if there is none associated with the VarDecl.

lib/AST/Decl.cpp

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3093,7 +3093,7 @@ bool AbstractStorageDecl::isSetterMutating() const {
30933093

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

@@ -3109,8 +3109,10 @@ AbstractStorageDecl::mutability(const DeclContext *useDC,
31093109
/// 'optional' storage requirements, which lack support for direct
31103110
/// writes in Swift.
31113111
StorageMutability
3112-
AbstractStorageDecl::mutabilityInSwift(const DeclContext *useDC,
3113-
const DeclRefExpr *base) const {
3112+
AbstractStorageDecl::mutabilityInSwift(
3113+
const DeclContext *useDC,
3114+
std::optional<const DeclRefExpr *> base
3115+
) const {
31143116
// TODO: Writing to an optional storage requirement is not supported in Swift.
31153117
if (getAttrs().hasAttribute<OptionalAttr>()) {
31163118
return StorageMutability::Immutable;
@@ -7300,7 +7302,7 @@ static StorageMutability storageIsMutable(bool isMutable) {
73007302
/// is a let member in an initializer.
73017303
StorageMutability
73027304
VarDecl::mutability(const DeclContext *UseDC,
7303-
const DeclRefExpr *base) const {
7305+
std::optional<const DeclRefExpr *> base) const {
73047306
// Parameters are settable or not depending on their ownership convention.
73057307
if (auto *PD = dyn_cast<ParamDecl>(this))
73067308
return storageIsMutable(!PD->isImmutableInFunctionBody());
@@ -7310,9 +7312,12 @@ VarDecl::mutability(const DeclContext *UseDC,
73107312
if (!isLet()) {
73117313
if (hasInitAccessor()) {
73127314
if (auto *ctor = dyn_cast_or_null<ConstructorDecl>(UseDC)) {
7313-
if (base && ctor->getImplicitSelfDecl() != base->getDecl())
7314-
return storageIsMutable(supportsMutation());
7315-
return StorageMutability::Initializable;
7315+
// If we're referencing 'self.', it's initializable.
7316+
if (!base ||
7317+
(*base && ctor->getImplicitSelfDecl() == (*base)->getDecl()))
7318+
return StorageMutability::Initializable;
7319+
7320+
return storageIsMutable(supportsMutation());
73167321
}
73177322
}
73187323

@@ -7370,17 +7375,18 @@ VarDecl::mutability(const DeclContext *UseDC,
73707375
getDeclContext()->getSelfNominalTypeDecl())
73717376
return StorageMutability::Immutable;
73727377

7373-
if (base && CD->getImplicitSelfDecl() != base->getDecl())
7374-
return StorageMutability::Immutable;
7375-
73767378
// If this is a convenience initializer (i.e. one that calls
73777379
// self.init), then let properties are never mutable in it. They are
73787380
// only mutable in designated initializers.
73797381
auto initKindAndExpr = CD->getDelegatingOrChainedInitKind();
73807382
if (initKindAndExpr.initKind == BodyInitKind::Delegating)
73817383
return StorageMutability::Immutable;
73827384

7383-
return StorageMutability::Initializable;
7385+
// If we were given a base and it is 'self', it's initializable.
7386+
if (!base || (*base && CD->getImplicitSelfDecl() == (*base)->getDecl()))
7387+
return StorageMutability::Initializable;
7388+
7389+
return StorageMutability::Immutable;
73847390
}
73857391

73867392
// If the 'let' has a value bound to it but has no PBD, then it is

test/decl/init/let-mutability.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-swift-frontend -typecheck -dump-ast %s | %FileCheck %s
2+
3+
public struct Data {
4+
init(_ bytes: [UInt8]) { }
5+
}
6+
7+
internal struct Item {
8+
public let data: Data
9+
10+
public init(tag: UInt8) {
11+
self.data = Data([tag << 2])
12+
}
13+
14+
// CHECK-LABEL: constructor_decl{{.*}}"init(tag:value:)"
15+
public init(tag: UInt8, value: UInt) {
16+
// CHECK: assign_expr
17+
// CHECK: member_ref_expr type="@lvalue Data"
18+
// CHECK-NEXT: declref_expr type="@lvalue Item"
19+
// CHECK-NEXT: member_ref_expr type="Data"
20+
self.data = Self(tag: tag).data
21+
}
22+
}

0 commit comments

Comments
 (0)