Skip to content

Commit 2317733

Browse files
authored
Merge pull request swiftlang#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

0 commit comments

Comments
 (0)