Skip to content

Commit 9ec2077

Browse files
committed
[SE-0306] Make actors semantically "final".
Treat actors as being semantically `final` throughout the type checker. This allows, for example, a non-`required` initializer to satisfy a protocol requirement. We're leaving the ABI open for actor inheritance should we need it. Addresses rdar://78269551.
1 parent 6801eba commit 9ec2077

15 files changed

+71
-24
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2374,6 +2374,10 @@ class ValueDecl : public Decl {
23742374
/// Note whether this declaration is known to be exposed to Objective-C.
23752375
void setIsObjC(bool Value);
23762376

2377+
/// Is this declaration semantically 'final', meaning that the type checker
2378+
/// should treat it as final even if the ABI does not?
2379+
bool isSemanticallyFinal() const;
2380+
23772381
/// Is this declaration 'final'?
23782382
bool isFinal() const;
23792383

lib/AST/ASTContext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2828,7 +2828,7 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD,
28282828
if (Ctx.isSwiftVersionAtLeast(5)) {
28292829
if (wantDynamicSelf && CD->isConvenienceInit())
28302830
if (auto *classDecl = selfTy->getClassOrBoundGenericClass())
2831-
if (!classDecl->isFinal())
2831+
if (!classDecl->isSemanticallyFinal())
28322832
isDynamicSelf = true;
28332833
}
28342834
} else if (isa<DestructorDecl>(AFD)) {

lib/AST/Decl.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2868,6 +2868,23 @@ void ValueDecl::setIsObjC(bool value) {
28682868
LazySemanticInfo.isObjC = value;
28692869
}
28702870

2871+
bool ValueDecl::isSemanticallyFinal() const {
2872+
// Actor types are semantically final.
2873+
if (auto classDecl = dyn_cast<ClassDecl>(this)) {
2874+
if (classDecl->isActor())
2875+
return true;
2876+
}
2877+
2878+
// As are members of actor types.
2879+
if (auto classDecl = getDeclContext()->getSelfClassDecl()) {
2880+
if (classDecl->isActor())
2881+
return true;
2882+
}
2883+
2884+
// For everything else, the same as 'final'.
2885+
return isFinal();
2886+
}
2887+
28712888
bool ValueDecl::isFinal() const {
28722889
return evaluateOrDefault(getASTContext().evaluator,
28732890
IsFinalRequest { const_cast<ValueDecl *>(this) },
@@ -3212,13 +3229,13 @@ bool ValueDecl::shouldHideFromEditor() const {
32123229
static AccessLevel getMaximallyOpenAccessFor(const ValueDecl *decl) {
32133230
// Non-final classes are considered open to @testable importers.
32143231
if (auto cls = dyn_cast<ClassDecl>(decl)) {
3215-
if (!cls->isFinal())
3232+
if (!cls->isSemanticallyFinal())
32163233
return AccessLevel::Open;
32173234

32183235
// Non-final overridable class members are considered open to
32193236
// @testable importers.
32203237
} else if (decl->isPotentiallyOverridable()) {
3221-
if (!cast<ValueDecl>(decl)->isFinal())
3238+
if (!cast<ValueDecl>(decl)->isSemanticallyFinal())
32223239
return AccessLevel::Open;
32233240
}
32243241

lib/IDE/CodeCompletion.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5422,7 +5422,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
54225422
switch (Reason) {
54235423
case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal:
54245424
case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal:
5425-
if (!C->isFinal())
5425+
if (!C->isSemanticallyFinal())
54265426
needRequired = true;
54275427
break;
54285428
case DeclVisibilityKind::MemberOfSuper:
@@ -5458,7 +5458,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
54585458
if (D->shouldHideFromEditor())
54595459
return;
54605460

5461-
if (D->isFinal())
5461+
if (D->isSemanticallyFinal())
54625462
return;
54635463

54645464
bool hasIntroducer = hasFuncIntroducer ||

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7720,7 +7720,7 @@ static bool isNonFinalClass(Type type) {
77207720
type = dynamicSelf->getSelfType();
77217721

77227722
if (auto classDecl = type->getClassOrBoundGenericClass())
7723-
return !classDecl->isFinal();
7723+
return !classDecl->isSemanticallyFinal();
77247724

77257725
if (auto archetype = type->getAs<ArchetypeType>())
77267726
if (auto super = archetype->getSuperclass())

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1789,7 +1789,7 @@ static ValueDecl *deriveDecodable_init(DerivedConformance &derived) {
17891789
}
17901790

17911791
// This constructor should be marked as `required` for non-final classes.
1792-
if (classDecl && !classDecl->isFinal()) {
1792+
if (classDecl && !classDecl->isSemanticallyFinal()) {
17931793
auto *reqAttr = new (C) RequiredAttr(/*IsImplicit=*/true);
17941794
initDecl->getAttrs().add(reqAttr);
17951795
}

lib/Sema/DerivedConformances.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ bool DerivedConformance::checkAndDiagnoseDisallowedContext(
545545
// A non-final class can't have an protocol-witnesss initializer in an
546546
// extension.
547547
if (auto CD = dyn_cast<ClassDecl>(Nominal)) {
548-
if (!CD->isFinal() && isa<ConstructorDecl>(synthesizing) &&
548+
if (!CD->isSemanticallyFinal() && isa<ConstructorDecl>(synthesizing) &&
549549
isa<ExtensionDecl>(ConformanceDecl)) {
550550
ConformanceDecl->diagnose(
551551
diag::cannot_synthesize_init_in_extension_of_nonfinal,

lib/Sema/TypeCheckAttr.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4489,7 +4489,7 @@ IndexSubset *DifferentiableAttributeTypeCheckRequest::evaluate(
44894489
if (diagnoseDynamicSelfResult) {
44904490
// Diagnose class initializers in non-final classes.
44914491
if (isa<ConstructorDecl>(original)) {
4492-
if (!classDecl->isFinal()) {
4492+
if (!classDecl->isSemanticallyFinal()) {
44934493
diags.diagnose(
44944494
attr->getLocation(),
44954495
diag::differentiable_attr_nonfinal_class_init_unsupported,
@@ -4790,7 +4790,7 @@ static bool typeCheckDerivativeAttr(ASTContext &Ctx, Decl *D,
47904790
if (diagnoseDynamicSelfResult) {
47914791
// Diagnose class initializers in non-final classes.
47924792
if (isa<ConstructorDecl>(originalAFD)) {
4793-
if (!classDecl->isFinal()) {
4793+
if (!classDecl->isSemanticallyFinal()) {
47944794
diags.diagnose(attr->getLocation(),
47954795
diag::derivative_attr_nonfinal_class_init_unsupported,
47964796
classDecl->getDeclaredInterfaceType());

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3545,7 +3545,7 @@ bool swift::checkSendableConformance(
35453545

35463546
if (classDecl) {
35473547
// An non-final class cannot conform to `Sendable`.
3548-
if (!classDecl->isFinal()) {
3548+
if (!classDecl->isSemanticallyFinal()) {
35493549
classDecl->diagnose(diag::concurrent_value_nonfinal_class,
35503550
classDecl->getName())
35513551
.limitBehavior(behavior);

lib/Sema/TypeCheckDecl.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
871871
return true;
872872

873873
// The presence of 'final' blocks the inference of 'dynamic'.
874-
if (decl->isFinal())
874+
if (decl->isSemanticallyFinal())
875875
return false;
876876

877877
// Types are never 'dynamic'.
@@ -1868,7 +1868,7 @@ FunctionOperatorRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
18681868
if (dc->isTypeContext()) {
18691869
if (auto classDecl = dc->getSelfClassDecl()) {
18701870
// For a class, we also need the function or class to be 'final'.
1871-
if (!classDecl->isFinal() && !FD->isFinal() &&
1871+
if (!classDecl->isSemanticallyFinal() && !FD->isFinal() &&
18721872
FD->getStaticLoc().isValid() &&
18731873
FD->getStaticSpelling() != StaticSpellingKind::KeywordStatic) {
18741874
FD->diagnose(diag::nonfinal_operator_in_class,

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ static void checkOverrideAccessControl(ValueDecl *baseDecl, ValueDecl *decl,
10281028
} else if (baseHasOpenAccess &&
10291029
classDecl->hasOpenAccess(dc) &&
10301030
decl->getFormalAccess() < AccessLevel::Public &&
1031-
!decl->isFinal()) {
1031+
!decl->isSemanticallyFinal()) {
10321032
{
10331033
auto diag = diags.diagnose(decl, diag::override_not_accessible,
10341034
/*setter*/false,
@@ -1150,7 +1150,7 @@ bool OverrideMatcher::checkOverride(ValueDecl *baseDecl,
11501150
if (decl->getASTContext().isSwiftVersionAtLeast(5) &&
11511151
baseDecl->getInterfaceType()->hasDynamicSelfType() &&
11521152
!decl->getInterfaceType()->hasDynamicSelfType() &&
1153-
!classDecl->isFinal()) {
1153+
!classDecl->isSemanticallyFinal()) {
11541154
diags.diagnose(decl, diag::override_dynamic_self_mismatch);
11551155
diags.diagnose(baseDecl, diag::overridden_here);
11561156
}
@@ -1942,7 +1942,7 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
19421942
}
19431943

19441944
// The overridden declaration cannot be 'final'.
1945-
if (base->isFinal() && !isAccessor) {
1945+
if (base->isSemanticallyFinal() && !isAccessor) {
19461946
// Use a special diagnostic for overriding an actor's unownedExecutor
19471947
// method. TODO: only if it's implicit? But then we need to
19481948
// propagate implicitness in module interfaces.

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -947,7 +947,7 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
947947
ClassDecl *covariantSelf = nullptr;
948948
if (witness->getDeclContext()->getExtendedProtocolDecl()) {
949949
if (auto *classDecl = dc->getSelfClassDecl()) {
950-
if (!classDecl->isFinal()) {
950+
if (!classDecl->isSemanticallyFinal()) {
951951
// If the requirement's type does not involve any associated types,
952952
// we use a class-constrained generic parameter as the 'Self' type
953953
// in the witness thunk.
@@ -3238,7 +3238,7 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
32383238
Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS) {
32393239
if (isa<ConstructorDecl>(Requirement)) {
32403240
if (auto CD = Adopter->getSelfClassDecl()) {
3241-
if (!CD->isFinal() && isa<ExtensionDecl>(Adopter)) {
3241+
if (!CD->isSemanticallyFinal() && isa<ExtensionDecl>(Adopter)) {
32423242
// In this case, user should mark class as 'final' or define
32433243
// 'required' initializer directly in the class definition.
32443244
return false;
@@ -4180,7 +4180,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
41804180
}
41814181

41824182
if (auto *classDecl = Adoptee->getClassOrBoundGenericClass()) {
4183-
if (!classDecl->isFinal()) {
4183+
if (!classDecl->isSemanticallyFinal()) {
41844184
checkNonFinalClassWitness(requirement, witness);
41854185
}
41864186
}
@@ -5201,7 +5201,7 @@ TypeChecker::couldDynamicallyConformToProtocol(Type type, ProtocolDecl *Proto,
52015201

52025202
// A non-final class might have a subclass that conforms to the protocol.
52035203
if (auto *classDecl = type->getClassOrBoundGenericClass()) {
5204-
if (!classDecl->isFinal())
5204+
if (!classDecl->isSemanticallyFinal())
52055205
return true;
52065206
}
52075207

test/Concurrency/global_actor_inference.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,10 @@ actor GenericSuper<T> {
175175

176176
actor GenericSub<T> : GenericSuper<[T]> { // expected-error{{actor types do not support inheritance}}
177177
override func method() { } // expected-note {{calls to instance method 'method()' from outside of its actor context are implicitly asynchronous}}
178-
// expected-error@-1{{actor-isolated instance method 'method()' has different actor isolation from global actor 'GenericGlobalActor<[T]>'-isolated overridden declaration}}
178+
// expected-error@-1{{instance method overrides a 'final' instance method}}
179179

180-
@GenericGlobalActor<T> override func method2() { } // expected-error{{global actor 'GenericGlobalActor<T>'-isolated instance method 'method2()' has different actor isolation from global actor 'GenericGlobalActor<[T]>'-isolated overridden declaration}}
181-
nonisolated override func method3() { } // expected-error{{nonisolated instance method 'method3()' has different actor isolation from global actor 'GenericGlobalActor<[T]>'-isolated overridden declaration}}
180+
@GenericGlobalActor<T> override func method2() { } // expected-error{{instance method overrides a 'final' instance method}}
181+
nonisolated override func method3() { } // expected-error{{instance method overrides a 'final' instance method}}
182182

183183
@OtherGlobalActor func testMethod() {
184184
method() // expected-error{{actor-isolated instance method 'method()' can not be referenced from global actor 'OtherGlobalActor'}}

test/ModuleInterface/actor_objc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@
1313
import Foundation
1414

1515
// CHECK-LABEL: @objc @_inheritsConvenienceInitializers public actor SomeActor : ObjectiveC.NSObject {
16-
// CHECK: @objc override dynamic public init()
16+
// CHECK: @objc override public init()
1717
public actor SomeActor: NSObject {
1818
}

test/decl/class/actor/conformance.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,29 @@ actor OtherActor: SyncProtocol {
5353
static func staticMethod() { }
5454
static var staticProperty: Int = 17
5555
}
56+
57+
protocol Initializers {
58+
init()
59+
init(string: String)
60+
init(int: Int) async
61+
}
62+
63+
protocol SelfReqs {
64+
func withBells() async -> Self
65+
}
66+
67+
actor A1: Initializers, SelfReqs {
68+
init() { }
69+
init(string: String) { }
70+
init(int: Int) async { }
71+
72+
func withBells() async -> A1 { self }
73+
}
74+
75+
actor A2: Initializers {
76+
init() { }
77+
init(string: String) { }
78+
init(int: Int) { }
79+
80+
func withBells() async -> A2 { self }
81+
}

0 commit comments

Comments
 (0)