Skip to content

Commit 6ca121d

Browse files
committed
[Typechecker] Do not incorrectly mark explicitly mutating methods as non-mutatingif we're in a class-constrained protocol extension
1 parent 70d77e9 commit 6ca121d

File tree

4 files changed

+93
-7
lines changed

4 files changed

+93
-7
lines changed

include/swift/AST/DeclContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@ class alignas(1 << DeclContextAlignInBits) DeclContext {
265265
/// Returns whether this context has value semantics.
266266
bool hasValueSemantics() const;
267267

268+
/// Returns whether this context is an extension constrained to a class type.
269+
bool isClassConstrainedProtocolExtension() const;
270+
268271
/// Determines whether this context is itself a local scope in a
269272
/// code block. A context that appears in such a scope, like a
270273
/// local type declaration, does not itself become a local context.

lib/AST/DeclContext.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,17 @@ bool DeclContext::hasValueSemantics() const {
10431043
return false;
10441044
}
10451045

1046+
bool DeclContext::isClassConstrainedProtocolExtension() const {
1047+
if (auto ED = dyn_cast<ExtensionDecl>(this)) {
1048+
if (ED->getExtendedType()->isExistentialType()) {
1049+
return ED->getGenericSignature()->requiresClass(
1050+
ED->getSelfInterfaceType());
1051+
}
1052+
}
1053+
1054+
return false;
1055+
}
1056+
10461057
SourceLoc swift::extractNearestSourceLoc(const DeclContext *dc) {
10471058
switch (dc->getContextKind()) {
10481059
case DeclContextKind::AbstractFunctionDecl:

lib/Sema/TypeCheckDecl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2091,6 +2091,8 @@ llvm::Expected<SelfAccessKind>
20912091
SelfAccessKindRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
20922092
if (FD->getAttrs().getAttribute<MutatingAttr>(true)) {
20932093
if (!FD->isInstanceMember() || !FD->getDeclContext()->hasValueSemantics()) {
2094+
if (FD->getDeclContext()->isClassConstrainedProtocolExtension())
2095+
return SelfAccessKind::Mutating;
20942096
return SelfAccessKind::NonMutating;
20952097
}
20962098
return SelfAccessKind::Mutating;

test/decl/ext/extensions.swift

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,31 +127,101 @@ struct WrapperContext {
127127

128128
// Class-constrained extension where protocol does not impose class requirement
129129
// SR-11298
130+
130131
protocol DoesNotImposeClassReq_1 {}
131-
132+
132133
class JustAClass: DoesNotImposeClassReq_1 {
133134
var property: String = ""
134135
}
135136

136137
extension DoesNotImposeClassReq_1 where Self: JustAClass {
137-
var wrappingProperty: String {
138+
var wrappingProperty1: String {
138139
get { return property }
139-
set { property = newValue }
140+
set { property = newValue } // Okay
141+
}
142+
143+
var wrappingProperty2: String {
144+
get { return property }
145+
nonmutating set { property = newValue } // Okay
146+
}
147+
148+
var wrappingProperty3: String {
149+
get { return property }
150+
mutating set { property = newValue } // Okay
151+
}
152+
153+
mutating func foo() {
154+
property = "" // Okay
155+
wrappingProperty1 = "" // Okay
156+
wrappingProperty2 = "" // Okay
157+
wrappingProperty3 = "" // Okay
158+
}
159+
160+
func bar() { // expected-note {{mark method 'mutating' to make 'self' mutable}}{{2-2=mutating }}
161+
property = "" // Okay
162+
wrappingProperty1 = "" // Okay
163+
wrappingProperty2 = "" // Okay
164+
wrappingProperty3 = "" // expected-error {{cannot assign to property: 'self' is immutable}}
165+
}
166+
167+
nonmutating func baz() { // expected-note {{mark method 'mutating' to make 'self' mutable}}
168+
property = "" // Okay
169+
wrappingProperty1 = "" // Okay
170+
wrappingProperty2 = "" // Okay
171+
wrappingProperty3 = "" // expected-error {{cannot assign to property: 'self' is immutable}}
140172
}
141173
}
142-
143-
let instanceOfJustAClass = JustAClass()
144-
instanceOfJustAClass.wrappingProperty = "" // Okay
174+
175+
let instanceOfJustAClass1 = JustAClass() // expected-note 2{{change 'let' to 'var' to make it mutable}}
176+
instanceOfJustAClass1.wrappingProperty1 = "" // Okay
177+
instanceOfJustAClass1.wrappingProperty2 = "" // Okay
178+
instanceOfJustAClass1.wrappingProperty3 = "" // expected-error {{cannot assign to property: 'instanceOfJustAClass1' is a 'let' constant}}
179+
instanceOfJustAClass1.foo() // expected-error {{cannot use mutating member on immutable value: 'instanceOfJustAClass1' is a 'let' constant}}
180+
instanceOfJustAClass1.bar() // Okay
181+
182+
var instanceOfJustAClass2 = JustAClass()
183+
instanceOfJustAClass2.foo() // Okay
145184

146185
protocol DoesNotImposeClassReq_2 {
147186
var property: String { get set }
148187
}
149188

150189
extension DoesNotImposeClassReq_2 where Self : AnyObject {
151-
var wrappingProperty: String {
190+
var wrappingProperty1: String {
152191
get { property }
153192
set { property = newValue } // expected-error {{cannot assign to property: 'self' is immutable}}
154193
}
194+
195+
var wrappingProperty2: String {
196+
get { property }
197+
nonmutating set { property = newValue } // expected-error {{cannot assign to property: 'self' is immutable}}
198+
}
199+
200+
var wrappingProperty3: String {
201+
get { property }
202+
mutating set { property = newValue } // Okay
203+
}
204+
205+
mutating func foo() {
206+
property = "" // Okay
207+
wrappingProperty1 = "" // Okay
208+
wrappingProperty2 = "" // Okay
209+
wrappingProperty3 = "" // Okay
210+
}
211+
212+
func bar() { // expected-note 2{{mark method 'mutating' to make 'self' mutable}}{{2-2=mutating }}
213+
property = "" // expected-error {{cannot assign to property: 'self' is immutable}}
214+
wrappingProperty1 = "" // Okay
215+
wrappingProperty2 = "" // Okay
216+
wrappingProperty3 = "" // expected-error {{cannot assign to property: 'self' is immutable}}
217+
}
218+
219+
nonmutating func baz() { // expected-note 2{{mark method 'mutating' to make 'self' mutable}}
220+
property = "" // expected-error {{cannot assign to property: 'self' is immutable}}
221+
wrappingProperty1 = "" // Okay
222+
wrappingProperty2 = "" // Okay
223+
wrappingProperty3 = "" // expected-error {{cannot assign to property: 'self' is immutable}}
224+
}
155225
}
156226

157227
// Reject extension of nominal type via parameterized typealias

0 commit comments

Comments
 (0)