Skip to content

Commit 8c71f89

Browse files
authored
Merge pull request #27126 from DougGregor/sr-11393-5.1-08-28-2019
[5.1 08-28-2019] [Type checker] Compute "mutating" for projected variables of wrapped properties
2 parents e51770b + b0fe74d commit 8c71f89

File tree

2 files changed

+97
-8
lines changed

2 files changed

+97
-8
lines changed

lib/Sema/TypeCheckDecl.cpp

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,11 +1911,24 @@ static bool computeIsGetterMutating(TypeChecker &TC,
19111911
// the "value" property of the outermost wrapper type is mutating and we're
19121912
// in a context that has value semantics.
19131913
if (auto var = dyn_cast<VarDecl>(storage)) {
1914-
if (auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0)) {
1915-
if (wrapperInfo.valueVar &&
1914+
VarDecl *originalVar = var->getOriginalWrappedProperty(
1915+
PropertyWrapperSynthesizedPropertyKind::StorageWrapper);
1916+
bool isProjectedValue = true;
1917+
if (!originalVar) {
1918+
originalVar = var;
1919+
isProjectedValue = false;
1920+
}
1921+
1922+
if (auto wrapperInfo = originalVar->getAttachedPropertyWrapperTypeInfo(0)) {
1923+
// Figure out which member we're looking through.
1924+
auto varMember = isProjectedValue
1925+
? &PropertyWrapperTypeInfo::projectedValueVar
1926+
: &PropertyWrapperTypeInfo::valueVar;
1927+
1928+
if (wrapperInfo.*varMember &&
19161929
(!storage->getGetter() || storage->getGetter()->isImplicit())) {
1917-
TC.validateDecl(wrapperInfo.valueVar);
1918-
return wrapperInfo.valueVar->isGetterMutating() &&
1930+
TC.validateDecl(wrapperInfo.*varMember);
1931+
return (wrapperInfo.*varMember)->isGetterMutating() &&
19191932
var->isInstanceMember() &&
19201933
doesContextHaveValueSemantics(var->getDeclContext());
19211934
}
@@ -1946,11 +1959,24 @@ static bool computeIsSetterMutating(TypeChecker &TC,
19461959
// the "value" property of the outermost wrapper type is mutating and we're
19471960
// in a context that has value semantics.
19481961
if (auto var = dyn_cast<VarDecl>(storage)) {
1949-
if (auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0)) {
1950-
if (wrapperInfo.valueVar &&
1962+
VarDecl *originalVar = var->getOriginalWrappedProperty(
1963+
PropertyWrapperSynthesizedPropertyKind::StorageWrapper);
1964+
bool isProjectedValue = true;
1965+
if (!originalVar) {
1966+
originalVar = var;
1967+
isProjectedValue = false;
1968+
}
1969+
1970+
if (auto wrapperInfo = originalVar->getAttachedPropertyWrapperTypeInfo(0)) {
1971+
// Figure out which member we're looking through.
1972+
auto varMember = isProjectedValue
1973+
? &PropertyWrapperTypeInfo::projectedValueVar
1974+
: &PropertyWrapperTypeInfo::valueVar;
1975+
1976+
if (wrapperInfo.*varMember &&
19511977
(!storage->getSetter() || storage->getSetter()->isImplicit())) {
1952-
TC.validateDecl(wrapperInfo.valueVar);
1953-
return wrapperInfo.valueVar->isSetterMutating() &&
1978+
TC.validateDecl(wrapperInfo.*varMember);
1979+
return (wrapperInfo.*varMember)->isSetterMutating() &&
19541980
var->isInstanceMember() &&
19551981
doesContextHaveValueSemantics(var->getDeclContext());
19561982
}

test/decl/var/property_wrappers.swift

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,3 +1068,66 @@ struct MissingPropertyWrapperUnwrap {
10681068
d(self._usesProjectedValue) // expected-error {{cannot convert value '_usesProjectedValue' of type 'Baz<W>' to expected type 'W', use wrapped value instead}}{{12-13=}}
10691069
}
10701070
}
1071+
1072+
// SR-11393
1073+
protocol Copyable: AnyObject {
1074+
func copy() -> Self
1075+
}
1076+
1077+
@propertyWrapper
1078+
struct CopyOnWrite<Value: Copyable> {
1079+
init(wrappedValue: Value) {
1080+
self.wrappedValue = wrappedValue
1081+
}
1082+
1083+
var wrappedValue: Value
1084+
1085+
var projectedValue: Value {
1086+
mutating get {
1087+
if !isKnownUniquelyReferenced(&wrappedValue) {
1088+
wrappedValue = wrappedValue.copy()
1089+
}
1090+
return wrappedValue
1091+
}
1092+
set {
1093+
wrappedValue = newValue
1094+
}
1095+
}
1096+
}
1097+
1098+
final class CopyOnWriteTest: Copyable {
1099+
let a: Int
1100+
init(a: Int) {
1101+
self.a = a
1102+
}
1103+
1104+
func copy() -> Self {
1105+
Self.init(a: a)
1106+
}
1107+
}
1108+
1109+
struct CopyOnWriteDemo1 {
1110+
@CopyOnWrite var a = CopyOnWriteTest(a: 3)
1111+
1112+
func foo() { // expected-note{{mark method 'mutating' to make 'self' mutable}}
1113+
_ = $a // expected-error{{cannot use mutating getter on immutable value: 'self' is immutable}}
1114+
}
1115+
}
1116+
1117+
@propertyWrapper
1118+
struct NonMutatingProjectedValueSetWrapper<Value> {
1119+
var wrappedValue: Value
1120+
var projectedValue: Value {
1121+
get { wrappedValue }
1122+
nonmutating set { }
1123+
}
1124+
}
1125+
1126+
struct UseNonMutatingProjectedValueSet {
1127+
@NonMutatingProjectedValueSetWrapper var x = 17
1128+
1129+
func test() { // expected-note{{mark method 'mutating' to make 'self' mutable}}
1130+
$x = 42 // okay
1131+
x = 42 // expected-error{{cannot assign to property: 'self' is immutable}}
1132+
}
1133+
}

0 commit comments

Comments
 (0)