Skip to content

Commit 961c16a

Browse files
committed
[Sema] Requestify default init synthesis
This commit adds two requests, one to compute whether or not a decl should have a default initializer, and another to synthesize it. This then allows us to remove the default constructor synthesis from addImplicitConstructorsToClass as well as completely eliminate addImplicitConstructorsToStruct. For now, we can just force the requests in addImplicitConstructors.
1 parent ffd5a9c commit 961c16a

File tree

6 files changed

+115
-43
lines changed

6 files changed

+115
-43
lines changed

include/swift/AST/Decl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3471,6 +3471,14 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
34713471
/// or \c nullptr if it does not have one.
34723472
ConstructorDecl *getMemberwiseInitializer() const;
34733473

3474+
/// Whether this declaration has a synthesized zero parameter default
3475+
/// initializer.
3476+
bool hasDefaultInitializer() const;
3477+
3478+
/// Retrieves the synthesized zero parameter default initializer for this
3479+
/// declaration, or \c nullptr if it doesn't have one.
3480+
ConstructorDecl *getDefaultInitializer() const;
3481+
34743482
// Implement isa/cast/dyncast/etc.
34753483
static bool classof(const Decl *D) {
34763484
return D->getKind() >= DeclKind::First_NominalTypeDecl &&

include/swift/AST/TypeCheckRequests.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,6 +1587,46 @@ class SynthesizeMemberwiseInitRequest
15871587
bool isCached() const { return true; }
15881588
};
15891589

1590+
/// Checks whether this type has a synthesized zero parameter default
1591+
/// initializer.
1592+
class HasDefaultInitRequest
1593+
: public SimpleRequest<HasDefaultInitRequest, bool(NominalTypeDecl *),
1594+
CacheKind::Cached> {
1595+
public:
1596+
using SimpleRequest::SimpleRequest;
1597+
1598+
private:
1599+
friend SimpleRequest;
1600+
1601+
// Evaluation.
1602+
llvm::Expected<bool> evaluate(Evaluator &evaluator,
1603+
NominalTypeDecl *decl) const;
1604+
1605+
public:
1606+
// Caching.
1607+
bool isCached() const { return true; }
1608+
};
1609+
1610+
/// Synthesizes a default initializer for a given type.
1611+
class SynthesizeDefaultInitRequest
1612+
: public SimpleRequest<SynthesizeDefaultInitRequest,
1613+
ConstructorDecl *(NominalTypeDecl *),
1614+
CacheKind::Cached> {
1615+
public:
1616+
using SimpleRequest::SimpleRequest;
1617+
1618+
private:
1619+
friend SimpleRequest;
1620+
1621+
// Evaluation.
1622+
llvm::Expected<ConstructorDecl *> evaluate(Evaluator &evaluator,
1623+
NominalTypeDecl *decl) const;
1624+
1625+
public:
1626+
// Caching.
1627+
bool isCached() const { return true; }
1628+
};
1629+
15901630
// Allow AnyValue to compare two Type values, even though Type doesn't
15911631
// support ==.
15921632
template<>

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,7 @@ SWIFT_REQUEST(TypeChecker, HasMemberwiseInitRequest,
173173
bool(StructDecl *), Cached, NoLocationInfo)
174174
SWIFT_REQUEST(TypeChecker, SynthesizeMemberwiseInitRequest,
175175
ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo)
176+
SWIFT_REQUEST(TypeChecker, HasDefaultInitRequest,
177+
bool(NominalTypeDecl *), Cached, NoLocationInfo)
178+
SWIFT_REQUEST(TypeChecker, SynthesizeDefaultInitRequest,
179+
ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo)

lib/AST/Decl.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3856,6 +3856,27 @@ ConstructorDecl *NominalTypeDecl::getMemberwiseInitializer() const {
38563856
ctx.evaluator, SynthesizeMemberwiseInitRequest{mutableThis}, nullptr);
38573857
}
38583858

3859+
bool NominalTypeDecl::hasDefaultInitializer() const {
3860+
// Currently only structs and classes can have default initializers.
3861+
if (!isa<StructDecl>(this) && !isa<ClassDecl>(this))
3862+
return false;
3863+
3864+
auto &ctx = getASTContext();
3865+
auto *mutableThis = const_cast<NominalTypeDecl *>(this);
3866+
return evaluateOrDefault(ctx.evaluator, HasDefaultInitRequest{mutableThis},
3867+
false);
3868+
}
3869+
3870+
ConstructorDecl *NominalTypeDecl::getDefaultInitializer() const {
3871+
if (!hasDefaultInitializer())
3872+
return nullptr;
3873+
3874+
auto &ctx = getASTContext();
3875+
auto *mutableThis = const_cast<NominalTypeDecl *>(this);
3876+
return evaluateOrDefault(ctx.evaluator,
3877+
SynthesizeDefaultInitRequest{mutableThis}, nullptr);
3878+
}
3879+
38593880
ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc,
38603881
MutableArrayRef<TypeLoc> Inherited,
38613882
GenericParamList *GenericParams, DeclContext *Parent)

lib/Sema/CodeSynthesis.cpp

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -864,26 +864,7 @@ static bool hasUserDefinedDesignatedInit(Evaluator &eval,
864864
false);
865865
}
866866

867-
static void addImplicitConstructorsToStruct(StructDecl *decl) {
868-
assert(!decl->hasClangNode() &&
869-
"ClangImporter is responsible for adding implicit constructors");
870-
assert(!decl->hasUnreferenceableStorage() &&
871-
"User-defined structs cannot have unreferenceable storage");
872-
873-
decl->setAddedImplicitInitializers();
874-
(void)decl->getMemberwiseInitializer();
875-
876-
// If the user has already defined a designated initializer, then don't
877-
// synthesize a default initializer.
878-
auto &ctx = decl->getASTContext();
879-
if (hasUserDefinedDesignatedInit(ctx.evaluator, decl))
880-
return;
881-
882-
if (areAllStoredPropertiesDefaultInitializable(ctx.evaluator, decl))
883-
TypeChecker::defineDefaultConstructor(decl);
884-
}
885-
886-
static void addImplicitConstructorsToClass(ClassDecl *decl) {
867+
static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) {
887868
// Bail out if we're validating one of our constructors already;
888869
// we'll revisit the issue later.
889870
if (!decl->hasClangNode()) {
@@ -1048,22 +1029,6 @@ static void addImplicitConstructorsToClass(ClassDecl *decl) {
10481029

10491030
return;
10501031
}
1051-
1052-
if (!foundDesignatedInit) {
1053-
// For a class with no superclass, automatically define a default
1054-
// constructor.
1055-
1056-
// ... unless there are uninitialized stored properties.
1057-
if (!defaultInitable)
1058-
return;
1059-
1060-
// Clang-imported types should never get a default constructor, just a
1061-
// memberwise one.
1062-
if (decl->hasClangNode())
1063-
return;
1064-
1065-
TypeChecker::defineDefaultConstructor(decl);
1066-
}
10671032
}
10681033

10691034
void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
@@ -1083,10 +1048,13 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
10831048
}
10841049
}
10851050

1086-
if (auto *structDecl = dyn_cast<StructDecl>(decl))
1087-
addImplicitConstructorsToStruct(structDecl);
10881051
if (auto *classDecl = dyn_cast<ClassDecl>(decl))
1089-
addImplicitConstructorsToClass(classDecl);
1052+
addImplicitInheritedConstructorsToClass(classDecl);
1053+
1054+
// Force the memberwise and default initializers if the type has them.
1055+
// FIXME: We need to be more lazy about synthesizing constructors.
1056+
(void)decl->getMemberwiseInitializer();
1057+
(void)decl->getDefaultInitializer();
10901058
}
10911059

10921060
void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target,
@@ -1218,6 +1186,37 @@ SynthesizeMemberwiseInitRequest::evaluate(Evaluator &evaluator,
12181186
return ctor;
12191187
}
12201188

1189+
llvm::Expected<bool>
1190+
HasDefaultInitRequest::evaluate(Evaluator &evaluator,
1191+
NominalTypeDecl *decl) const {
1192+
assert(isa<StructDecl>(decl) || isa<ClassDecl>(decl));
1193+
1194+
// Don't synthesize a default for imported decls.
1195+
if (decl->hasClangNode())
1196+
return false;
1197+
1198+
if (auto *sd = dyn_cast<StructDecl>(decl)) {
1199+
assert(!sd->hasUnreferenceableStorage() &&
1200+
"User-defined structs cannot have unreferenceable storage");
1201+
(void)sd;
1202+
}
1203+
1204+
// Don't synthesize a default for a subclass, it will attempt to inherit its
1205+
// initializers from its superclass.
1206+
if (auto *cd = dyn_cast<ClassDecl>(decl))
1207+
if (cd->getSuperclass())
1208+
return false;
1209+
1210+
// If the user has already defined a designated initializer, then don't
1211+
// synthesize a default init.
1212+
if (hasUserDefinedDesignatedInit(evaluator, decl))
1213+
return false;
1214+
1215+
// We can only synthesize a default init if all the stored properties have an
1216+
// initial value.
1217+
return areAllStoredPropertiesDefaultInitializable(evaluator, decl);
1218+
}
1219+
12211220
/// Synthesizer callback for a function body consisting of "return".
12221221
static std::pair<BraceStmt *, bool>
12231222
synthesizeSingleReturnFunctionBody(AbstractFunctionDecl *afd, void *) {
@@ -1228,7 +1227,9 @@ synthesizeSingleReturnFunctionBody(AbstractFunctionDecl *afd, void *) {
12281227
/*isTypeChecked=*/true };
12291228
}
12301229

1231-
void TypeChecker::defineDefaultConstructor(NominalTypeDecl *decl) {
1230+
llvm::Expected<ConstructorDecl *>
1231+
SynthesizeDefaultInitRequest::evaluate(Evaluator &evaluator,
1232+
NominalTypeDecl *decl) const {
12321233
auto &ctx = decl->getASTContext();
12331234

12341235
FrontendStatsTracer StatsTracer(ctx.Stats, "define-default-ctor", decl);
@@ -1245,4 +1246,5 @@ void TypeChecker::defineDefaultConstructor(NominalTypeDecl *decl) {
12451246

12461247
// Lazily synthesize an empty body for the default constructor.
12471248
ctor->setBodySynthesizer(synthesizeSingleReturnFunctionBody);
1249+
return ctor;
12481250
}

lib/Sema/TypeChecker.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,9 +1149,6 @@ class TypeChecker final : public LazyResolver {
11491149
Optional<Type> boolType;
11501150

11511151
public:
1152-
/// Define the default constructor for the given struct or class.
1153-
static void defineDefaultConstructor(NominalTypeDecl *decl);
1154-
11551152
/// Fold the given sequence expression into an (unchecked) expression
11561153
/// tree.
11571154
Expr *foldSequence(SequenceExpr *expr, DeclContext *dc);

0 commit comments

Comments
 (0)