Skip to content

Commit c99facb

Browse files
authored
Merge pull request swiftlang#27057 from theblixguy/unrevert/SR-11298
Unrevert "[Sema] Setter has incorrect mutating-ness inside class-constrained protocol extension"
2 parents 9a24013 + 3d058c6 commit c99facb

File tree

8 files changed

+92
-25
lines changed

8 files changed

+92
-25
lines changed

CHANGELOG.md

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,63 @@ CHANGELOG
2626
Swift Next
2727
----------
2828

29+
* [SR-11298][]:
30+
31+
A class-constrained protocol extension, where the extended protocol does
32+
not impose a class constraint, will now infer the constraint implicitly.
33+
34+
```swift
35+
protocol Foo {}
36+
class Bar: Foo {
37+
var someProperty: Int = 0
38+
}
39+
40+
// Even though 'Foo' does not impose a class constraint, it is automatically
41+
// inferred due to the Self: Bar constraint.
42+
extension Foo where Self: Bar {
43+
var anotherProperty: Int {
44+
get { return someProperty }
45+
// As a result, the setter is now implicitly nonmutating, just like it would
46+
// be if 'Foo' had a class constraint.
47+
set { someProperty = newValue }
48+
}
49+
}
50+
```
51+
52+
As a result, this could lead to code that currently compiles today to throw an error.
53+
54+
```swift
55+
protocol Foo {
56+
var someProperty: Int { get set }
57+
}
58+
59+
class Bar: Foo {
60+
var someProperty = 0
61+
}
62+
63+
extension Foo where Self: Bar {
64+
var anotherProperty1: Int {
65+
get { return someProperty }
66+
// This will now error, because the protocol requirement
67+
// is implicitly mutating and the setter is implicitly
68+
// nonmutating.
69+
set { someProperty = newValue } // Error
70+
}
71+
}
72+
```
73+
74+
**Workaround**: Define a new mutable variable inside the setter that has a reference to `self`:
75+
76+
```swift
77+
var anotherProperty1: Int {
78+
get { return someProperty }
79+
set {
80+
var mutableSelf = self
81+
mutableSelf.someProperty = newValue // Okay
82+
}
83+
}
84+
```
85+
2986
* [SE-0253][]:
3087

3188
Values of types that declare `func callAsFunction` methods can be called
@@ -51,7 +108,7 @@ Swift Next
51108

52109
* [SR-4206][]:
53110

54-
A method override is no longer allowed to have a generic signature with
111+
A method override is no longer allowed to have a generic signature with
55112
requirements not imposed by the base method. For example:
56113

57114
```
@@ -7765,3 +7822,4 @@ Swift 1.0
77657822
[SR-8974]: <https://bugs.swift.org/browse/SR-8974>
77667823
[SR-9043]: <https://bugs.swift.org/browse/SR-9043>
77677824
[SR-9827]: <https://bugs.swift.org/browse/SR-9827>
7825+
[SR-11298]: <https://bugs.swift.org/browse/SR-11298>

include/swift/AST/DeclContext.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,10 @@ class alignas(1 << DeclContextAlignInBits) DeclContext {
261261

262262
/// Returns the kind of context this is.
263263
DeclContextKind getContextKind() const;
264-
264+
265+
/// Returns whether this context has value semantics.
266+
bool hasValueSemantics() const;
267+
265268
/// Determines whether this context is itself a local scope in a
266269
/// code block. A context that appears in such a scope, like a
267270
/// local type declaration, does not itself become a local context.

lib/AST/Decl.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5634,12 +5634,18 @@ void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC) const {
56345634
if (FD && !FD->isMutating() && !FD->isImplicit() && FD->isInstanceMember()&&
56355635
!FD->getDeclContext()->getDeclaredInterfaceType()
56365636
->hasReferenceSemantics()) {
5637-
// Do not suggest the fix it in implicit getters
5637+
// Do not suggest the fix-it in implicit getters
56385638
if (auto AD = dyn_cast<AccessorDecl>(FD)) {
56395639
if (AD->isGetter() && !AD->getAccessorKeywordLoc().isValid())
56405640
return;
5641+
5642+
auto accessorDC = AD->getDeclContext();
5643+
// Do not suggest the fix-it if `Self` is a class type.
5644+
if (accessorDC->isTypeContext() && !accessorDC->hasValueSemantics()) {
5645+
return;
5646+
}
56415647
}
5642-
5648+
56435649
auto &d = getASTContext().Diags;
56445650
d.diagnose(FD->getFuncLoc(), diag::change_to_mutating,
56455651
isa<AccessorDecl>(FD))

lib/AST/DeclContext.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,14 @@ DeclContextKind DeclContext::getContextKind() const {
10181018
llvm_unreachable("Unhandled DeclContext ASTHierarchy");
10191019
}
10201020

1021+
bool DeclContext::hasValueSemantics() const {
1022+
if (auto contextTy = getSelfTypeInContext()) {
1023+
return !contextTy->hasReferenceSemantics();
1024+
}
1025+
1026+
return false;
1027+
}
1028+
10211029
SourceLoc swift::extractNearestSourceLoc(const DeclContext *dc) {
10221030
switch (dc->getContextKind()) {
10231031
case DeclContextKind::AbstractFunctionDecl:

lib/Sema/TypeCheckDecl.cpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1953,17 +1953,10 @@ void TypeChecker::validateDecl(OperatorDecl *OD) {
19531953
}
19541954
}
19551955

1956-
bool swift::doesContextHaveValueSemantics(DeclContext *dc) {
1957-
if (Type contextTy = dc->getDeclaredInterfaceType())
1958-
return !contextTy->hasReferenceSemantics();
1959-
return false;
1960-
}
1961-
19621956
llvm::Expected<SelfAccessKind>
19631957
SelfAccessKindRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
19641958
if (FD->getAttrs().getAttribute<MutatingAttr>(true)) {
1965-
if (!FD->isInstanceMember() ||
1966-
!doesContextHaveValueSemantics(FD->getDeclContext())) {
1959+
if (!FD->isInstanceMember() || !FD->getDeclContext()->hasValueSemantics()) {
19671960
return SelfAccessKind::NonMutating;
19681961
}
19691962
return SelfAccessKind::Mutating;
@@ -1985,8 +1978,7 @@ SelfAccessKindRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
19851978
case AccessorKind::MutableAddress:
19861979
case AccessorKind::Set:
19871980
case AccessorKind::Modify:
1988-
if (AD->isInstanceMember() &&
1989-
doesContextHaveValueSemantics(AD->getDeclContext()))
1981+
if (AD->isInstanceMember() && AD->getDeclContext()->hasValueSemantics())
19901982
return SelfAccessKind::Mutating;
19911983
break;
19921984

lib/Sema/TypeCheckDecl.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ class DeclContext;
2525
class ValueDecl;
2626
class Pattern;
2727

28-
bool doesContextHaveValueSemantics(DeclContext *dc);
29-
3028
/// Walks up the override chain for \p CD until it finds an initializer that is
3129
/// required and non-implicit. If no such initializer exists, returns the
3230
/// declaration where \c required was introduced (i.e. closest to the root

lib/Sema/TypeCheckStorage.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,9 @@ void swift::validatePatternBindingEntries(TypeChecker &tc,
268268
llvm::Expected<bool>
269269
IsGetterMutatingRequest::evaluate(Evaluator &evaluator,
270270
AbstractStorageDecl *storage) const {
271-
bool result = (!storage->isStatic() &&
272-
doesContextHaveValueSemantics(storage->getDeclContext()));
271+
auto storageDC = storage->getDeclContext();
272+
bool result = (!storage->isStatic() && storageDC->isTypeContext() &&
273+
storageDC->hasValueSemantics());
273274

274275
// 'lazy' overrides the normal accessor-based rules and heavily
275276
// restricts what accessors can be used. The getter is considered
@@ -297,7 +298,7 @@ IsGetterMutatingRequest::evaluate(Evaluator &evaluator,
297298

298299
// Protocol requirements are always written as '{ get }' or '{ get set }';
299300
// the @_borrowed attribute determines if getReadImpl() becomes Get or Read.
300-
if (isa<ProtocolDecl>(storage->getDeclContext()))
301+
if (isa<ProtocolDecl>(storageDC))
301302
return checkMutability(AccessorKind::Get);
302303

303304
switch (storage->getReadImpl()) {
@@ -323,8 +324,9 @@ IsSetterMutatingRequest::evaluate(Evaluator &evaluator,
323324
AbstractStorageDecl *storage) const {
324325
// By default, the setter is mutating if we have an instance member of a
325326
// value type, but this can be overridden below.
326-
bool result = (!storage->isStatic() &&
327-
doesContextHaveValueSemantics(storage->getDeclContext()));
327+
auto storageDC = storage->getDeclContext();
328+
bool result = (!storage->isStatic() && storageDC->isTypeContext() &&
329+
storageDC->hasValueSemantics());
328330

329331
// If we have an attached property wrapper, the setter is mutating
330332
// or not based on the composition of the wrappers.

test/decl/ext/extensions.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ struct WrapperContext {
126126
}
127127

128128
// Class-constrained extension where protocol does not impose class requirement
129-
129+
// SR-11298
130130
protocol DoesNotImposeClassReq_1 {}
131131

132132
class JustAClass: DoesNotImposeClassReq_1 {
@@ -140,8 +140,8 @@ extension DoesNotImposeClassReq_1 where Self: JustAClass {
140140
}
141141
}
142142

143-
let instanceOfJustAClass = JustAClass() // expected-note {{change 'let' to 'var' to make it mutable}}
144-
instanceOfJustAClass.wrappingProperty = "" // expected-error {{cannot assign to property: 'instanceOfJustAClass' is a 'let' constant}}
143+
let instanceOfJustAClass = JustAClass()
144+
instanceOfJustAClass.wrappingProperty = "" // Okay
145145

146146
protocol DoesNotImposeClassReq_2 {
147147
var property: String { get set }
@@ -150,7 +150,7 @@ protocol DoesNotImposeClassReq_2 {
150150
extension DoesNotImposeClassReq_2 where Self : AnyObject {
151151
var wrappingProperty: String {
152152
get { property }
153-
set { property = newValue } // Okay
153+
set { property = newValue } // expected-error {{cannot assign to property: 'self' is immutable}}
154154
}
155155
}
156156

0 commit comments

Comments
 (0)