Skip to content

Commit 525e245

Browse files
authored
Merge pull request swiftlang#73909 from DougGregor/let-instance-property-mutability-fix
Ensure that we do not turn rvalues into lvalues
2 parents 3693592 + d051b62 commit 525e245

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
@@ -5773,9 +5773,8 @@ class AbstractStorageDecl : public ValueDecl {
57735773
/// Determine whether references to this storage declaration may appear
57745774
/// on the left-hand side of an assignment, as the operand of a
57755775
/// `&` or 'inout' operator, or as a component in a writable key path.
5776-
bool isSettable(const DeclContext *useDC,
5777-
const DeclRefExpr *base = nullptr) const {
5778-
switch (mutability(useDC, base)) {
5776+
bool isSettable(const DeclContext *useDC) const {
5777+
switch (mutability(useDC)) {
57795778
case StorageMutability::Immutable:
57805779
return false;
57815780
case StorageMutability::Mutable:
@@ -5786,8 +5785,9 @@ class AbstractStorageDecl : public ValueDecl {
57865785

57875786
/// Determine the mutability of this storage declaration when
57885787
/// accessed from a given declaration context.
5789-
StorageMutability mutability(const DeclContext *useDC,
5790-
const DeclRefExpr *base = nullptr) const;
5788+
StorageMutability mutability(
5789+
const DeclContext *useDC,
5790+
std::optional<const DeclRefExpr *> base = std::nullopt) const;
57915791

57925792
/// Determine the mutability of this storage declaration when
57935793
/// accessed from a given declaration context in Swift.
@@ -5797,7 +5797,7 @@ class AbstractStorageDecl : public ValueDecl {
57975797
/// writes in Swift.
57985798
StorageMutability mutabilityInSwift(
57995799
const DeclContext *useDC,
5800-
const DeclRefExpr *base = nullptr) const;
5800+
std::optional<const DeclRefExpr *> base = std::nullopt) const;
58015801

58025802
/// Determine whether references to this storage declaration in Swift may
58035803
/// appear on the left-hand side of an assignment, as the operand of a
@@ -5806,9 +5806,8 @@ class AbstractStorageDecl : public ValueDecl {
58065806
/// This method is equivalent to \c isSettable with the exception of
58075807
/// 'optional' storage requirements, which lack support for direct writes
58085808
/// in Swift.
5809-
bool isSettableInSwift(const DeclContext *useDC,
5810-
const DeclRefExpr *base = nullptr) const {
5811-
switch (mutabilityInSwift(useDC, base)) {
5809+
bool isSettableInSwift(const DeclContext *useDC) const {
5810+
switch (mutabilityInSwift(useDC)) {
58125811
case StorageMutability::Immutable:
58135812
return false;
58145813
case StorageMutability::Mutable:
@@ -6137,8 +6136,9 @@ class VarDecl : public AbstractStorageDecl {
61376136

61386137
/// Determine the mutability of this variable declaration when
61396138
/// accessed from a given declaration context.
6140-
StorageMutability mutability(const DeclContext *useDC,
6141-
const DeclRefExpr *base = nullptr) const;
6139+
StorageMutability mutability(
6140+
const DeclContext *useDC,
6141+
std::optional<const DeclRefExpr *> base = std::nullopt) const;
61426142

61436143
/// Return the parent pattern binding that may provide an initializer for this
61446144
/// 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
@@ -3087,7 +3087,7 @@ bool AbstractStorageDecl::isSetterMutating() const {
30873087

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

@@ -3103,8 +3103,10 @@ AbstractStorageDecl::mutability(const DeclContext *useDC,
31033103
/// 'optional' storage requirements, which lack support for direct
31043104
/// writes in Swift.
31053105
StorageMutability
3106-
AbstractStorageDecl::mutabilityInSwift(const DeclContext *useDC,
3107-
const DeclRefExpr *base) const {
3106+
AbstractStorageDecl::mutabilityInSwift(
3107+
const DeclContext *useDC,
3108+
std::optional<const DeclRefExpr *> base
3109+
) const {
31083110
// TODO: Writing to an optional storage requirement is not supported in Swift.
31093111
if (getAttrs().hasAttribute<OptionalAttr>()) {
31103112
return StorageMutability::Immutable;
@@ -7392,7 +7394,7 @@ static StorageMutability storageIsMutable(bool isMutable) {
73927394
/// is a let member in an initializer.
73937395
StorageMutability
73947396
VarDecl::mutability(const DeclContext *UseDC,
7395-
const DeclRefExpr *base) const {
7397+
std::optional<const DeclRefExpr *> base) const {
73967398
// Parameters are settable or not depending on their ownership convention.
73977399
if (auto *PD = dyn_cast<ParamDecl>(this))
73987400
return storageIsMutable(!PD->isImmutableInFunctionBody());
@@ -7402,9 +7404,12 @@ VarDecl::mutability(const DeclContext *UseDC,
74027404
if (!isLet()) {
74037405
if (hasInitAccessor()) {
74047406
if (auto *ctor = dyn_cast_or_null<ConstructorDecl>(UseDC)) {
7405-
if (base && ctor->getImplicitSelfDecl() != base->getDecl())
7406-
return storageIsMutable(supportsMutation());
7407-
return StorageMutability::Initializable;
7407+
// If we're referencing 'self.', it's initializable.
7408+
if (!base ||
7409+
(*base && ctor->getImplicitSelfDecl() == (*base)->getDecl()))
7410+
return StorageMutability::Initializable;
7411+
7412+
return storageIsMutable(supportsMutation());
74087413
}
74097414
}
74107415

@@ -7462,17 +7467,18 @@ VarDecl::mutability(const DeclContext *UseDC,
74627467
getDeclContext()->getSelfNominalTypeDecl())
74637468
return StorageMutability::Immutable;
74647469

7465-
if (base && CD->getImplicitSelfDecl() != base->getDecl())
7466-
return StorageMutability::Immutable;
7467-
74687470
// If this is a convenience initializer (i.e. one that calls
74697471
// self.init), then let properties are never mutable in it. They are
74707472
// only mutable in designated initializers.
74717473
auto initKindAndExpr = CD->getDelegatingOrChainedInitKind();
74727474
if (initKindAndExpr.initKind == BodyInitKind::Delegating)
74737475
return StorageMutability::Immutable;
74747476

7475-
return StorageMutability::Initializable;
7477+
// If we were given a base and it is 'self', it's initializable.
7478+
if (!base || (*base && CD->getImplicitSelfDecl() == (*base)->getDecl()))
7479+
return StorageMutability::Initializable;
7480+
7481+
return StorageMutability::Immutable;
74767482
}
74777483

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