Skip to content

Implementation of "Restrict cross-module struct initializers to be delegating" #12352

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 8 additions & 11 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -3674,19 +3674,16 @@ NOTE(resilience_decl_declared_here,
NOTE(resilience_decl_declared_here_versioned,
none, "%0 %1 is not '@_versioned' or public", (DescriptiveDeclKind, DeclName))

ERROR(designated_init_in_extension_resilient,none,
"initializer declared in an extension of "
"non-'@_fixed_layout' type %0 must delegate to another initializer", (Type))
ERROR(designated_init_in_cross_module_extension,none,
"initializer for %0 %1 must use \"self.init(...)\" or \"self = ...\" "
"because it is not in module %2",
(DescriptiveDeclKind, Type, Identifier))

ERROR(designated_init_inlineable_resilient,none,
"initializer for non-'@_fixed_layout' type %0 is "
"'%select{@_transparent|@inline(__always)|@_inlineable|%error}1' and must "
"delegate to another initializer", (Type, unsigned))

ERROR(class_designated_init_inlineable_resilient,none,
"initializer for class %0 is "
"'%select{@_transparent|@inline(__always)|@_inlineable|%error}1' and must "
"delegate to another initializer", (Type, unsigned))
"initializer for %select{non-'@_fixed_layout' struct|class}0 %1 is "
"'%select{@_transparent|@inline(__always)|@_inlineable|%error}2' and must "
"delegate to another initializer",
(bool, Type, /*FragileFunctionKind*/unsigned))

ERROR(inlineable_stored_property,
none, "'@_inlineable' attribute cannot be applied to stored properties", ())
Expand Down
69 changes: 39 additions & 30 deletions lib/Sema/ResilienceDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,38 +136,47 @@ bool TypeChecker::diagnoseInlineableDeclRef(SourceLoc loc,
}

void TypeChecker::diagnoseResilientConstructor(ConstructorDecl *ctor) {
auto nominalDecl = ctor->getDeclContext()
->getAsNominalTypeOrNominalTypeExtensionContext();
const DeclContext *containingContext = ctor->getDeclContext();
const NominalTypeDecl *nominalDecl =
containingContext->getAsNominalTypeOrNominalTypeExtensionContext();

// These restrictions only apply to concrete types, and not protocol
// extensions.
if (isa<ProtocolDecl>(nominalDecl))
// These restrictions only apply to structs and classes.
if (isa<ProtocolDecl>(nominalDecl) || isa<EnumDecl>(nominalDecl))
return;

bool isDelegating =
(ctor->getDelegatingOrChainedInitKind(&Diags) ==
ConstructorDecl::BodyInitKind::Delegating);

if (!isDelegating &&
!nominalDecl->hasFixedLayout(ctor->getParentModule(),
ctor->getResilienceExpansion())) {
if (ctor->getResilienceExpansion() == ResilienceExpansion::Minimal) {
// An @_inlineable designated initializer defined in a resilient type
// cannot initialize stored properties directly, and must chain to
// another initializer.
diagnose(ctor->getLoc(),
isa<ClassDecl>(nominalDecl)
? diag::class_designated_init_inlineable_resilient
: diag::designated_init_inlineable_resilient,
nominalDecl->getDeclaredInterfaceType(),
getFragileFunctionKind(ctor));
} else {
// A designated initializer defined on an extension of a resilient
// type from a different resilience domain cannot initialize stored
// properties directly, and must chain to another initializer.
diagnose(ctor->getLoc(),
diag::designated_init_in_extension_resilient,
nominalDecl->getDeclaredInterfaceType());
}
if (ctor->getDelegatingOrChainedInitKind(&Diags) ==
ConstructorDecl::BodyInitKind::Delegating) {
return;
}

// An initializer defined on an extension of a struct from another module
// cannot initialize stored properties directly, and must chain to another
// initializer.
if (isa<ExtensionDecl>(containingContext) &&
nominalDecl->getParentModule() != containingContext->getParentModule()) {
// Classes already can't have designated initializers in extensions, and
// we diagnose this elsewhere.
if (isa<ClassDecl>(nominalDecl))
return;

diagnose(ctor->getLoc(),
diag::designated_init_in_cross_module_extension,
nominalDecl->getDescriptiveKind(),
nominalDecl->getDeclaredInterfaceType(),
nominalDecl->getParentModule()->getName());
return;
}

// Within the module, we're only concerned about inlinable initializers on
// non-fixed-layout types.
if (ctor->getResilienceExpansion() == ResilienceExpansion::Minimal &&
!nominalDecl->hasFixedLayout()) {
// An @_inlineable designated initializer defined in a resilient type
// cannot initialize stored properties directly, and must chain to
// another initializer.
diagnose(ctor->getLoc(), diag::designated_init_inlineable_resilient,
isa<ClassDecl>(nominalDecl),
nominalDecl->getDeclaredInterfaceType(),
getFragileFunctionKind(ctor));
}
}
15 changes: 8 additions & 7 deletions test/decl/init/resilience.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
import resilient_struct
import resilient_protocol

// Point is @_fixed_layout -- this is OK
// Even though Point is @_fixed_layout this is not allowed.
extension Point {
init(xx: Int, yy: Int) {
// expected-error@-1 {{initializer for struct 'Point' must use "self.init(...)" or "self = ..." because it is not in module 'resilient_struct'}}
self.x = xx
self.y = yy
}
Expand All @@ -17,7 +18,7 @@ extension Point {
// Size is not @_fixed_layout, so we cannot define a new designated initializer
extension Size {
init(ww: Int, hh: Int) {
// expected-error@-1 {{initializer declared in an extension of non-'@_fixed_layout' type 'Size' must delegate to another initializer}}
// expected-error@-1 {{initializer for struct 'Size' must use "self.init(...)" or "self = ..." because it is not in module 'resilient_struct'}}
self.w = ww
self.h = hh
}
Expand All @@ -30,7 +31,7 @@ extension Size {
// FIXME: This should be allowed, but Sema doesn't distinguish this
// case from memberwise initialization, and DI explodes the value type
init(other: Size) {
// expected-error@-1 {{initializer declared in an extension of non-'@_fixed_layout' type 'Size' must delegate to another initializer}}
// expected-error@-1 {{initializer for struct 'Size' must use "self.init(...)" or "self = ..." because it is not in module 'resilient_struct'}}
self = other
}
}
Expand All @@ -41,17 +42,17 @@ public struct Animal {
public let name: String

@_inlineable public init(name: String) {
// expected-error@-1 {{initializer for non-'@_fixed_layout' type 'Animal' is '@_inlineable' and must delegate to another initializer}}
// expected-error@-1 {{initializer for non-'@_fixed_layout' struct 'Animal' is '@_inlineable' and must delegate to another initializer}}
self.name = name
}

@inline(__always) public init(dog: String) {
// expected-error@-1 {{initializer for non-'@_fixed_layout' type 'Animal' is '@inline(__always)' and must delegate to another initializer}}
// expected-error@-1 {{initializer for non-'@_fixed_layout' struct 'Animal' is '@inline(__always)' and must delegate to another initializer}}
self.name = dog
}

@_transparent public init(cat: String) {
// expected-error@-1 {{initializer for non-'@_fixed_layout' type 'Animal' is '@_transparent' and must delegate to another initializer}}
// expected-error@-1 {{initializer for non-'@_fixed_layout' struct 'Animal' is '@_transparent' and must delegate to another initializer}}
self.name = cat
}

Expand All @@ -63,7 +64,7 @@ public struct Animal {
// FIXME: This should be allowed, but Sema doesn't distinguish this
// case from memberwise initialization, and DI explodes the value type
@_inlineable public init(other: Animal) {
// expected-error@-1 {{initializer for non-'@_fixed_layout' type 'Animal' is '@_inlineable' and must delegate to another initializer}}
// expected-error@-1 {{initializer for non-'@_fixed_layout' struct 'Animal' is '@_inlineable' and must delegate to another initializer}}
self = other
}
}
Expand Down