Skip to content

[Shepherd] Support classes with missing designated inits #28629

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

Merged
Merged
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
29 changes: 22 additions & 7 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ class alignas(1 << DeclAlignInBits) Decl {
RawForeignKind : 2,

/// \see ClassDecl::getEmittedMembers()
HasForcedEmittedMembers : 1,
HasForcedEmittedMembers : 1,

HasMissingDesignatedInitializers : 1,
ComputedHasMissingDesignatedInitializers : 1,
Expand Down Expand Up @@ -3833,6 +3833,25 @@ class ClassDecl final : public NominalTypeDecl {
return None;
}

Optional<bool> getCachedHasMissingDesignatedInitializers() const {
if (!Bits.ClassDecl.ComputedHasMissingDesignatedInitializers) {
// Force loading all the members, which will add this attribute if any of
// members are determined to be missing while loading.
auto mutableThis = const_cast<ClassDecl *>(this);
(void)mutableThis->lookupDirect(DeclBaseName::createConstructor());
}

if (Bits.ClassDecl.ComputedHasMissingDesignatedInitializers)
return Bits.ClassDecl.HasMissingDesignatedInitializers;

return None;
}

void setHasMissingDesignatedInitializers(bool value) {
Bits.ClassDecl.HasMissingDesignatedInitializers = value;
Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = true;
}

/// Marks that this class inherits convenience initializers from its
/// superclass.
void setInheritsSuperclassInitializers(bool value) {
Expand All @@ -3843,6 +3862,7 @@ class ClassDecl final : public NominalTypeDecl {
friend class SuperclassDeclRequest;
friend class SuperclassTypeRequest;
friend class EmittedMembersRequest;
friend class HasMissingDesignatedInitializersRequest;
friend class InheritsSuperclassInitializersRequest;
friend class TypeChecker;

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

void setHasMissingDesignatedInitializers(bool newValue = true) {
Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = 1;
Bits.ClassDecl.HasMissingDesignatedInitializers = newValue;
}

/// Returns true if the class has missing members that require vtable entries.
///
/// In this case, the class cannot be subclassed, because we cannot construct
Expand Down Expand Up @@ -3992,7 +4007,7 @@ class ClassDecl final : public NominalTypeDecl {

/// Determine whether this class inherits the convenience initializers
/// from its superclass.
bool inheritsSuperclassInitializers();
bool inheritsSuperclassInitializers() const;

/// Walks the class hierarchy starting from this class, checking various
/// conditions.
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsModuleDiffer.def
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ ERROR(objc_name_change,none,"%0 has ObjC name change from %1 to %2", (StringRef,

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

ERROR(added_invisible_designated_init,none,"%0 has new designated initializers that are not visible to clients", (StringRef))

ERROR(not_inheriting_convenience_inits,none,"%0 no longer inherits convenience inits from its superclass", (StringRef))

#ifndef DIAG_NO_UNDEF
# if defined(DIAG)
# undef DIAG
Expand Down
23 changes: 23 additions & 0 deletions include/swift/AST/NameLookupRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,29 @@ class SuperclassDeclRequest :
void cacheResult(ClassDecl *value) const;
};

/// Requests whether or not this class has designated initializers that are
/// not public or @usableFromInline.
class HasMissingDesignatedInitializersRequest :
public SimpleRequest<HasMissingDesignatedInitializersRequest,
bool(ClassDecl *),
CacheKind::SeparatelyCached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
llvm::Expected<bool>
evaluate(Evaluator &evaluator, ClassDecl *subject) const;

public:
// Caching
bool isCached() const { return true; }
Optional<bool> getCachedResult() const;
void cacheResult(bool) const;
};

/// Request the nominal declaration extended by a given extension declaration.
class ExtendedNominalRequest :
public SimpleRequest<ExtendedNominalRequest,
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/NameLookupTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ SWIFT_REQUEST(NameLookup, SelfBoundsFromWhereClauseRequest,
Uncached, NoLocationInfo)
SWIFT_REQUEST(NameLookup, SuperclassDeclRequest, ClassDecl *(NominalTypeDecl *),
SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(NameLookup, HasMissingDesignatedInitializersRequest,
bool(ClassDecl *),
SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(NameLookup, TypeDeclsFromWhereClauseRequest,
DirectlyReferencedTypeDecls(ExtensionDecl *), Uncached,
NoLocationInfo)
Expand Down
2 changes: 2 additions & 0 deletions include/swift/IDE/DigesterEnums.def
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ KEY_BOOL(HasStorage, hasStorage)
KEY_BOOL(ReqNewWitnessTableEntry, reqNewWitnessTableEntry)
KEY_BOOL(IsABIPlaceholder, isABIPlaceholder)
KEY_BOOL(IsExternal, isExternal)
KEY_BOOL(HasMissingDesignatedInitializers, hasMissingDesignatedInitializers)
KEY_BOOL(InheritsConvenienceInitializers, inheritsConvenienceInitializers)

KEY(kind)

Expand Down
16 changes: 16 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,22 @@ void PrintAST::printAttributes(const Decl *D) {
Printer << " ";
}
}

// If the declaration has designated inits that won't be visible to
// clients, or if it inherits superclass convenience initializers,
// then print those attributes specially.
if (auto CD = dyn_cast<ClassDecl>(D)) {
if (Options.PrintImplicitAttrs) {
if (CD->inheritsSuperclassInitializers()) {
Printer.printAttrName("@_inheritsConvenienceInitializers");
Printer << " ";
}
if (CD->hasMissingDesignatedInitializers()) {
Printer.printAttrName("@_hasMissingDesignatedInitializers");
Printer << " ";
}
}
}
}

D->getAttrs().print(Printer, Options, D);
Expand Down
14 changes: 5 additions & 9 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4127,15 +4127,11 @@ GetDestructorRequest::evaluate(Evaluator &evaluator, ClassDecl *CD) const {
return DD;
}


bool ClassDecl::hasMissingDesignatedInitializers() const {
if (!Bits.ClassDecl.ComputedHasMissingDesignatedInitializers) {
auto *mutableThis = const_cast<ClassDecl *>(this);
mutableThis->Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = 1;
(void)mutableThis->lookupDirect(DeclBaseName::createConstructor());
}

return Bits.ClassDecl.HasMissingDesignatedInitializers;
return evaluateOrDefault(
getASTContext().evaluator,
HasMissingDesignatedInitializersRequest{const_cast<ClassDecl *>(this)},
false);
}

bool ClassDecl::hasMissingVTableEntries() const {
Expand All @@ -4158,7 +4154,7 @@ bool ClassDecl::isIncompatibleWithWeakReferences() const {
return false;
}

bool ClassDecl::inheritsSuperclassInitializers() {
bool ClassDecl::inheritsSuperclassInitializers() const {
// If there's no superclass, there's nothing to inherit.
if (!getSuperclass())
return false;
Expand Down
41 changes: 41 additions & 0 deletions lib/AST/NameLookupRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,47 @@ void SuperclassDeclRequest::cacheResult(ClassDecl *value) const {
protocolDecl->LazySemanticInfo.SuperclassDecl.setPointerAndInt(value, true);
}

//----------------------------------------------------------------------------//
// Missing designated initializers computation
//----------------------------------------------------------------------------//

Optional<bool> HasMissingDesignatedInitializersRequest::getCachedResult() const {
auto classDecl = std::get<0>(getStorage());
return classDecl->getCachedHasMissingDesignatedInitializers();
}

void HasMissingDesignatedInitializersRequest::cacheResult(bool result) const {
auto classDecl = std::get<0>(getStorage());
classDecl->setHasMissingDesignatedInitializers(result);
}

llvm::Expected<bool>
HasMissingDesignatedInitializersRequest::evaluate(Evaluator &evaluator,
ClassDecl *subject) const {
// Short-circuit and check for the attribute here.
if (subject->getAttrs().hasAttribute<HasMissingDesignatedInitializersAttr>())
return true;

AccessScope scope =
subject->getFormalAccessScope(/*useDC*/nullptr,
/*treatUsableFromInlineAsPublic*/true);
// This flag only makes sense for public types that will be written in the
// module.
if (!scope.isPublic())
return false;

auto constructors = subject->lookupDirect(DeclBaseName::createConstructor());
return llvm::any_of(constructors, [&](ValueDecl *decl) {
auto init = cast<ConstructorDecl>(decl);
if (!init->isDesignatedInit())
return false;
AccessScope scope =
init->getFormalAccessScope(/*useDC*/nullptr,
/*treatUsableFromInlineAsPublic*/true);
return !scope.isPublic();
});
}

//----------------------------------------------------------------------------//
// Extended nominal computation.
//----------------------------------------------------------------------------//
Expand Down
3 changes: 2 additions & 1 deletion lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7695,7 +7695,8 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
if (getClangModuleForDecl(theClass) == getClangModuleForDecl(method)) {
if (auto swiftClass = castIgnoringCompatibilityAlias<ClassDecl>(
importDecl(theClass, CurrentVersion))) {
swiftClass->setHasMissingDesignatedInitializers();
SwiftContext.evaluator.cacheOutput(
HasMissingDesignatedInitializersRequest{swiftClass}, true);
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1002,13 +1002,18 @@ static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) {
llvm::Expected<bool>
InheritsSuperclassInitializersRequest::evaluate(Evaluator &eval,
ClassDecl *decl) const {
// Check if we parsed the @_inheritsConvenienceInitializers attribute.
if (decl->getAttrs().hasAttribute<InheritsConvenienceInitializersAttr>())
return true;

auto superclass = decl->getSuperclass();
assert(superclass);

// If the superclass has known-missing designated initializers, inheriting
// is unsafe.
auto *superclassDecl = superclass->getClassOrBoundGenericClass();
if (superclassDecl->hasMissingDesignatedInitializers())
if (superclassDecl->getModuleContext() != decl->getParentModule() &&
superclassDecl->hasMissingDesignatedInitializers())
return false;

// If we're allowed to inherit designated initializers, then we can inherit
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,7 @@ static void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) {

auto *superclassDecl = classDecl->getSuperclassDecl();
if (superclassDecl &&
superclassDecl->getModuleContext() != classDecl->getModuleContext() &&
superclassDecl->hasMissingDesignatedInitializers())
return;

Expand Down
7 changes: 6 additions & 1 deletion lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3416,6 +3416,7 @@ class DeclDeserializer {
DeclContextID contextID;
bool isImplicit, isObjC;
bool inheritsSuperclassInitializers;
bool hasMissingDesignatedInits;
GenericSignatureID genericSigID;
TypeID superclassID;
uint8_t rawAccessLevel;
Expand All @@ -3424,6 +3425,7 @@ class DeclDeserializer {
decls_block::ClassLayout::readRecord(scratch, nameID, contextID,
isImplicit, isObjC,
inheritsSuperclassInitializers,
hasMissingDesignatedInits,
genericSigID, superclassID,
rawAccessLevel, numConformances,
numInheritedTypes,
Expand Down Expand Up @@ -3466,6 +3468,8 @@ class DeclDeserializer {
theClass->setSuperclass(MF.getType(superclassID));
ctx.evaluator.cacheOutput(InheritsSuperclassInitializersRequest{theClass},
std::move(inheritsSuperclassInitializers));
ctx.evaluator.cacheOutput(HasMissingDesignatedInitializersRequest{theClass},
std::move(hasMissingDesignatedInits));

handleInherited(theClass,
rawInheritedAndDependencyIDs.slice(0, numInheritedTypes));
Expand Down Expand Up @@ -5390,7 +5394,8 @@ Decl *handleErrorAndSupplyMissingClassMember(ASTContext &context,
Decl *suppliedMissingMember = nullptr;
auto handleMissingClassMember = [&](const DeclDeserializationError &error) {
if (error.isDesignatedInitializer())
containingClass->setHasMissingDesignatedInitializers();
context.evaluator.cacheOutput(
HasMissingDesignatedInitializersRequest{containingClass}, true);
if (error.getNumberOfVTableEntries() > 0)
containingClass->setHasMissingVTableEntries();

Expand Down
3 changes: 2 additions & 1 deletion lib/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 531; // function parameter noDerivative
const uint16_t SWIFTMODULE_VERSION_MINOR = 532; // @_hasMissingDesignatedInitializers

/// A standard hash seed used for all string hashes in a serialized module.
///
Expand Down Expand Up @@ -1114,6 +1114,7 @@ namespace decls_block {
BCFixed<1>, // implicit?
BCFixed<1>, // explicitly objc?
BCFixed<1>, // inherits convenience initializers from its superclass?
BCFixed<1>, // has missing designated initializers?
GenericSignatureIDField, // generic environment
TypeIDField, // superclass
AccessLevelField, // access level
Expand Down
7 changes: 3 additions & 4 deletions lib/Serialization/Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3139,17 +3139,16 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
uint8_t rawAccessLevel =
getRawStableAccessLevel(theClass->getFormalAccess());

bool inheritsSuperclassInitializers =
const_cast<ClassDecl *>(theClass)->
inheritsSuperclassInitializers();
auto mutableClass = const_cast<ClassDecl *>(theClass);

unsigned abbrCode = S.DeclTypeAbbrCodes[ClassLayout::Code];
ClassLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode,
S.addDeclBaseNameRef(theClass->getName()),
contextID.getOpaqueValue(),
theClass->isImplicit(),
theClass->isObjC(),
inheritsSuperclassInitializers,
mutableClass->inheritsSuperclassInitializers(),
mutableClass->hasMissingDesignatedInitializers(),
S.addGenericSignatureRef(
theClass->getGenericSignature()),
S.addTypeRef(theClass->getSuperclass()),
Expand Down
14 changes: 7 additions & 7 deletions test/IDE/print_ast_tc_decls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ class d0120_TestClassBase {
}

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

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

class `extension` : `class` {}
// PASS_ONE_LINE_TYPE-DAG: {{^}} class `extension` : d0200_EscapedIdentifiers.`class` {{{$}}
// PASS_ONE_LINE_TYPEREPR-DAG: {{^}} class `extension` : `class` {{{$}}
// PASS_ONE_LINE_TYPE-DAG: {{^}} @_inheritsConvenienceInitializers class `extension` : d0200_EscapedIdentifiers.`class` {{{$}}
// PASS_ONE_LINE_TYPEREPR-DAG: {{^}} @_inheritsConvenienceInitializers class `extension` : `class` {{{$}}
// PASS_COMMON: {{^}} @objc deinit{{$}}
// PASS_COMMON-NEXT: {{^}} {{(override )?}}init(){{$}}
// PASS_COMMON-NEXT: {{^}} }{{$}}
Expand Down Expand Up @@ -748,7 +748,7 @@ class d0260_ExplodePattern_TestClassBase {
}

class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase {
// PASS_EXPLODE_PATTERN-LABEL: {{^}}class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase {{{$}}
// PASS_EXPLODE_PATTERN-LABEL: {{^}}@_inheritsConvenienceInitializers class d0261_ExplodePattern_TestClassDerived : d0260_ExplodePattern_TestClassBase {{{$}}

override final var baseProp2: Int {
get {
Expand Down Expand Up @@ -791,13 +791,13 @@ class ClassWithInheritance2 : FooProtocol, BarProtocol {}
// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance2 : FooProtocol, BarProtocol {{{$}}

class ClassWithInheritance3 : FooClass {}
// PASS_ONE_LINE-DAG: {{^}}class ClassWithInheritance3 : FooClass {{{$}}
// PASS_ONE_LINE-DAG: {{^}}@_inheritsConvenienceInitializers class ClassWithInheritance3 : FooClass {{{$}}

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

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

class ClassWithInheritance6 : QuxProtocol, SubFooProtocol {
typealias Qux = Int
Expand Down
Loading