[Sema] Requestify memberwise init synthesis

This commit adds two requests, one to compute
whether or not a decl should have a memberwise
initializer, and another to synthesize it.
This commit is contained in:
Hamish Knight
2019-10-22 15:26:39 -07:00
parent d8477d51f2
commit ffd5a9cfd6
7 changed files with 112 additions and 24 deletions

View File

@@ -25,6 +25,7 @@ SWIFT_TYPEID(Requirement)
SWIFT_TYPEID(ResilienceExpansion)
SWIFT_TYPEID(Type)
SWIFT_TYPEID(TypePair)
SWIFT_TYPEID_NAMED(ConstructorDecl *, ConstructorDecl)
SWIFT_TYPEID_NAMED(CustomAttr *, CustomAttr)
SWIFT_TYPEID_NAMED(Decl *, Decl)
SWIFT_TYPEID_NAMED(EnumDecl *, EnumDecl)

View File

@@ -23,6 +23,7 @@ namespace swift {
class AbstractFunctionDecl;
class BraceStmt;
class ConstructorDecl;
class CustomAttr;
class Decl;
class EnumDecl;

View File

@@ -3464,6 +3464,13 @@ public:
});
}
/// Whether this declaration has a synthesized memberwise initializer.
bool hasMemberwiseInitializer() const;
/// Retrieves the synthesized memberwise initializer for this declaration,
/// or \c nullptr if it does not have one.
ConstructorDecl *getMemberwiseInitializer() const;
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
return D->getKind() >= DeclKind::First_NominalTypeDecl &&

View File

@@ -1549,6 +1549,44 @@ public:
bool isCached() const { return true; }
};
/// Checks whether this type has a synthesized memberwise initializer.
class HasMemberwiseInitRequest
: public SimpleRequest<HasMemberwiseInitRequest, bool(StructDecl *),
CacheKind::Cached> {
public:
using SimpleRequest::SimpleRequest;
private:
friend SimpleRequest;
// Evaluation.
llvm::Expected<bool> evaluate(Evaluator &evaluator, StructDecl *decl) const;
public:
// Caching.
bool isCached() const { return true; }
};
/// Synthesizes a memberwise initializer for a given type.
class SynthesizeMemberwiseInitRequest
: public SimpleRequest<SynthesizeMemberwiseInitRequest,
ConstructorDecl *(NominalTypeDecl *),
CacheKind::Cached> {
public:
using SimpleRequest::SimpleRequest;
private:
friend SimpleRequest;
// Evaluation.
llvm::Expected<ConstructorDecl *> evaluate(Evaluator &evaluator,
NominalTypeDecl *decl) const;
public:
// Caching.
bool isCached() const { return true; }
};
// Allow AnyValue to compare two Type values, even though Type doesn't
// support ==.
template<>

View File

@@ -169,3 +169,7 @@ SWIFT_REQUEST(TypeChecker, AreAllStoredPropertiesDefaultInitableRequest,
bool(NominalTypeDecl *), Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, HasUserDefinedDesignatedInitRequest,
bool(NominalTypeDecl *), Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, HasMemberwiseInitRequest,
bool(StructDecl *), Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, SynthesizeMemberwiseInitRequest,
ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo)

View File

@@ -3834,6 +3834,28 @@ StructDecl::StructDecl(SourceLoc StructLoc, Identifier Name, SourceLoc NameLoc,
Bits.StructDecl.HasUnreferenceableStorage = false;
}
bool NominalTypeDecl::hasMemberwiseInitializer() const {
// Currently only structs can have memberwise initializers.
auto *sd = dyn_cast<StructDecl>(this);
if (!sd)
return false;
auto &ctx = getASTContext();
auto *mutableThis = const_cast<StructDecl *>(sd);
return evaluateOrDefault(ctx.evaluator, HasMemberwiseInitRequest{mutableThis},
false);
}
ConstructorDecl *NominalTypeDecl::getMemberwiseInitializer() const {
if (!hasMemberwiseInitializer())
return nullptr;
auto &ctx = getASTContext();
auto *mutableThis = const_cast<NominalTypeDecl *>(this);
return evaluateOrDefault(
ctx.evaluator, SynthesizeMemberwiseInitRequest{mutableThis}, nullptr);
}
ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc,
MutableArrayRef<TypeLoc> Inherited,
GenericParamList *GenericParams, DeclContext *Parent)

View File

@@ -871,36 +871,14 @@ static void addImplicitConstructorsToStruct(StructDecl *decl) {
"User-defined structs cannot have unreferenceable storage");
decl->setAddedImplicitInitializers();
// Check whether there is a user-declared constructor or an instance
// variable.
bool FoundMemberwiseInitializedProperty = false;
(void)decl->getMemberwiseInitializer();
// If the user has already defined a designated initializer, then don't
// synthesize an initializer.
// synthesize a default initializer.
auto &ctx = decl->getASTContext();
if (hasUserDefinedDesignatedInit(ctx.evaluator, decl))
return;
for (auto member : decl->getMembers()) {
if (auto var = dyn_cast<VarDecl>(member)) {
// If this is a backing storage property for a property wrapper,
// skip it.
if (var->getOriginalWrappedProperty())
continue;
if (var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
FoundMemberwiseInitializedProperty = true;
}
}
if (FoundMemberwiseInitializedProperty) {
// Create the implicit memberwise constructor.
auto ctor = createImplicitConstructor(
decl, ImplicitConstructorKind::Memberwise, ctx);
decl->addMember(ctor);
}
if (areAllStoredPropertiesDefaultInitializable(ctx.evaluator, decl))
TypeChecker::defineDefaultConstructor(decl);
}
@@ -1203,6 +1181,43 @@ void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target,
}
}
llvm::Expected<bool>
HasMemberwiseInitRequest::evaluate(Evaluator &evaluator,
StructDecl *decl) const {
// Don't synthesize a memberwise init for imported decls.
if (decl->hasClangNode())
return false;
// If the user has already defined a designated initializer, then don't
// synthesize a memberwise init.
if (hasUserDefinedDesignatedInit(evaluator, decl))
return false;
for (auto *member : decl->getMembers()) {
if (auto *var = dyn_cast<VarDecl>(member)) {
// If this is a backing storage property for a property wrapper,
// skip it.
if (var->getOriginalWrappedProperty())
continue;
if (var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
return true;
}
}
return false;
}
llvm::Expected<ConstructorDecl *>
SynthesizeMemberwiseInitRequest::evaluate(Evaluator &evaluator,
NominalTypeDecl *decl) const {
// Create the implicit memberwise constructor.
auto &ctx = decl->getASTContext();
auto ctor =
createImplicitConstructor(decl, ImplicitConstructorKind::Memberwise, ctx);
decl->addMember(ctor);
return ctor;
}
/// Synthesizer callback for a function body consisting of "return".
static std::pair<BraceStmt *, bool>
synthesizeSingleReturnFunctionBody(AbstractFunctionDecl *afd, void *) {