Skip to content

Commit ac43753

Browse files
committed
prevent serialization of allocating inits for actors
A public designated initializer of a class would have its allocating entry-point serialized in the module, meaning with `-O` that entry-point can get inlined into programs linking against that module. Once that entry-point is inlined, the program will _require_ that it remain non-delegating, because it will depend on the 2nd entry-point (for actual initializing) to be in the library. As a result of this change, public initializers of an actor should be resilient in a library, whether their underlying implementation is delegating or not.
1 parent 73bc4b6 commit ac43753

File tree

3 files changed

+65
-33
lines changed

3 files changed

+65
-33
lines changed

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -669,13 +669,14 @@ IsSerialized_t SILDeclRef::isSerialized() const {
669669
return IsSerialized;
670670

671671
// The allocating entry point for designated initializers are serialized
672-
// if the class is @usableFromInline or public.
672+
// if the class is @usableFromInline or public. Actors are excluded because
673+
// whether the init is designated is not clearly reflected in the source code.
673674
if (kind == SILDeclRef::Kind::Allocator) {
674675
auto *ctor = cast<ConstructorDecl>(d);
675-
if (ctor->isDesignatedInit() &&
676-
ctor->getDeclContext()->getSelfClassDecl()) {
677-
if (!ctor->hasClangNode())
678-
return IsSerialized;
676+
if (auto classDecl = ctor->getDeclContext()->getSelfClassDecl()) {
677+
if (!classDecl->isAnyActor() && ctor->isDesignatedInit())
678+
if (!ctor->hasClangNode())
679+
return IsSerialized;
679680
}
680681
}
681682

lib/Sema/TypeCheckDecl.cpp

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,37 @@ InitKindRequest::evaluate(Evaluator &evaluator, ConstructorDecl *decl) const {
421421

422422
// if there is no `convenience` keyword...
423423

424-
// actors infer whether they are `convenience` from their body kind.
425-
if (auto classDecl = dyn_cast<ClassDecl>(nominal)) {
424+
if (auto classDcl = dyn_cast<ClassDecl>(nominal)) {
425+
// A designated init for a class must be written within the class itself.
426+
//
427+
// This is because designated initializers of classes get a vtable entry,
428+
// and extensions cannot add vtable entries to the extended type.
429+
//
430+
// If we implement the ability for extensions defined in the same module
431+
// (or the same file) to add vtable entries, we can re-evaluate this
432+
// restriction.
433+
if (!decl->isSynthesized() &&
434+
isa<ExtensionDecl>(decl->getDeclContext()) &&
435+
!(decl->getAttrs().hasAttribute<DynamicReplacementAttr>())) {
436+
437+
if (classDcl->getForeignClassKind() == ClassDecl::ForeignKind::CFType) {
438+
diags.diagnose(decl->getLoc(),
439+
diag::cfclass_designated_init_in_extension,
440+
nominal->getName());
441+
return CtorInitializerKind::Designated;
442+
} else {
443+
auto diag = diags.diagnose(decl->getLoc(),
444+
diag::designated_init_in_extension,
445+
nominal->getName());
446+
447+
if (!classDcl->isAnyActor())
448+
diag.fixItInsert(decl->getLoc(), "convenience ");
449+
450+
return CtorInitializerKind::Convenience;
451+
}
452+
}
453+
454+
// actors infer whether they are `convenience` from their body kind.
426455
if (classDecl->isAnyActor()) {
427456
auto kind = decl->getDelegatingOrChainedInitKind();
428457
switch (kind.initKind) {
@@ -435,32 +464,8 @@ InitKindRequest::evaluate(Evaluator &evaluator, ConstructorDecl *decl) const {
435464
return CtorInitializerKind::Convenience;
436465
}
437466
}
438-
}
439467

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 ");
461-
return CtorInitializerKind::Convenience;
462-
}
463-
}
468+
} // end of ClassDecl context
464469
} // end of Nominal context
465470

466471
// initializers in protocol extensions must be convenience inits

test/SILGen/inlinable_attribute.swift

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-emit-silgen -module-name inlinable_attribute -emit-verbose-sil -warnings-as-errors %s | %FileCheck %s
1+
// RUN: %target-swift-emit-silgen -module-name inlinable_attribute -emit-verbose-sil -warnings-as-errors -disable-availability-checking %s | %FileCheck %s
22

33
// CHECK-LABEL: sil [serialized] [ossa] @$s19inlinable_attribute15fragileFunctionyyF : $@convention(thin) () -> ()
44
@inlinable public func fragileFunction() {
@@ -37,6 +37,32 @@ public class MyCls {
3737
}
3838
}
3939

40+
public actor MyAct {
41+
// CHECK-LABEL: sil [serialized] [ossa] @$s19inlinable_attribute5MyActCfD : $@convention(method) (@owned MyAct) -> ()
42+
@inlinable deinit {}
43+
44+
/// whether delegating or not, the initializers for an actor are not serialized unless marked inlinable.
45+
46+
// CHECK-LABEL: sil [exact_self_class] [ossa] @$s19inlinable_attribute5MyActC14designatedInitACyt_tcfC : $@convention(method) (@thick MyAct.Type) -> @owned MyAct
47+
// CHECK-LABEL: sil [ossa] @$s19inlinable_attribute5MyActC14designatedInitACyt_tcfc : $@convention(method) (@owned MyAct) -> @owned MyAct
48+
public init(designatedInit: ()) {}
49+
50+
// CHECK-LABEL: sil [ossa] @$s19inlinable_attribute5MyActC15convenienceInitACyt_tcfC : $@convention(method) (@thick MyAct.Type) -> @owned MyAct
51+
public init(convenienceInit: ()) {
52+
self.init(designatedInit: ())
53+
}
54+
55+
56+
// CHECK-LABEL: sil [serialized] [exact_self_class] [ossa] @$s19inlinable_attribute5MyActC0A14DesignatedInitACyt_tcfC : $@convention(method) (@thick MyAct.Type) -> @owned MyAct
57+
// CHECK-LABEL: sil [serialized] [ossa] @$s19inlinable_attribute5MyActC0A14DesignatedInitACyt_tcfc : $@convention(method) (@owned MyAct) -> @owned MyAct
58+
@inlinable public init(inlinableDesignatedInit: ()) {}
59+
60+
// CHECK-LABEL: sil [serialized] [ossa] @$s19inlinable_attribute5MyActC0A15ConvenienceInitACyt_tcfC : $@convention(method) (@thick MyAct.Type) -> @owned MyAct
61+
@inlinable public init(inlinableConvenienceInit: ()) {
62+
self.init(designatedInit: ())
63+
}
64+
}
65+
4066
// Make sure enum case constructors for public and versioned enums are
4167
// [serialized].
4268
@usableFromInline enum MyEnum {

0 commit comments

Comments
 (0)