Skip to content

Commit 2317733

Browse files
authored
Merge pull request #28629 from CodaFi/convenience-never-comes-without-cost
[Shepherd] Support classes with missing designated inits
2 parents 8bd3e2d + 4d73173 commit 2317733

33 files changed

+429
-52
lines changed

include/swift/AST/Decl.h

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ class alignas(1 << DeclAlignInBits) Decl {
539539
RawForeignKind : 2,
540540

541541
/// \see ClassDecl::getEmittedMembers()
542-
HasForcedEmittedMembers : 1,
542+
HasForcedEmittedMembers : 1,
543543

544544
HasMissingDesignatedInitializers : 1,
545545
ComputedHasMissingDesignatedInitializers : 1,
@@ -3833,6 +3833,25 @@ class ClassDecl final : public NominalTypeDecl {
38333833
return None;
38343834
}
38353835

3836+
Optional<bool> getCachedHasMissingDesignatedInitializers() const {
3837+
if (!Bits.ClassDecl.ComputedHasMissingDesignatedInitializers) {
3838+
// Force loading all the members, which will add this attribute if any of
3839+
// members are determined to be missing while loading.
3840+
auto mutableThis = const_cast<ClassDecl *>(this);
3841+
(void)mutableThis->lookupDirect(DeclBaseName::createConstructor());
3842+
}
3843+
3844+
if (Bits.ClassDecl.ComputedHasMissingDesignatedInitializers)
3845+
return Bits.ClassDecl.HasMissingDesignatedInitializers;
3846+
3847+
return None;
3848+
}
3849+
3850+
void setHasMissingDesignatedInitializers(bool value) {
3851+
Bits.ClassDecl.HasMissingDesignatedInitializers = value;
3852+
Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = true;
3853+
}
3854+
38363855
/// Marks that this class inherits convenience initializers from its
38373856
/// superclass.
38383857
void setInheritsSuperclassInitializers(bool value) {
@@ -3843,6 +3862,7 @@ class ClassDecl final : public NominalTypeDecl {
38433862
friend class SuperclassDeclRequest;
38443863
friend class SuperclassTypeRequest;
38453864
friend class EmittedMembersRequest;
3865+
friend class HasMissingDesignatedInitializersRequest;
38463866
friend class InheritsSuperclassInitializersRequest;
38473867
friend class TypeChecker;
38483868

@@ -3949,11 +3969,6 @@ class ClassDecl final : public NominalTypeDecl {
39493969
/// initializers that cannot be represented in Swift.
39503970
bool hasMissingDesignatedInitializers() const;
39513971

3952-
void setHasMissingDesignatedInitializers(bool newValue = true) {
3953-
Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = 1;
3954-
Bits.ClassDecl.HasMissingDesignatedInitializers = newValue;
3955-
}
3956-
39573972
/// Returns true if the class has missing members that require vtable entries.
39583973
///
39593974
/// In this case, the class cannot be subclassed, because we cannot construct
@@ -3992,7 +4007,7 @@ class ClassDecl final : public NominalTypeDecl {
39924007

39934008
/// Determine whether this class inherits the convenience initializers
39944009
/// from its superclass.
3995-
bool inheritsSuperclassInitializers();
4010+
bool inheritsSuperclassInitializers() const;
39964011

39974012
/// Walks the class hierarchy starting from this class, checking various
39984013
/// conditions.

include/swift/AST/DiagnosticsModuleDiffer.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ ERROR(objc_name_change,none,"%0 has ObjC name change from %1 to %2", (StringRef,
9898

9999
ERROR(desig_init_added,none,"%0 has been added as a designated initializer to an open class", (StringRef))
100100

101+
ERROR(added_invisible_designated_init,none,"%0 has new designated initializers that are not visible to clients", (StringRef))
102+
103+
ERROR(not_inheriting_convenience_inits,none,"%0 no longer inherits convenience inits from its superclass", (StringRef))
104+
101105
#ifndef DIAG_NO_UNDEF
102106
# if defined(DIAG)
103107
# undef DIAG

include/swift/AST/NameLookupRequests.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,29 @@ class SuperclassDeclRequest :
161161
void cacheResult(ClassDecl *value) const;
162162
};
163163

164+
/// Requests whether or not this class has designated initializers that are
165+
/// not public or @usableFromInline.
166+
class HasMissingDesignatedInitializersRequest :
167+
public SimpleRequest<HasMissingDesignatedInitializersRequest,
168+
bool(ClassDecl *),
169+
CacheKind::SeparatelyCached> {
170+
public:
171+
using SimpleRequest::SimpleRequest;
172+
173+
private:
174+
friend SimpleRequest;
175+
176+
// Evaluation.
177+
llvm::Expected<bool>
178+
evaluate(Evaluator &evaluator, ClassDecl *subject) const;
179+
180+
public:
181+
// Caching
182+
bool isCached() const { return true; }
183+
Optional<bool> getCachedResult() const;
184+
void cacheResult(bool) const;
185+
};
186+
164187
/// Request the nominal declaration extended by a given extension declaration.
165188
class ExtendedNominalRequest :
166189
public SimpleRequest<ExtendedNominalRequest,

include/swift/AST/NameLookupTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ SWIFT_REQUEST(NameLookup, SelfBoundsFromWhereClauseRequest,
5656
Uncached, NoLocationInfo)
5757
SWIFT_REQUEST(NameLookup, SuperclassDeclRequest, ClassDecl *(NominalTypeDecl *),
5858
SeparatelyCached, NoLocationInfo)
59+
SWIFT_REQUEST(NameLookup, HasMissingDesignatedInitializersRequest,
60+
bool(ClassDecl *),
61+
SeparatelyCached, NoLocationInfo)
5962
SWIFT_REQUEST(NameLookup, TypeDeclsFromWhereClauseRequest,
6063
DirectlyReferencedTypeDecls(ExtensionDecl *), Uncached,
6164
NoLocationInfo)

include/swift/IDE/DigesterEnums.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ KEY_BOOL(HasStorage, hasStorage)
132132
KEY_BOOL(ReqNewWitnessTableEntry, reqNewWitnessTableEntry)
133133
KEY_BOOL(IsABIPlaceholder, isABIPlaceholder)
134134
KEY_BOOL(IsExternal, isExternal)
135+
KEY_BOOL(HasMissingDesignatedInitializers, hasMissingDesignatedInitializers)
136+
KEY_BOOL(InheritsConvenienceInitializers, inheritsConvenienceInitializers)
135137

136138
KEY(kind)
137139

lib/AST/ASTPrinter.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,22 @@ void PrintAST::printAttributes(const Decl *D) {
990990
Printer << " ";
991991
}
992992
}
993+
994+
// If the declaration has designated inits that won't be visible to
995+
// clients, or if it inherits superclass convenience initializers,
996+
// then print those attributes specially.
997+
if (auto CD = dyn_cast<ClassDecl>(D)) {
998+
if (Options.PrintImplicitAttrs) {
999+
if (CD->inheritsSuperclassInitializers()) {
1000+
Printer.printAttrName("@_inheritsConvenienceInitializers");
1001+
Printer << " ";
1002+
}
1003+
if (CD->hasMissingDesignatedInitializers()) {
1004+
Printer.printAttrName("@_hasMissingDesignatedInitializers");
1005+
Printer << " ";
1006+
}
1007+
}
1008+
}
9931009
}
9941010

9951011
D->getAttrs().print(Printer, Options, D);

lib/AST/Decl.cpp

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4127,15 +4127,11 @@ GetDestructorRequest::evaluate(Evaluator &evaluator, ClassDecl *CD) const {
41274127
return DD;
41284128
}
41294129

4130-
41314130
bool ClassDecl::hasMissingDesignatedInitializers() const {
4132-
if (!Bits.ClassDecl.ComputedHasMissingDesignatedInitializers) {
4133-
auto *mutableThis = const_cast<ClassDecl *>(this);
4134-
mutableThis->Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = 1;
4135-
(void)mutableThis->lookupDirect(DeclBaseName::createConstructor());
4136-
}
4137-
4138-
return Bits.ClassDecl.HasMissingDesignatedInitializers;
4131+
return evaluateOrDefault(
4132+
getASTContext().evaluator,
4133+
HasMissingDesignatedInitializersRequest{const_cast<ClassDecl *>(this)},
4134+
false);
41394135
}
41404136

41414137
bool ClassDecl::hasMissingVTableEntries() const {
@@ -4158,7 +4154,7 @@ bool ClassDecl::isIncompatibleWithWeakReferences() const {
41584154
return false;
41594155
}
41604156

4161-
bool ClassDecl::inheritsSuperclassInitializers() {
4157+
bool ClassDecl::inheritsSuperclassInitializers() const {
41624158
// If there's no superclass, there's nothing to inherit.
41634159
if (!getSuperclass())
41644160
return false;

lib/AST/NameLookupRequests.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,47 @@ void SuperclassDeclRequest::cacheResult(ClassDecl *value) const {
6767
protocolDecl->LazySemanticInfo.SuperclassDecl.setPointerAndInt(value, true);
6868
}
6969

70+
//----------------------------------------------------------------------------//
71+
// Missing designated initializers computation
72+
//----------------------------------------------------------------------------//
73+
74+
Optional<bool> HasMissingDesignatedInitializersRequest::getCachedResult() const {
75+
auto classDecl = std::get<0>(getStorage());
76+
return classDecl->getCachedHasMissingDesignatedInitializers();
77+
}
78+
79+
void HasMissingDesignatedInitializersRequest::cacheResult(bool result) const {
80+
auto classDecl = std::get<0>(getStorage());
81+
classDecl->setHasMissingDesignatedInitializers(result);
82+
}
83+
84+
llvm::Expected<bool>
85+
HasMissingDesignatedInitializersRequest::evaluate(Evaluator &evaluator,
86+
ClassDecl *subject) const {
87+
// Short-circuit and check for the attribute here.
88+
if (subject->getAttrs().hasAttribute<HasMissingDesignatedInitializersAttr>())
89+
return true;
90+
91+
AccessScope scope =
92+
subject->getFormalAccessScope(/*useDC*/nullptr,
93+
/*treatUsableFromInlineAsPublic*/true);
94+
// This flag only makes sense for public types that will be written in the
95+
// module.
96+
if (!scope.isPublic())
97+
return false;
98+
99+
auto constructors = subject->lookupDirect(DeclBaseName::createConstructor());
100+
return llvm::any_of(constructors, [&](ValueDecl *decl) {
101+
auto init = cast<ConstructorDecl>(decl);
102+
if (!init->isDesignatedInit())
103+
return false;
104+
AccessScope scope =
105+
init->getFormalAccessScope(/*useDC*/nullptr,
106+
/*treatUsableFromInlineAsPublic*/true);
107+
return !scope.isPublic();
108+
});
109+
}
110+
70111
//----------------------------------------------------------------------------//
71112
// Extended nominal computation.
72113
//----------------------------------------------------------------------------//

lib/ClangImporter/ImportDecl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7695,7 +7695,8 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
76957695
if (getClangModuleForDecl(theClass) == getClangModuleForDecl(method)) {
76967696
if (auto swiftClass = castIgnoringCompatibilityAlias<ClassDecl>(
76977697
importDecl(theClass, CurrentVersion))) {
7698-
swiftClass->setHasMissingDesignatedInitializers();
7698+
SwiftContext.evaluator.cacheOutput(
7699+
HasMissingDesignatedInitializersRequest{swiftClass}, true);
76997700
}
77007701
}
77017702
}

lib/Sema/CodeSynthesis.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1002,13 +1002,18 @@ static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) {
10021002
llvm::Expected<bool>
10031003
InheritsSuperclassInitializersRequest::evaluate(Evaluator &eval,
10041004
ClassDecl *decl) const {
1005+
// Check if we parsed the @_inheritsConvenienceInitializers attribute.
1006+
if (decl->getAttrs().hasAttribute<InheritsConvenienceInitializersAttr>())
1007+
return true;
1008+
10051009
auto superclass = decl->getSuperclass();
10061010
assert(superclass);
10071011

10081012
// If the superclass has known-missing designated initializers, inheriting
10091013
// is unsafe.
10101014
auto *superclassDecl = superclass->getClassOrBoundGenericClass();
1011-
if (superclassDecl->hasMissingDesignatedInitializers())
1015+
if (superclassDecl->getModuleContext() != decl->getParentModule() &&
1016+
superclassDecl->hasMissingDesignatedInitializers())
10121017
return false;
10131018

10141019
// If we're allowed to inherit designated initializers, then we can inherit

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@ static void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) {
11031103

11041104
auto *superclassDecl = classDecl->getSuperclassDecl();
11051105
if (superclassDecl &&
1106+
superclassDecl->getModuleContext() != classDecl->getModuleContext() &&
11061107
superclassDecl->hasMissingDesignatedInitializers())
11071108
return;
11081109

lib/Serialization/Deserialization.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3416,6 +3416,7 @@ class DeclDeserializer {
34163416
DeclContextID contextID;
34173417
bool isImplicit, isObjC;
34183418
bool inheritsSuperclassInitializers;
3419+
bool hasMissingDesignatedInits;
34193420
GenericSignatureID genericSigID;
34203421
TypeID superclassID;
34213422
uint8_t rawAccessLevel;
@@ -3424,6 +3425,7 @@ class DeclDeserializer {
34243425
decls_block::ClassLayout::readRecord(scratch, nameID, contextID,
34253426
isImplicit, isObjC,
34263427
inheritsSuperclassInitializers,
3428+
hasMissingDesignatedInits,
34273429
genericSigID, superclassID,
34283430
rawAccessLevel, numConformances,
34293431
numInheritedTypes,
@@ -3466,6 +3468,8 @@ class DeclDeserializer {
34663468
theClass->setSuperclass(MF.getType(superclassID));
34673469
ctx.evaluator.cacheOutput(InheritsSuperclassInitializersRequest{theClass},
34683470
std::move(inheritsSuperclassInitializers));
3471+
ctx.evaluator.cacheOutput(HasMissingDesignatedInitializersRequest{theClass},
3472+
std::move(hasMissingDesignatedInits));
34693473

34703474
handleInherited(theClass,
34713475
rawInheritedAndDependencyIDs.slice(0, numInheritedTypes));
@@ -5390,7 +5394,8 @@ Decl *handleErrorAndSupplyMissingClassMember(ASTContext &context,
53905394
Decl *suppliedMissingMember = nullptr;
53915395
auto handleMissingClassMember = [&](const DeclDeserializationError &error) {
53925396
if (error.isDesignatedInitializer())
5393-
containingClass->setHasMissingDesignatedInitializers();
5397+
context.evaluator.cacheOutput(
5398+
HasMissingDesignatedInitializersRequest{containingClass}, true);
53945399
if (error.getNumberOfVTableEntries() > 0)
53955400
containingClass->setHasMissingVTableEntries();
53965401

lib/Serialization/ModuleFormat.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5555
/// describe what change you made. The content of this comment isn't important;
5656
/// it just ensures a conflict if two people change the module format.
5757
/// Don't worry about adhering to the 80-column limit for this line.
58-
const uint16_t SWIFTMODULE_VERSION_MINOR = 531; // function parameter noDerivative
58+
const uint16_t SWIFTMODULE_VERSION_MINOR = 532; // @_hasMissingDesignatedInitializers
5959

6060
/// A standard hash seed used for all string hashes in a serialized module.
6161
///
@@ -1114,6 +1114,7 @@ namespace decls_block {
11141114
BCFixed<1>, // implicit?
11151115
BCFixed<1>, // explicitly objc?
11161116
BCFixed<1>, // inherits convenience initializers from its superclass?
1117+
BCFixed<1>, // has missing designated initializers?
11171118
GenericSignatureIDField, // generic environment
11181119
TypeIDField, // superclass
11191120
AccessLevelField, // access level

lib/Serialization/Serialization.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3139,17 +3139,16 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
31393139
uint8_t rawAccessLevel =
31403140
getRawStableAccessLevel(theClass->getFormalAccess());
31413141

3142-
bool inheritsSuperclassInitializers =
3143-
const_cast<ClassDecl *>(theClass)->
3144-
inheritsSuperclassInitializers();
3142+
auto mutableClass = const_cast<ClassDecl *>(theClass);
31453143

31463144
unsigned abbrCode = S.DeclTypeAbbrCodes[ClassLayout::Code];
31473145
ClassLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode,
31483146
S.addDeclBaseNameRef(theClass->getName()),
31493147
contextID.getOpaqueValue(),
31503148
theClass->isImplicit(),
31513149
theClass->isObjC(),
3152-
inheritsSuperclassInitializers,
3150+
mutableClass->inheritsSuperclassInitializers(),
3151+
mutableClass->hasMissingDesignatedInitializers(),
31533152
S.addGenericSignatureRef(
31543153
theClass->getGenericSignature()),
31553154
S.addTypeRef(theClass->getSuperclass()),

test/IDE/print_ast_tc_decls.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ class d0120_TestClassBase {
452452
}
453453

454454
class d0121_TestClassDerived : d0120_TestClassBase {
455-
// PASS_COMMON-LABEL: {{^}}class d0121_TestClassDerived : d0120_TestClassBase {{{$}}
455+
// PASS_COMMON-LABEL: {{^}}@_inheritsConvenienceInitializers {{()?}}class d0121_TestClassDerived : d0120_TestClassBase {{{$}}
456456

457457
required init() { super.init() }
458458
// PASS_COMMON-NEXT: {{^}} required init(){{$}}
@@ -611,8 +611,8 @@ struct d0200_EscapedIdentifiers {
611611
// PASS_ONE_LINE_TYPEREPR-DAG: {{^}} typealias `protocol` = `class`{{$}}
612612

613613
class `extension` : `class` {}
614-
// PASS_ONE_LINE_TYPE-DAG: {{^}} class `extension` : d0200_EscapedIdentifiers.`class` {{{$}}
615-
// PASS_ONE_LINE_TYPEREPR-DAG: {{^}} class `extension` : `class` {{{$}}
614+
// PASS_ONE_LINE_TYPE-DAG: {{^}} @_inheritsConvenienceInitializers class `extension` : d0200_EscapedIdentifiers.`class` {{{$}}
615+
// PASS_ONE_LINE_TYPEREPR-DAG: {{^}} @_inheritsConvenienceInitializers class `extension` : `class` {{{$}}
616616
// PASS_COMMON: {{^}} @objc deinit{{$}}
617617
// PASS_COMMON-NEXT: {{^}} {{(override )?}}init(){{$}}
618618
// PASS_COMMON-NEXT: {{^}} }{{$}}
@@ -748,7 +748,7 @@ class d0260_ExplodePattern_TestClassBase {
748748
}
749749

750750
class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase {
751-
// PASS_EXPLODE_PATTERN-LABEL: {{^}}class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase {{{$}}
751+
// PASS_EXPLODE_PATTERN-LABEL: {{^}}@_inheritsConvenienceInitializers class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase {{{$}}
752752

753753
override final var baseProp2: Int {
754754
get {
@@ -791,13 +791,13 @@ class ClassWithInheritance2 : FooProtocol, BarProtocol {}
791791
// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance2 : FooProtocol, BarProtocol {{{$}}
792792

793793
class ClassWithInheritance3 : FooClass {}
794-
// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance3 : FooClass {{{$}}
794+
// PASS_ONE_LINE-DAG: {{^}}@_inheritsConvenienceInitializers class ClassWithInheritance3 : FooClass {{{$}}
795795

796796
class ClassWithInheritance4 : FooClass, FooProtocol {}
797-
// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance4 : FooClass, FooProtocol {{{$}}
797+
// PASS_ONE_LINE-DAG: {{^}}@_inheritsConvenienceInitializers class ClassWithInheritance4 : FooClass, FooProtocol {{{$}}
798798

799799
class ClassWithInheritance5 : FooClass, FooProtocol, BarProtocol {}
800-
// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance5 : FooClass, FooProtocol, BarProtocol {{{$}}
800+
// PASS_ONE_LINE-DAG: {{^}}@_inheritsConvenienceInitializers class ClassWithInheritance5 : FooClass, FooProtocol, BarProtocol {{{$}}
801801

802802
class ClassWithInheritance6 : QuxProtocol, SubFooProtocol {
803803
typealias Qux = Int

0 commit comments

Comments
 (0)