Skip to content

Commit 6d8dd55

Browse files
authored
Merge pull request #59828 from kavon/inconvenienced-actors-continued
Inconvenienced actors, continued
2 parents 8e70e1c + bfa9095 commit 6d8dd55

File tree

6 files changed

+130
-37
lines changed

6 files changed

+130
-37
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3569,7 +3569,7 @@ ERROR(designated_init_in_extension,none,
35693569
"designated initializer cannot be declared in an extension of %0; "
35703570
"did you mean this to be a convenience initializer?",
35713571
(DeclName))
3572-
ERROR(cfclass_designated_init_in_extension,none,
3572+
ERROR(designated_init_in_extension_no_convenience_tip,none,
35733573
"designated initializer cannot be declared in an extension of %0",
35743574
(DeclName))
35753575
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
Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,43 @@
1+
import Foundation
12

23
// This input is useful to ensure the delegation status of
34
// an actor's initializer does not affect ABI stability
4-
public actor BigFoot {
55

6-
public let name: String
6+
public protocol NamedEntity: Actor {
7+
nonisolated var name: String? { get }
8+
}
9+
10+
public actor BigFoot: NamedEntity {
11+
12+
public nonisolated let name: String?
13+
14+
private init(withName name: String) {
15+
self.name = name
16+
}
17+
18+
public init() {
19+
#if DELEGATES
20+
self.init(withName: "Sasquatch")
21+
#else
22+
self.name = nil
23+
#endif
24+
}
25+
}
26+
27+
28+
@objc public actor BigFootObjC: NSObject, NamedEntity {
29+
30+
public nonisolated let name: String?
731

832
private init(withName name: String) {
933
self.name = name
1034
}
1135

12-
public init?() {
36+
@objc public override init() {
1337
#if DELEGATES
1438
self.init(withName: "Sasquatch")
1539
#else
16-
return nil
40+
self.name = nil
1741
#endif
1842
}
1943
}

test/Concurrency/Runtime/actor_init_abi.swift

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,23 +47,38 @@
4747

4848
// REQUIRES: executable_test
4949
// REQUIRES: concurrency
50+
// REQUIRES: objc_interop
5051

5152
// rdar://76038845
5253
// REQUIRES: concurrency_runtime
5354
// UNSUPPORTED: back_deployment_runtime
5455

5556
import MysteryInit
5657

57-
@main
58-
struct Main {
59-
static func main() {
60-
switch BigFoot() {
58+
// NOTE: the number of myth/real checks in this function (in either mode) should
59+
// match the number of times `test` is called. The -NOT check is there to catch
60+
// any mistakes in updating the test.
61+
func test(_ bigFoot: any NamedEntity) {
62+
switch bigFoot.name {
6163
case .none:
6264
print("bigfoot is myth")
6365
// CHECK-NO-DELEGATES: bigfoot is myth
66+
// CHECK-NO-DELEGATES: bigfoot is myth
67+
68+
// CHECK-NO-DELEGATES-NOT: bigfoot
6469
default:
6570
print("bigfoot is real")
6671
// CHECK-DELEGATES: bigfoot is real
72+
// CHECK-DELEGATES: bigfoot is real
73+
74+
// CHECK-DELEGATES-NOT: bigfoot
6775
}
76+
}
77+
78+
@main
79+
struct Main {
80+
static func main() {
81+
test(BigFoot())
82+
test(BigFootObjC())
6883
}
6984
}

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)