Skip to content

[6.0] Ensure that we do not turn rvalues into lvalues #73910

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
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
22 changes: 11 additions & 11 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5757,9 +5757,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 DeclRefExpr *base = nullptr) const {
switch (mutability(useDC, base)) {
bool isSettable(const DeclContext *useDC) const {
switch (mutability(useDC)) {
case StorageMutability::Immutable:
return false;
case StorageMutability::Mutable:
Expand All @@ -5770,8 +5769,9 @@ class AbstractStorageDecl : public ValueDecl {

/// Determine the mutability of this storage declaration when
/// accessed from a given declaration context.
StorageMutability mutability(const DeclContext *useDC,
const DeclRefExpr *base = nullptr) const;
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.
Expand All @@ -5781,7 +5781,7 @@ class AbstractStorageDecl : public ValueDecl {
/// writes in Swift.
StorageMutability mutabilityInSwift(
const DeclContext *useDC,
const DeclRefExpr *base = nullptr) const;
std::optional<const DeclRefExpr *> base = std::nullopt) 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,9 +5790,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 DeclRefExpr *base = nullptr) const {
switch (mutabilityInSwift(useDC, base)) {
bool isSettableInSwift(const DeclContext *useDC) const {
switch (mutabilityInSwift(useDC)) {
case StorageMutability::Immutable:
return false;
case StorageMutability::Mutable:
Expand Down Expand Up @@ -6114,8 +6113,9 @@ class VarDecl : public AbstractStorageDecl {

/// Determine the mutability of this variable declaration when
/// accessed from a given declaration context.
StorageMutability mutability(const DeclContext *useDC,
const DeclRefExpr *base = nullptr) const;
StorageMutability mutability(
const DeclContext *useDC,
std::optional<const DeclRefExpr *> base = std::nullopt) 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
28 changes: 17 additions & 11 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3093,7 +3093,7 @@ bool AbstractStorageDecl::isSetterMutating() const {

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

Expand All @@ -3109,8 +3109,10 @@ AbstractStorageDecl::mutability(const DeclContext *useDC,
/// 'optional' storage requirements, which lack support for direct
/// writes in Swift.
StorageMutability
AbstractStorageDecl::mutabilityInSwift(const DeclContext *useDC,
const DeclRefExpr *base) const {
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;
Expand Down Expand Up @@ -7300,7 +7302,7 @@ static StorageMutability storageIsMutable(bool isMutable) {
/// is a let member in an initializer.
StorageMutability
VarDecl::mutability(const DeclContext *UseDC,
const DeclRefExpr *base) const {
std::optional<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());
Expand All @@ -7310,9 +7312,12 @@ VarDecl::mutability(const DeclContext *UseDC,
if (!isLet()) {
if (hasInitAccessor()) {
if (auto *ctor = dyn_cast_or_null<ConstructorDecl>(UseDC)) {
if (base && ctor->getImplicitSelfDecl() != base->getDecl())
return storageIsMutable(supportsMutation());
return StorageMutability::Initializable;
// If we're referencing 'self.', it's initializable.
if (!base ||
(*base && ctor->getImplicitSelfDecl() == (*base)->getDecl()))
return StorageMutability::Initializable;

return storageIsMutable(supportsMutation());
}
}

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

if (base && CD->getImplicitSelfDecl() != base->getDecl())
return StorageMutability::Immutable;

// 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;

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

return StorageMutability::Immutable;
}

// If the 'let' has a value bound to it but has no PBD, then it is
Expand Down
22 changes: 22 additions & 0 deletions test/decl/init/let-mutability.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// RUN: %target-swift-frontend -typecheck -dump-ast %s | %FileCheck %s

public struct Data {
init(_ bytes: [UInt8]) { }
}

internal struct Item {
public let data: Data

public init(tag: UInt8) {
self.data = Data([tag << 2])
}

// CHECK-LABEL: constructor_decl{{.*}}"init(tag:value:)"
public init(tag: UInt8, value: UInt) {
// CHECK: assign_expr
// CHECK: member_ref_expr type="@lvalue Data"
// CHECK-NEXT: declref_expr type="@lvalue Item"
// CHECK-NEXT: member_ref_expr type="Data"
self.data = Self(tag: tag).data
}
}