Skip to content

Sema: implement getInitKind using a request evaluator. #26377

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 7 commits into from
Jul 30, 2019
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
1 change: 1 addition & 0 deletions include/swift/AST/ASTTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ SWIFT_TYPEID_NAMED(Decl *, Decl)
SWIFT_TYPEID(Type)
SWIFT_TYPEID(PropertyWrapperBackingPropertyInfo)
SWIFT_TYPEID(PropertyWrapperTypeInfo)
SWIFT_TYPEID(CtorInitializerKind)
SWIFT_TYPEID_NAMED(Optional<PropertyWrapperMutability>, PropertyWrapperMutability)
SWIFT_TYPEID_NAMED(CustomAttr *, CustomAttr)
SWIFT_TYPEID_NAMED(TypeAliasDecl *, TypeAliasDecl)
1 change: 1 addition & 0 deletions include/swift/AST/ASTTypeIDs.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class CustomAttr;
class NominalTypeDecl;
struct PropertyWrapperBackingPropertyInfo;
struct PropertyWrapperTypeInfo;
enum class CtorInitializerKind;
struct PropertyWrapperMutability;
class Type;
class VarDecl;
Expand Down
14 changes: 2 additions & 12 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -450,17 +450,14 @@ class alignas(1 << DeclAlignInBits) Decl {
AccessorKind : 4
);

SWIFT_INLINE_BITFIELD(ConstructorDecl, AbstractFunctionDecl, 3+2+2+1,
SWIFT_INLINE_BITFIELD(ConstructorDecl, AbstractFunctionDecl, 3+2+1,
/// The body initialization kind (+1), or zero if not yet computed.
///
/// This value is cached but is not serialized, because it is a property
/// of the definition of the constructor that is useful only to semantic
/// analysis and SIL generation.
ComputedBodyInitKind : 3,

/// The kind of initializer we have.
InitKind : 2,

/// The failability of this initializer, which is an OptionalTypeKind.
Failability : 2,

Expand Down Expand Up @@ -6525,14 +6522,7 @@ class ConstructorDecl : public AbstractFunctionDecl {
}

/// Determine the kind of initializer this is.
CtorInitializerKind getInitKind() const {
return static_cast<CtorInitializerKind>(Bits.ConstructorDecl.InitKind);
}

/// Set whether this is a convenience initializer.
void setInitKind(CtorInitializerKind kind) {
Bits.ConstructorDecl.InitKind = static_cast<unsigned>(kind);
}
CtorInitializerKind getInitKind() const;

/// Whether this is a designated initializer.
bool isDesignatedInit() const {
Expand Down
4 changes: 2 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2813,13 +2813,13 @@ NOTE(convenience_init_here,none,
ERROR(designated_init_in_extension,none,
"designated initializer cannot be declared in an extension of %0; "
"did you mean this to be a convenience initializer?",
(Type))
(DeclName))
ERROR(enumstruct_convenience_init,none,
"delegating initializers in %0 are not marked with 'convenience'",
(StringRef))
ERROR(nonclass_convenience_init,none,
"convenience initializer not allowed in non-class type %0",
(Type))
(DeclName))
ERROR(cfclass_convenience_init,none,
"convenience initializers are not supported in extensions of CF types",
())
Expand Down
22 changes: 22 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,28 @@ class IsObjCRequest :
void cacheResult(bool value) const;
};

void simple_display(llvm::raw_ostream &out, CtorInitializerKind initKind);

/// Computes the kind of initializer for a given \c ConstructorDecl
class InitKindRequest:
public SimpleRequest<InitKindRequest,
CtorInitializerKind(ConstructorDecl *),
CacheKind::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
llvm::Expected<CtorInitializerKind>
evaluate(Evaluator &evaluator, ConstructorDecl *decl) const;

public:
// Caching.
bool isCached() const { return true; }
};

/// Determine whether the given protocol declaration is class-bounded.
class ProtocolRequiresClassRequest:
public SimpleRequest<ProtocolRequiresClassRequest,
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ SWIFT_TYPEID(SuperclassTypeRequest)
SWIFT_TYPEID(EnumRawTypeRequest)
SWIFT_TYPEID(OverriddenDeclsRequest)
SWIFT_TYPEID(IsObjCRequest)
SWIFT_TYPEID(InitKindRequest)
SWIFT_TYPEID(ProtocolRequiresClassRequest)
SWIFT_TYPEID(ExistentialConformsToSelfRequest)
SWIFT_TYPEID(ExistentialTypeSupportedRequest)
Expand Down
7 changes: 6 additions & 1 deletion lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6925,7 +6925,6 @@ ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc,

Bits.ConstructorDecl.ComputedBodyInitKind = 0;
Bits.ConstructorDecl.HasStubImplementation = 0;
Bits.ConstructorDecl.InitKind = static_cast<unsigned>(CtorInitializerKind::Designated);
Bits.ConstructorDecl.Failability = static_cast<unsigned>(Failability);

assert(Name.getBaseName() == DeclBaseName::createConstructor());
Expand Down Expand Up @@ -7117,6 +7116,12 @@ Type ConstructorDecl::getInitializerInterfaceType() {
return InitializerInterfaceType;
}

CtorInitializerKind ConstructorDecl::getInitKind() const {
return evaluateOrDefault(getASTContext().evaluator,
InitKindRequest{const_cast<ConstructorDecl *>(this)},
CtorInitializerKind::Designated);
}

ConstructorDecl::BodyInitKind
ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags,
ApplyExpr **init) const {
Expand Down
17 changes: 16 additions & 1 deletion lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,14 +592,29 @@ void swift::simple_display(
out << " }";
}

void swift::simple_display(
llvm::raw_ostream &out, const CtorInitializerKind initKind) {
out << "{ ";
switch (initKind) {
case CtorInitializerKind::Designated:
out << "designated"; break;
case CtorInitializerKind::Convenience:
out << "convenience"; break;
case CtorInitializerKind::ConvenienceFactory:
out << "convenience_factory"; break;
case CtorInitializerKind::Factory:
out << "factory"; break;
}
out << " }";
}

void swift::simple_display(llvm::raw_ostream &os, PropertyWrapperMutability m) {
static const char *names[] =
{"is nonmutating", "is mutating", "doesn't exist"};

os << "getter " << names[m.Getter] << ", setter " << names[m.Setter];
}


//----------------------------------------------------------------------------//
// FunctionBuilder-related requests.
//----------------------------------------------------------------------------//
Expand Down
7 changes: 5 additions & 2 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/Stmt.h"
#include "swift/AST/Types.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/PrettyStackTrace.h"
#include "swift/ClangImporter/ClangModule.h"
Expand Down Expand Up @@ -5695,7 +5696,8 @@ Decl *SwiftDeclConverter::importGlobalAsInitializer(
initOptionality, /*FailabilityLoc=*/SourceLoc(),
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), parameterList,
/*GenericParams=*/nullptr, dc);
result->setInitKind(initKind);
result->getASTContext().evaluator.cacheOutput(InitKindRequest{result},
std::move(initKind));
result->setImportAsStaticMember();

// Set the constructor's type.
Expand Down Expand Up @@ -6324,7 +6326,8 @@ ConstructorDecl *SwiftDeclConverter::importConstructor(
result->setImplicit();

// Set the kind of initializer.
result->setInitKind(kind);
result->getASTContext().evaluator.cacheOutput(InitKindRequest{result},
std::move(kind));

// Consult API notes to determine whether this initializer is required.
if (!required && isRequiredInitializer(objcMethod))
Expand Down
8 changes: 1 addition & 7 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6514,6 +6514,7 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) {
throwsLoc.isValid(), throwsLoc,
Params.get(), nullptr,
CurDeclContext);
CD->getAttrs() = Attributes;

// Parse a 'where' clause if present, adding it to our GenericParamList.
if (Tok.is(tok::kw_where)) {
Expand All @@ -6529,11 +6530,6 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) {

CD->setGenericParams(GenericParams);

CtorInitializerKind initKind = CtorInitializerKind::Designated;
if (Attributes.hasAttribute<ConvenienceAttr>())
initKind = CtorInitializerKind::Convenience;
CD->setInitKind(initKind);

// No need to setLocalDiscriminator.

DefaultArgs.setFunctionContext(CD, CD->getParameters());
Expand All @@ -6556,8 +6552,6 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) {
parseAbstractFunctionBody(CD);
}

CD->getAttrs() = Attributes;

return makeParserResult(CD);
}

Expand Down
3 changes: 2 additions & 1 deletion lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,8 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl<ASTNode> &Entries,
SourceLoc StartLoc = Tok.getLoc();
auto CD = cast<ConstructorDecl>(CurDeclContext);
// Hint at missing 'self.' or 'super.' then skip this statement.
bool isSelf = !CD->isDesignatedInit() || !isa<ClassDecl>(CD->getParent());
bool isSelf = CD->getAttrs().hasAttribute<ConvenienceAttr>() ||
!isa<ClassDecl>(CD->getParent());
diagnose(StartLoc, diag::invalid_nested_init, isSelf)
.fixItInsert(StartLoc, isSelf ? "self." : "super.");
NeedParseErrorRecovery = true;
Expand Down
117 changes: 66 additions & 51 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,69 @@ static bool doesAccessorNeedDynamicAttribute(AccessorDecl *accessor) {
llvm_unreachable("covered switch");
}

llvm::Expected<CtorInitializerKind>
InitKindRequest::evaluate(Evaluator &evaluator, ConstructorDecl *decl) const {
auto &diags = decl->getASTContext().Diags;

// Convenience inits are only allowed on classes and in extensions thereof.
if (decl->getAttrs().hasAttribute<ConvenienceAttr>()) {
if (auto nominal = decl->getDeclContext()->getSelfNominalTypeDecl()) {
auto classDecl = dyn_cast<ClassDecl>(nominal);

// Forbid convenience inits on Foreign CF types, as Swift does not yet
// support user-defined factory inits.
if (classDecl &&
classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType) {
diags.diagnose(decl->getLoc(), diag::cfclass_convenience_init);
}

if (!classDecl) {
auto ConvenienceLoc =
decl->getAttrs().getAttribute<ConvenienceAttr>()->getLocation();

// Produce a tailored diagnostic for structs and enums.
bool isStruct = dyn_cast<StructDecl>(nominal) != nullptr;
if (isStruct || dyn_cast<EnumDecl>(nominal)) {
diags.diagnose(decl->getLoc(), diag::enumstruct_convenience_init,
isStruct ? "structs" : "enums")
.fixItRemove(ConvenienceLoc);
} else {
diags.diagnose(decl->getLoc(), diag::nonclass_convenience_init,
nominal->getName())
.fixItRemove(ConvenienceLoc);
}
return CtorInitializerKind::Designated;
}
}

return CtorInitializerKind::Convenience;

} else if (auto nominal = decl->getDeclContext()->getSelfNominalTypeDecl()) {
// A designated init for a class must be written within the class itself.
//
// This is because designated initializers of classes get a vtable entry,
// and extensions cannot add vtable entries to the extended type.
//
// If we implement the ability for extensions defined in the same module
// (or the same file) to add vtable entries, we can re-evaluate this
// restriction.
if (dyn_cast<ClassDecl>(nominal) &&
!decl->isSynthesized() && isa<ExtensionDecl>(decl->getDeclContext()) &&
!(decl->getAttrs().hasAttribute<DynamicReplacementAttr>())) {
diags.diagnose(decl->getLoc(), diag::designated_init_in_extension,
nominal->getName())
.fixItInsert(decl->getLoc(), "convenience ");
return CtorInitializerKind::Convenience;
}

if (decl->getDeclContext()->getExtendedProtocolDecl()) {
return CtorInitializerKind::Convenience;
}
}

return CtorInitializerKind::Designated;
}

llvm::Expected<bool>
ProtocolRequiresClassRequest::evaluate(Evaluator &evaluator,
ProtocolDecl *decl) const {
Expand Down Expand Up @@ -3488,6 +3551,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
void visitConstructorDecl(ConstructorDecl *CD) {
TC.validateDecl(CD);

// Compute these requests in case they emit diagnostics.
(void) CD->getInitKind();

if (!CD->isInvalid()) {
checkGenericParams(CD->getGenericParams(), CD, TC);
TC.checkReferencedGenericParams(CD);
Expand Down Expand Up @@ -4314,57 +4380,6 @@ void TypeChecker::validateDecl(ValueDecl *D) {

checkDeclAttributesEarly(CD);

// convenience initializers are only allowed on classes and in
// extensions thereof.
if (CD->isConvenienceInit()) {
if (auto extType = CD->getDeclContext()->getDeclaredInterfaceType()) {
auto extClass = extType->getClassOrBoundGenericClass();

// Forbid convenience inits on Foreign CF types, as Swift does not yet
// support user-defined factory inits.
if (extClass &&
extClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType) {
diagnose(CD->getLoc(), diag::cfclass_convenience_init);
}

if (!extClass && !extType->hasError()) {
auto ConvenienceLoc =
CD->getAttrs().getAttribute<ConvenienceAttr>()->getLocation();

// Produce a tailored diagnostic for structs and enums.
bool isStruct = extType->getStructOrBoundGenericStruct() != nullptr;
if (isStruct || extType->getEnumOrBoundGenericEnum()) {
diagnose(CD->getLoc(), diag::enumstruct_convenience_init,
isStruct ? "structs" : "enums")
.fixItRemove(ConvenienceLoc);
} else {
diagnose(CD->getLoc(), diag::nonclass_convenience_init, extType)
.fixItRemove(ConvenienceLoc);
}
CD->setInitKind(CtorInitializerKind::Designated);
}
}
} else if (auto extType = CD->getDeclContext()->getDeclaredInterfaceType()) {
// A designated initializer for a class must be written within the class
// itself.
//
// This is because designated initializers of classes get a vtable entry,
// and extensions cannot add vtable entries to the extended type.
//
// If we implement the ability for extensions defined in the same module
// (or the same file) to add vtable entries, we can re-evaluate this
// restriction.
if (extType->getClassOrBoundGenericClass() &&
isa<ExtensionDecl>(CD->getDeclContext()) &&
!(CD->getAttrs().hasAttribute<DynamicReplacementAttr>())) {
diagnose(CD->getLoc(), diag::designated_init_in_extension, extType)
.fixItInsert(CD->getLoc(), "convenience ");
CD->setInitKind(CtorInitializerKind::Convenience);
} else if (CD->getDeclContext()->getExtendedProtocolDecl()) {
CD->setInitKind(CtorInitializerKind::Convenience);
}
}

validateGenericFuncOrSubscriptSignature(CD, CD, CD);

// We want the constructor to be available for name lookup as soon
Expand Down
11 changes: 1 addition & 10 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4351,17 +4351,8 @@ canSuppressPotentialWitnessWarningWithMovement(ValueDecl *requirement,
// If the witness is a designated or required initializer, we can't move it
// to an extension.
if (auto ctor = dyn_cast<ConstructorDecl>(witness)) {
switch (ctor->getInitKind()) {
case CtorInitializerKind::Designated:
if (ctor->isDesignatedInit() || ctor->isRequired())
return None;

case CtorInitializerKind::Convenience:
case CtorInitializerKind::ConvenienceFactory:
case CtorInitializerKind::Factory:
break;
}

if (ctor->isRequired()) return None;
}

// We can move this entity to an extension.
Expand Down
1 change: 0 additions & 1 deletion lib/Sema/TypeCheckStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2068,7 +2068,6 @@ static void checkClassConstructorBody(ClassDecl *classDecl,
ctor->getDeclContext()->getDeclaredInterfaceType())
.fixItInsert(ctor->getLoc(), "convenience ");
ctx.Diags.diagnose(initExpr->getLoc(), diag::delegation_here);
ctor->setInitKind(CtorInitializerKind::Convenience);
}

// An inlinable constructor in a class must always be delegating,
Expand Down
3 changes: 2 additions & 1 deletion lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2707,7 +2707,8 @@ class swift::DeclDeserializer {
if (hasStubImplementation)
ctor->setStubImplementation(true);
if (initKind.hasValue())
ctor->setInitKind(initKind.getValue());
ctx.evaluator.cacheOutput(InitKindRequest{ctor},
std::move(initKind.getValue()));
ctor->setNeedsNewVTableEntry(needsNewVTableEntry);

ctor->setOverriddenDecl(cast_or_null<ConstructorDecl>(overridden.get()));
Expand Down
Loading