Skip to content

Commit 70d77e9

Browse files
committed
Revert "Revert "Merge pull request #27057 from theblixguy/unrevert/SR-11298""
This reverts commit 7725818.
1 parent 7214b0d commit 70d77e9

File tree

8 files changed

+91
-24
lines changed

8 files changed

+91
-24
lines changed

CHANGELOG.md

+59-1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,63 @@ Swift 5.2
5858
(foo as Magic)(5)
5959
```
6060

61+
* [SR-11298][]:
62+
63+
A class-constrained protocol extension, where the extended protocol does
64+
not impose a class constraint, will now infer the constraint implicitly.
65+
66+
```swift
67+
protocol Foo {}
68+
class Bar: Foo {
69+
var someProperty: Int = 0
70+
}
71+
72+
// Even though 'Foo' does not impose a class constraint, it is automatically
73+
// inferred due to the Self: Bar constraint.
74+
extension Foo where Self: Bar {
75+
var anotherProperty: Int {
76+
get { return someProperty }
77+
// As a result, the setter is now implicitly nonmutating, just like it would
78+
// be if 'Foo' had a class constraint.
79+
set { someProperty = newValue }
80+
}
81+
}
82+
```
83+
84+
As a result, this could lead to code that currently compiles today to throw an error.
85+
86+
```swift
87+
protocol Foo {
88+
var someProperty: Int { get set }
89+
}
90+
91+
class Bar: Foo {
92+
var someProperty = 0
93+
}
94+
95+
extension Foo where Self: Bar {
96+
var anotherProperty1: Int {
97+
get { return someProperty }
98+
// This will now error, because the protocol requirement
99+
// is implicitly mutating and the setter is implicitly
100+
// nonmutating.
101+
set { someProperty = newValue } // Error
102+
}
103+
}
104+
```
105+
106+
**Workaround**: Define a new mutable variable inside the setter that has a reference to `self`:
107+
108+
```swift
109+
var anotherProperty1: Int {
110+
get { return someProperty }
111+
set {
112+
var mutableSelf = self
113+
mutableSelf.someProperty = newValue // Okay
114+
}
115+
}
116+
```
117+
61118
* [SE-0253][]:
62119

63120
Values of types that declare `func callAsFunction` methods can be called
@@ -83,7 +140,7 @@ Swift 5.2
83140

84141
* [SR-4206][]:
85142

86-
A method override is no longer allowed to have a generic signature with
143+
A method override is no longer allowed to have a generic signature with
87144
requirements not imposed by the base method. For example:
88145

89146
```
@@ -7799,4 +7856,5 @@ Swift 1.0
77997856
[SR-8974]: <https://bugs.swift.org/browse/SR-8974>
78007857
[SR-9043]: <https://bugs.swift.org/browse/SR-9043>
78017858
[SR-9827]: <https://bugs.swift.org/browse/SR-9827>
7859+
[SR-11298]: <https://bugs.swift.org/browse/SR-11298>
78027860
[SR-11429]: <https://bugs.swift.org/browse/SR-11429>

include/swift/AST/DeclContext.h

+4-1
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

+7-1
Original file line numberDiff line numberDiff line change
@@ -5628,10 +5628,16 @@ void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC) const {
56285628
if (FD && !FD->isMutating() && !FD->isImplicit() && FD->isInstanceMember()&&
56295629
!FD->getDeclContext()->getDeclaredInterfaceType()
56305630
->hasReferenceSemantics()) {
5631-
// Do not suggest the fix it in implicit getters
5631+
// Do not suggest the fix-it in implicit getters
56325632
if (auto AD = dyn_cast<AccessorDecl>(FD)) {
56335633
if (AD->isGetter() && !AD->getAccessorKeywordLoc().isValid())
56345634
return;
5635+
5636+
auto accessorDC = AD->getDeclContext();
5637+
// Do not suggest the fix-it if `Self` is a class type.
5638+
if (accessorDC->isTypeContext() && !accessorDC->hasValueSemantics()) {
5639+
return;
5640+
}
56355641
}
56365642

56375643
auto &d = getASTContext().Diags;

lib/AST/DeclContext.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,14 @@ DeclContextKind DeclContext::getContextKind() const {
10351035
llvm_unreachable("Unhandled DeclContext ASTHierarchy");
10361036
}
10371037

1038+
bool DeclContext::hasValueSemantics() const {
1039+
if (auto contextTy = getSelfTypeInContext()) {
1040+
return !contextTy->hasReferenceSemantics();
1041+
}
1042+
1043+
return false;
1044+
}
1045+
10381046
SourceLoc swift::extractNearestSourceLoc(const DeclContext *dc) {
10391047
switch (dc->getContextKind()) {
10401048
case DeclContextKind::AbstractFunctionDecl:

lib/Sema/TypeCheckDecl.cpp

+2-10
Original file line numberDiff line numberDiff line change
@@ -2087,17 +2087,10 @@ OperatorPrecedenceGroupRequest::evaluate(Evaluator &evaluator,
20872087
return group;
20882088
}
20892089

2090-
bool swift::doesContextHaveValueSemantics(DeclContext *dc) {
2091-
if (Type contextTy = dc->getDeclaredInterfaceType())
2092-
return !contextTy->hasReferenceSemantics();
2093-
return false;
2094-
}
2095-
20962090
llvm::Expected<SelfAccessKind>
20972091
SelfAccessKindRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
20982092
if (FD->getAttrs().getAttribute<MutatingAttr>(true)) {
2099-
if (!FD->isInstanceMember() ||
2100-
!doesContextHaveValueSemantics(FD->getDeclContext())) {
2093+
if (!FD->isInstanceMember() || !FD->getDeclContext()->hasValueSemantics()) {
21012094
return SelfAccessKind::NonMutating;
21022095
}
21032096
return SelfAccessKind::Mutating;
@@ -2119,8 +2112,7 @@ SelfAccessKindRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
21192112
case AccessorKind::MutableAddress:
21202113
case AccessorKind::Set:
21212114
case AccessorKind::Modify:
2122-
if (AD->isInstanceMember() &&
2123-
doesContextHaveValueSemantics(AD->getDeclContext()))
2115+
if (AD->isInstanceMember() && AD->getDeclContext()->hasValueSemantics())
21242116
return SelfAccessKind::Mutating;
21252117
break;
21262118

lib/Sema/TypeCheckDecl.h

-2
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

+7-5
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,9 @@ void swift::validatePatternBindingEntries(TypeChecker &tc,
270270
llvm::Expected<bool>
271271
IsGetterMutatingRequest::evaluate(Evaluator &evaluator,
272272
AbstractStorageDecl *storage) const {
273-
bool result = (!storage->isStatic() &&
274-
doesContextHaveValueSemantics(storage->getDeclContext()));
273+
auto storageDC = storage->getDeclContext();
274+
bool result = (!storage->isStatic() && storageDC->isTypeContext() &&
275+
storageDC->hasValueSemantics());
275276

276277
// 'lazy' overrides the normal accessor-based rules and heavily
277278
// restricts what accessors can be used. The getter is considered
@@ -299,7 +300,7 @@ IsGetterMutatingRequest::evaluate(Evaluator &evaluator,
299300

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

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

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

test/decl/ext/extensions.swift

+4-4
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)