Skip to content

Commit 9ce12ac

Browse files
committed
[Type checker] Compute "mutating" for projected variables of wrapped properties
Extend the recently-refactored logic for determining whether the getter/setter for a wrapped property is "mutating" to also consider the projection variables (e.g., $foo). Fixes SR-11393 / rdar://problem/54839570.
1 parent b379097 commit 9ce12ac

File tree

2 files changed

+87
-8
lines changed

2 files changed

+87
-8
lines changed

lib/Sema/TypeCheckStorage.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,27 +2214,43 @@ getSetterMutatingness(VarDecl *var, DeclContext *dc) {
22142214
llvm::Expected<Optional<PropertyWrapperMutability>>
22152215
PropertyWrapperMutabilityRequest::evaluate(Evaluator &,
22162216
VarDecl *var) const {
2217-
unsigned numWrappers = var->getAttachedPropertyWrappers().size();
2218-
if (numWrappers < 1)
2219-
return None;
2217+
VarDecl *originalVar = var;
2218+
unsigned numWrappers = originalVar->getAttachedPropertyWrappers().size();
2219+
bool isProjectedValue = false;
2220+
if (numWrappers < 1) {
2221+
originalVar = var->getOriginalWrappedProperty(
2222+
PropertyWrapperSynthesizedPropertyKind::StorageWrapper);
2223+
if (!originalVar)
2224+
return None;
2225+
2226+
numWrappers = originalVar->getAttachedPropertyWrappers().size();
2227+
isProjectedValue = true;
2228+
}
2229+
22202230
if (var->getParsedAccessor(AccessorKind::Get))
22212231
return None;
22222232
if (var->getParsedAccessor(AccessorKind::Set))
22232233
return None;
22242234

2235+
// Figure out which member we're looking through.
2236+
auto varMember = isProjectedValue
2237+
? &PropertyWrapperTypeInfo::projectedValueVar
2238+
: &PropertyWrapperTypeInfo::valueVar;
2239+
22252240
// Start with the traits from the outermost wrapper.
2226-
auto firstWrapper = var->getAttachedPropertyWrapperTypeInfo(0);
2227-
if (!firstWrapper.valueVar)
2241+
auto firstWrapper = originalVar->getAttachedPropertyWrapperTypeInfo(0);
2242+
if (firstWrapper.*varMember == nullptr)
22282243
return None;
22292244

22302245
PropertyWrapperMutability result;
22312246

2232-
result.Getter = getGetterMutatingness(firstWrapper.valueVar);
2233-
result.Setter = getSetterMutatingness(firstWrapper.valueVar,
2247+
result.Getter = getGetterMutatingness(firstWrapper.*varMember);
2248+
result.Setter = getSetterMutatingness(firstWrapper.*varMember,
22342249
var->getInnermostDeclContext());
22352250

22362251
// Compose the traits of the following wrappers.
2237-
for (unsigned i = 1; i < numWrappers; ++i) {
2252+
for (unsigned i = 1; i < numWrappers && !isProjectedValue; ++i) {
2253+
assert(var == originalVar);
22382254
auto wrapper = var->getAttachedPropertyWrapperTypeInfo(i);
22392255
if (!wrapper.valueVar)
22402256
return None;

test/decl/var/property_wrappers.swift

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,3 +1581,66 @@ protocol SR_11288_P5 {
15811581
struct SR_11288_S5<T>: SR_11288_P5 {
15821582
@SR_11288_Wrapper5 var answer = 42 // Okay
15831583
}
1584+
1585+
// SR-11393
1586+
protocol Copyable: AnyObject {
1587+
func copy() -> Self
1588+
}
1589+
1590+
@propertyWrapper
1591+
struct CopyOnWrite<Value: Copyable> {
1592+
init(wrappedValue: Value) {
1593+
self.wrappedValue = wrappedValue
1594+
}
1595+
1596+
var wrappedValue: Value
1597+
1598+
var projectedValue: Value {
1599+
mutating get {
1600+
if !isKnownUniquelyReferenced(&wrappedValue) {
1601+
wrappedValue = wrappedValue.copy()
1602+
}
1603+
return wrappedValue
1604+
}
1605+
set {
1606+
wrappedValue = newValue
1607+
}
1608+
}
1609+
}
1610+
1611+
final class CopyOnWriteTest: Copyable {
1612+
let a: Int
1613+
init(a: Int) {
1614+
self.a = a
1615+
}
1616+
1617+
func copy() -> Self {
1618+
Self.init(a: a)
1619+
}
1620+
}
1621+
1622+
struct CopyOnWriteDemo1 {
1623+
@CopyOnWrite var a = CopyOnWriteTest(a: 3)
1624+
1625+
func foo() { // expected-note{{mark method 'mutating' to make 'self' mutable}}
1626+
_ = $a // expected-error{{cannot use mutating getter on immutable value: 'self' is immutable}}
1627+
}
1628+
}
1629+
1630+
@propertyWrapper
1631+
struct NonMutatingProjectedValueSetWrapper<Value> {
1632+
var wrappedValue: Value
1633+
var projectedValue: Value {
1634+
get { wrappedValue }
1635+
nonmutating set { }
1636+
}
1637+
}
1638+
1639+
struct UseNonMutatingProjectedValueSet {
1640+
@NonMutatingProjectedValueSetWrapper var x = 17
1641+
1642+
func test() { // expected-note{{mark method 'mutating' to make 'self' mutable}}
1643+
$x = 42 // okay
1644+
x = 42 // expected-error{{cannot assign to property: 'self' is immutable}}
1645+
}
1646+
}

0 commit comments

Comments
 (0)