Skip to content

Commit d051b62

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 2c0914c commit d051b62

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
@@ -5760,9 +5760,8 @@ class AbstractStorageDecl : public ValueDecl {
57605760
/// Determine whether references to this storage declaration may appear
57615761
/// on the left-hand side of an assignment, as the operand of a
57625762
/// `&` or 'inout' operator, or as a component in a writable key path.
5763-
bool isSettable(const DeclContext *useDC,
5764-
const DeclRefExpr *base = nullptr) const {
5765-
switch (mutability(useDC, base)) {
5763+
bool isSettable(const DeclContext *useDC) const {
5764+
switch (mutability(useDC)) {
57665765
case StorageMutability::Immutable:
57675766
return false;
57685767
case StorageMutability::Mutable:
@@ -5773,8 +5772,9 @@ class AbstractStorageDecl : public ValueDecl {
57735772

57745773
/// Determine the mutability of this storage declaration when
57755774
/// accessed from a given declaration context.
5776-
StorageMutability mutability(const DeclContext *useDC,
5777-
const DeclRefExpr *base = nullptr) const;
5775+
StorageMutability mutability(
5776+
const DeclContext *useDC,
5777+
std::optional<const DeclRefExpr *> base = std::nullopt) const;
57785778

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

57895789
/// Determine whether references to this storage declaration in Swift may
57905790
/// appear on the left-hand side of an assignment, as the operand of a
@@ -5793,9 +5793,8 @@ class AbstractStorageDecl : public ValueDecl {
57935793
/// This method is equivalent to \c isSettable with the exception of
57945794
/// 'optional' storage requirements, which lack support for direct writes
57955795
/// in Swift.
5796-
bool isSettableInSwift(const DeclContext *useDC,
5797-
const DeclRefExpr *base = nullptr) const {
5798-
switch (mutabilityInSwift(useDC, base)) {
5796+
bool isSettableInSwift(const DeclContext *useDC) const {
5797+
switch (mutabilityInSwift(useDC)) {
57995798
case StorageMutability::Immutable:
58005799
return false;
58015800
case StorageMutability::Mutable:
@@ -6117,8 +6116,9 @@ class VarDecl : public AbstractStorageDecl {
61176116

61186117
/// Determine the mutability of this variable declaration when
61196118
/// accessed from a given declaration context.
6120-
StorageMutability mutability(const DeclContext *useDC,
6121-
const DeclRefExpr *base = nullptr) const;
6119+
StorageMutability mutability(
6120+
const DeclContext *useDC,
6121+
std::optional<const DeclRefExpr *> base = std::nullopt) const;
61226122

61236123
/// Return the parent pattern binding that may provide an initializer for this
61246124
/// 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;
@@ -7312,7 +7314,7 @@ static StorageMutability storageIsMutable(bool isMutable) {
73127314
/// is a let member in an initializer.
73137315
StorageMutability
73147316
VarDecl::mutability(const DeclContext *UseDC,
7315-
const DeclRefExpr *base) const {
7317+
std::optional<const DeclRefExpr *> base) const {
73167318
// Parameters are settable or not depending on their ownership convention.
73177319
if (auto *PD = dyn_cast<ParamDecl>(this))
73187320
return storageIsMutable(!PD->isImmutableInFunctionBody());
@@ -7322,9 +7324,12 @@ VarDecl::mutability(const DeclContext *UseDC,
73227324
if (!isLet()) {
73237325
if (hasInitAccessor()) {
73247326
if (auto *ctor = dyn_cast_or_null<ConstructorDecl>(UseDC)) {
7325-
if (base && ctor->getImplicitSelfDecl() != base->getDecl())
7326-
return storageIsMutable(supportsMutation());
7327-
return StorageMutability::Initializable;
7327+
// If we're referencing 'self.', it's initializable.
7328+
if (!base ||
7329+
(*base && ctor->getImplicitSelfDecl() == (*base)->getDecl()))
7330+
return StorageMutability::Initializable;
7331+
7332+
return storageIsMutable(supportsMutation());
73287333
}
73297334
}
73307335

@@ -7382,17 +7387,18 @@ VarDecl::mutability(const DeclContext *UseDC,
73827387
getDeclContext()->getSelfNominalTypeDecl())
73837388
return StorageMutability::Immutable;
73847389

7385-
if (base && CD->getImplicitSelfDecl() != base->getDecl())
7386-
return StorageMutability::Immutable;
7387-
73887390
// If this is a convenience initializer (i.e. one that calls
73897391
// self.init), then let properties are never mutable in it. They are
73907392
// only mutable in designated initializers.
73917393
auto initKindAndExpr = CD->getDelegatingOrChainedInitKind();
73927394
if (initKindAndExpr.initKind == BodyInitKind::Delegating)
73937395
return StorageMutability::Immutable;
73947396

7395-
return StorageMutability::Initializable;
7397+
// If we were given a base and it is 'self', it's initializable.
7398+
if (!base || (*base && CD->getImplicitSelfDecl() == (*base)->getDecl()))
7399+
return StorageMutability::Initializable;
7400+
7401+
return StorageMutability::Immutable;
73967402
}
73977403

73987404
// 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)