Skip to content

Commit 20dc9a4

Browse files
committed
Sema: Fix circular validation between memberwise initializer and stored property
Fixes <rdar://problem/42704745>.
1 parent 7566f98 commit 20dc9a4

File tree

5 files changed

+48
-22
lines changed

5 files changed

+48
-22
lines changed

lib/Sema/CodeSynthesis.cpp

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,6 +1690,25 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var,
16901690
arg->setDefaultArgumentKind(DefaultArgumentKind::StoredProperty);
16911691
}
16921692

1693+
bool swift::isMemberwiseInitialized(VarDecl *var) {
1694+
// Implicit, computed, and static properties are not initialized.
1695+
// The exception is lazy properties, which due to batch mode we may or
1696+
// may not have yet finalized, so they may currently be "stored" or
1697+
// "computed" in the current AST state.
1698+
if (var->isImplicit() || var->isStatic())
1699+
return false;
1700+
1701+
if (!var->hasStorage() && !var->getAttrs().hasAttribute<LazyAttr>())
1702+
return false;
1703+
1704+
// Initialized 'let' properties have storage, but don't get an argument
1705+
// to the memberwise initializer since they already have an initial
1706+
// value that cannot be overridden.
1707+
if (var->isLet() && var->getParentInitializer())
1708+
return false;
1709+
1710+
return true;
1711+
}
16931712
/// Create an implicit struct or class constructor.
16941713
///
16951714
/// \param decl The struct or class for which a constructor will be created.
@@ -1716,23 +1735,10 @@ ConstructorDecl *swift::createImplicitConstructor(TypeChecker &tc,
17161735
auto var = dyn_cast<VarDecl>(member);
17171736
if (!var)
17181737
continue;
1719-
1720-
// Implicit, computed, and static properties are not initialized.
1721-
// The exception is lazy properties, which due to batch mode we may or
1722-
// may not have yet finalized, so they may currently be "stored" or
1723-
// "computed" in the current AST state.
1724-
if (var->isImplicit() || var->isStatic())
1725-
continue;
17261738

1727-
if (!var->hasStorage() && !var->getAttrs().hasAttribute<LazyAttr>())
1739+
if (!isMemberwiseInitialized(var))
17281740
continue;
17291741

1730-
// Initialized 'let' properties have storage, but don't get an argument
1731-
// to the memberwise initializer since they already have an initial
1732-
// value that cannot be overridden.
1733-
if (var->isLet() && var->getParentInitializer())
1734-
continue;
1735-
17361742
accessLevel = std::min(accessLevel, var->getFormalAccess());
17371743

17381744
tc.validateDecl(var);
@@ -1765,9 +1771,9 @@ ConstructorDecl *swift::createImplicitConstructor(TypeChecker &tc,
17651771
DeclName name(ctx, DeclBaseName::createConstructor(), paramList);
17661772
auto *ctor =
17671773
new (ctx) ConstructorDecl(name, Loc,
1768-
OTK_None, /*FailabilityLoc=*/SourceLoc(),
1769-
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
1770-
paramList, /*GenericParams=*/nullptr, decl);
1774+
OTK_None, /*FailabilityLoc=*/SourceLoc(),
1775+
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
1776+
paramList, /*GenericParams=*/nullptr, decl);
17711777

17721778
// Mark implicit.
17731779
ctor->setImplicit();

lib/Sema/CodeSynthesis.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ enum class ImplicitConstructorKind {
6969
Memberwise
7070
};
7171

72+
bool isMemberwiseInitialized(VarDecl *var);
73+
7274
/// Create an implicit struct or class constructor.
7375
///
7476
/// \param decl The struct or class for which a constructor will be created.

lib/Sema/TypeCheckDecl.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5048,8 +5048,8 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
50485048
}
50495049
}
50505050

5051-
// Bail out if we're validating one of our constructors already; we'll
5052-
// revisit the issue later.
5051+
// Bail out if we're validating one of our constructors or stored properties
5052+
// already; we'll revisit the issue later.
50535053
if (isa<ClassDecl>(decl)) {
50545054
for (auto member : decl->getMembers()) {
50555055
if (auto ctor = dyn_cast<ConstructorDecl>(member)) {
@@ -5060,13 +5060,25 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
50605060
}
50615061
}
50625062

5063+
if (isa<StructDecl>(decl)) {
5064+
for (auto member : decl->getMembers()) {
5065+
if (auto var = dyn_cast<VarDecl>(member)) {
5066+
if (!isMemberwiseInitialized(var))
5067+
continue;
5068+
5069+
validateDecl(var);
5070+
if (!var->hasValidSignature())
5071+
return;
5072+
}
5073+
}
5074+
}
5075+
50635076
decl->setAddedImplicitInitializers();
50645077

50655078
// Check whether there is a user-declared constructor or an instance
50665079
// variable.
50675080
bool FoundMemberwiseInitializedProperty = false;
50685081
bool SuppressDefaultInitializer = false;
5069-
bool SuppressMemberwiseInitializer = false;
50705082
bool FoundDesignatedInit = false;
50715083

50725084
SmallVector<std::pair<ValueDecl *, Type>, 4> declaredInitializers;
@@ -5110,7 +5122,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
51105122
}
51115123

51125124
if (auto var = dyn_cast<VarDecl>(member)) {
5113-
if (var->hasStorage() && !var->isStatic() && !var->isInvalid()) {
5125+
if (isMemberwiseInitialized(var)) {
51145126
// Initialized 'let' properties have storage, but don't get an argument
51155127
// to the memberwise initializer since they already have an initial
51165128
// value that cannot be overridden.
@@ -5165,7 +5177,7 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
51655177
assert(!structDecl->hasUnreferenceableStorage() &&
51665178
"User-defined structs cannot have unreferenceable storage");
51675179

5168-
if (!FoundDesignatedInit && !SuppressMemberwiseInitializer) {
5180+
if (!FoundDesignatedInit) {
51695181
// For a struct with memberwise initialized properties, we add a
51705182
// memberwise init.
51715183
if (FoundMemberwiseInitializedProperty) {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
struct S {
2+
var a = S() // expected-error {{'S' cannot be constructed because it has no accessible initializers}}
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s %S/Inputs/memberwise-init-multifile-other.swift
2+
3+
func f(_: S) {}

0 commit comments

Comments
 (0)