Skip to content

Commit e1d1aee

Browse files
committed
prevent extensions from having designated inits for actors
When I began removing `convenience` from actor inits, I thought it would be ok for an actor's extensions to be non-delegating. But that's wrong, because the actor could be in a resilient module and still have its properties change between stored and computed, meaning that there isn't a practical way to allow this, unless if the extension and the actor are in the same file. For now, just re-ban this behavior before anybody notices :)
1 parent 2cbc0c8 commit e1d1aee

File tree

4 files changed

+83
-29
lines changed

4 files changed

+83
-29
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3603,7 +3603,7 @@ ERROR(designated_init_in_extension,none,
36033603
"designated initializer cannot be declared in an extension of %0; "
36043604
"did you mean this to be a convenience initializer?",
36053605
(DeclName))
3606-
ERROR(cfclass_designated_init_in_extension,none,
3606+
ERROR(designated_init_in_extension_no_convenience_tip,none,
36073607
"designated initializer cannot be declared in an extension of %0",
36083608
(DeclName))
36093609
ERROR(no_convenience_keyword_init,none,

lib/Sema/TypeCheckDecl.cpp

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -419,48 +419,60 @@ InitKindRequest::evaluate(Evaluator &evaluator, ConstructorDecl *decl) const {
419419
return CtorInitializerKind::Convenience;
420420
}
421421

422-
// if there is no `convenience` keyword...
422+
// if there's no `convenience` attribute...
423423

424-
// actors infer whether they are `convenience` from their body kind.
425-
if (auto classDecl = dyn_cast<ClassDecl>(nominal)) {
426-
if (classDecl->isAnyActor()) {
424+
if (auto classDcl = dyn_cast<ClassDecl>(nominal)) {
425+
426+
// actors infer whether they are `convenience` from their body kind.
427+
if (classDcl->isAnyActor()) {
427428
auto kind = decl->getDelegatingOrChainedInitKind();
428429
switch (kind.initKind) {
429430
case BodyInitKind::ImplicitChained:
430431
case BodyInitKind::Chained:
431432
case BodyInitKind::None:
432-
return CtorInitializerKind::Designated;
433+
break; // it's designated, we need more checks.
433434

434435
case BodyInitKind::Delegating:
435436
return CtorInitializerKind::Convenience;
436437
}
437438
}
438-
}
439439

440-
// A designated init for a class must be written within the class itself.
441-
//
442-
// This is because designated initializers of classes get a vtable entry,
443-
// and extensions cannot add vtable entries to the extended type.
444-
//
445-
// If we implement the ability for extensions defined in the same module
446-
// (or the same file) to add vtable entries, we can re-evaluate this
447-
// restriction.
448-
if (isa<ClassDecl>(nominal) && !decl->isSynthesized() &&
449-
isa<ExtensionDecl>(decl->getDeclContext()) &&
450-
!(decl->getAttrs().hasAttribute<DynamicReplacementAttr>())) {
451-
if (cast<ClassDecl>(nominal)->getForeignClassKind() == ClassDecl::ForeignKind::CFType) {
452-
diags.diagnose(decl->getLoc(),
453-
diag::cfclass_designated_init_in_extension,
454-
nominal->getName());
455-
return CtorInitializerKind::Designated;
456-
} else {
457-
diags.diagnose(decl->getLoc(),
458-
diag::designated_init_in_extension,
459-
nominal->getName())
460-
.fixItInsert(decl->getLoc(), "convenience ");
440+
// A designated init for a class must be written within the class itself.
441+
//
442+
// This is because designated initializers of classes get a vtable entry,
443+
// and extensions cannot add vtable entries to the extended type.
444+
//
445+
// If we implement the ability for extensions defined in the same module
446+
// (or the same file) to add vtable entries, we can re-evaluate this
447+
// restriction.
448+
if (!decl->isSynthesized() &&
449+
isa<ExtensionDecl>(decl->getDeclContext()) &&
450+
!(decl->getAttrs().hasAttribute<DynamicReplacementAttr>())) {
451+
452+
if (classDcl->getForeignClassKind() == ClassDecl::ForeignKind::CFType) {
453+
diags.diagnose(decl->getLoc(),
454+
diag::designated_init_in_extension_no_convenience_tip,
455+
nominal->getName());
456+
457+
// despite having reported it as an error, say that it is designated.
458+
return CtorInitializerKind::Designated;
459+
460+
} else if (classDcl->isAnyActor()) {
461+
// tailor the diagnostic to not mention `convenience`
462+
diags.diagnose(decl->getLoc(),
463+
diag::designated_init_in_extension_no_convenience_tip,
464+
nominal->getName());
465+
466+
} else {
467+
diags.diagnose(decl->getLoc(),
468+
diag::designated_init_in_extension,
469+
nominal->getName())
470+
.fixItInsert(decl->getLoc(), "convenience ");
471+
}
472+
461473
return CtorInitializerKind::Convenience;
462474
}
463-
}
475+
} // end of Class context
464476
} // end of Nominal context
465477

466478
// initializers in protocol extensions must be convenience inits

test/decl/class/actor/basic.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,10 @@ extension A2 {
4646

4747
class subscript(i: Int) -> Int { i } // expected-error{{class subscripts are only allowed within classes; use 'static' to declare a static subscript}}
4848
static subscript(s: String) -> String { s }
49+
50+
init(delegates: ()) {
51+
self.init()
52+
}
53+
54+
init(doesNotDelegate: ()) {} // expected-error {{designated initializer cannot be declared in an extension of 'A2'}}
4955
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %target-swift-frontend -swift-version 5 -disable-availability-checking -emit-sil -verify %s
2+
3+
// check the initializer kinds for protocols and their extensions
4+
5+
protocol GoodActor {
6+
init()
7+
init(with: Int)
8+
}
9+
10+
extension GoodActor {
11+
init() {
12+
self.init(with: 0)
13+
}
14+
15+
init(with: Int) {} // expected-error {{'self.init' isn't called on all paths before returning from initializer}}
16+
}
17+
18+
actor Myself: GoodActor {
19+
var x: Int
20+
21+
init(with val: Int) {
22+
self.x = val
23+
}
24+
}
25+
26+
actor SomebodyElse: GoodActor {
27+
var x: Int
28+
29+
init(with val: Int) {
30+
self.x = val
31+
}
32+
33+
init() {
34+
self.x = 0
35+
}
36+
}

0 commit comments

Comments
 (0)