Skip to content

Commit c5ae0a8

Browse files
committed
[Concurrency] Diagnose ConcurrentValue restrictions on classes.
One cannot introduce ConcurrentValue conformance on a class that has a (non-NSObject) superclass, nor put a ConcurrentValue conformance on an open class.
1 parent 604ab0d commit c5ae0a8

File tree

4 files changed

+69
-4
lines changed

4 files changed

+69
-4
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4324,6 +4324,13 @@ ERROR(concurrent_value_outside_source_file,none,
43244324
"conformance 'ConcurrentValue' must occur in the same source file as "
43254325
"%0 %1; use 'UnsafeConcurrentValue' for retroactive conformance",
43264326
(DescriptiveDeclKind, DeclName))
4327+
ERROR(concurrent_value_open_class,none,
4328+
"open class %0 cannot conform to `ConcurrentValue`; "
4329+
"use `UnsafeConcurrentValue`", (DeclName))
4330+
ERROR(concurrent_value_inherit,none,
4331+
"`ConcurrentValue` class %1 cannot inherit from another class"
4332+
"%select{| other than 'NSObject'}0",
4333+
(bool, DeclName))
43274334

43284335
ERROR(actorindependent_let,none,
43294336
"'@actorIndependent' is meaningless on 'let' declarations because "

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2305,10 +2305,12 @@ void swift::checkConcurrentValueConformance(ProtocolConformance *conformance) {
23052305
if (!nominal)
23062306
return;
23072307

2308-
// Actors implicitly conform to ConcurrentValue and protect their state.
23092308
auto classDecl = dyn_cast<ClassDecl>(nominal);
2310-
if (classDecl && classDecl->isActor())
2311-
return;
2309+
if (classDecl) {
2310+
// Actors implicitly conform to ConcurrentValue and protect their state.
2311+
if (classDecl->isActor())
2312+
return;
2313+
}
23122314

23132315
// ConcurrentValue can only be used in the same source file.
23142316
auto conformanceDecl = conformanceDC->getAsDecl();
@@ -2320,6 +2322,29 @@ void swift::checkConcurrentValueConformance(ProtocolConformance *conformance) {
23202322
return;
23212323
}
23222324

2325+
if (classDecl) {
2326+
// An open class cannot conform to `ConcurrentValue`.
2327+
if (classDecl->getFormalAccess() == AccessLevel::Open) {
2328+
classDecl->diagnose(
2329+
diag::concurrent_value_open_class, classDecl->getName());
2330+
return;
2331+
}
2332+
2333+
// A 'ConcurrentValue' class cannot inherit from another class, although
2334+
// we allow `NSObject` for Objective-C interoperability.
2335+
if (!isa<InheritedProtocolConformance>(conformance)) {
2336+
if (auto superclassDecl = classDecl->getSuperclassDecl()) {
2337+
if (!superclassDecl->isNSObject()) {
2338+
classDecl->diagnose(
2339+
diag::concurrent_value_inherit,
2340+
nominal->getASTContext().LangOpts.EnableObjCInterop,
2341+
classDecl->getName());
2342+
return;
2343+
}
2344+
}
2345+
}
2346+
}
2347+
23232348
// Stored properties of structs and classes must have
23242349
// ConcurrentValue-conforming types.
23252350
if (isa<StructDecl>(nominal) || classDecl) {

test/Concurrency/concurrent_value_checking.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,20 @@ class C5: UnsafeConcurrentValue {
221221
}
222222

223223
class C6: C5 {
224-
var y: Int = 0 // still okay
224+
var y: Int = 0 // still okay, it's unsafe
225225
}
226226

227+
class C7<T>: ConcurrentValue { }
228+
229+
class C8: C7<Int> { } // okay
230+
231+
open class C9: ConcurrentValue { } // expected-error{{open class 'C9' cannot conform to `ConcurrentValue`; use `UnsafeConcurrentValue`}}
232+
233+
public class C10: ConcurrentValue { }
234+
// expected-note@-1{{superclass is declared here}}
235+
open class C11: C10 { }
236+
// expected-error@-1{{superclass 'C10' of open class must be open}}
237+
// expected-error@-2{{open class 'C11' cannot conform to `ConcurrentValue`; use `UnsafeConcurrentValue`}}
227238

228239
// ----------------------------------------------------------------------
229240
// UnsafeConcurrentValue disabling checking
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -enable-experimental-concurrent-value-checking
2+
3+
// REQUIRES: concurrency
4+
// REQUIRES: objc_interop
5+
6+
import Foundation
7+
8+
class A: NSObject, ConcurrentValue {
9+
let x: Int = 5
10+
}
11+
12+
class B: NSObject, ConcurrentValue {
13+
var x: Int = 5 // expected-error{{stored property 'x' of 'ConcurrentValue'-conforming class 'B' is mutable}}
14+
}
15+
16+
class C { }
17+
18+
class D: NSObject, ConcurrentValue {
19+
let c: C = C() // expected-error{{stored property 'c' of 'ConcurrentValue'-conforming class 'D' has non-concurrent-value type 'C'}}
20+
}
21+
22+

0 commit comments

Comments
 (0)