Skip to content

Commit 8d6a55f

Browse files
authored
Merge pull request #16655 from davezarzycki/formalize_DeclIsBeingValidatedRAII
[AST] NFC: Formalize Decl validation tracking via RAII
2 parents 71656c7 + b29d278 commit 8d6a55f

14 files changed

+172
-135
lines changed

include/swift/AST/Decl.h

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -253,9 +253,11 @@ bool conflicting(ASTContext &ctx,
253253

254254
/// Decl - Base class for all declarations in Swift.
255255
class alignas(1 << DeclAlignInBits) Decl {
256+
public:
256257
enum class ValidationState {
257258
Unchecked,
258259
Checking,
260+
CheckingWithValidSignature,
259261
Checked,
260262
};
261263

@@ -796,33 +798,51 @@ class alignas(1 << DeclAlignInBits) Decl {
796798
Bits.Decl.EarlyAttrValidation = validated;
797799
}
798800

799-
/// Whether the declaration has a valid interface type and
800-
/// generic signature.
801-
bool isBeingValidated() const {
802-
return Bits.Decl.ValidationState == unsigned(ValidationState::Checking);
801+
/// Get the validation state.
802+
ValidationState getValidationState() const {
803+
return ValidationState(Bits.Decl.ValidationState);
803804
}
804805

805-
/// Toggle whether or not the declaration is being validated.
806-
void setIsBeingValidated(bool ibv = true) {
807-
if (ibv) {
808-
assert(Bits.Decl.ValidationState == unsigned(ValidationState::Unchecked));
809-
Bits.Decl.ValidationState = unsigned(ValidationState::Checking);
810-
} else {
811-
assert(Bits.Decl.ValidationState == unsigned(ValidationState::Checking));
812-
Bits.Decl.ValidationState = unsigned(ValidationState::Checked);
806+
private:
807+
friend class DeclValidationRAII;
808+
809+
/// Set the validation state.
810+
void setValidationState(ValidationState VS) {
811+
assert(VS > getValidationState() && "Validation is unidirectional");
812+
Bits.Decl.ValidationState = unsigned(VS);
813+
}
814+
815+
public:
816+
/// Whether the declaration is in the middle of validation or not.
817+
bool isBeingValidated() const {
818+
switch (getValidationState()) {
819+
case ValidationState::Unchecked:
820+
case ValidationState::Checked:
821+
return false;
822+
default:
823+
return true;
813824
}
814825
}
815826

827+
/// Update the validation state for the declaration to allow access to the
828+
/// generic signature.
829+
void setSignatureIsValidated() {
830+
assert(getValidationState() == ValidationState::Checking);
831+
setValidationState(ValidationState::CheckingWithValidSignature);
832+
}
833+
816834
bool hasValidationStarted() const {
817-
return Bits.Decl.ValidationState >= unsigned(ValidationState::Checking);
835+
return getValidationState() > ValidationState::Unchecked;
818836
}
819837

820-
/// Manually indicate that validation has started for the declaration.
838+
/// Manually indicate that validation is complete for the declaration. For
839+
/// example: during importing, code synthesis, or derived conformances.
821840
///
822-
/// This is implied by setIsBeingValidated(true) (i.e. starting validation)
823-
/// and so rarely needs to be called directly.
824-
void setValidationStarted() {
825-
if (Bits.Decl.ValidationState != unsigned(ValidationState::Checking))
841+
/// For normal code validation, please use DeclValidationRAII instead.
842+
///
843+
/// FIXME -- Everything should use DeclValidationRAII instead of this.
844+
void setValidationToChecked() {
845+
if (!isBeingValidated())
826846
Bits.Decl.ValidationState = unsigned(ValidationState::Checked);
827847
}
828848

@@ -924,6 +944,25 @@ class alignas(1 << DeclAlignInBits) Decl {
924944
}
925945
};
926946

947+
/// \brief Use RAII to track Decl validation progress and non-reentrancy.
948+
class DeclValidationRAII {
949+
Decl *D;
950+
951+
public:
952+
DeclValidationRAII(const DeclValidationRAII &) = delete;
953+
DeclValidationRAII(DeclValidationRAII &&) = delete;
954+
void operator =(const DeclValidationRAII &) = delete;
955+
void operator =(DeclValidationRAII &&) = delete;
956+
957+
DeclValidationRAII(Decl *decl) : D(decl) {
958+
D->setValidationState(Decl::ValidationState::Checking);
959+
}
960+
961+
~DeclValidationRAII() {
962+
D->setValidationState(Decl::ValidationState::Checked);
963+
}
964+
};
965+
927966
/// \brief Allocates memory for a Decl with the given \p baseSize. If necessary,
928967
/// it includes additional space immediately preceding the Decl for a ClangNode.
929968
/// \note \p baseSize does not need to include space for a ClangNode if
@@ -1677,9 +1716,9 @@ class ExtensionDecl final : public GenericContext, public Decl,
16771716
/// Retrieve one of the types listed in the "inherited" clause.
16781717
Type getInheritedType(unsigned index) const;
16791718

1680-
/// Whether we have fully checked the extension.
1719+
/// Whether we have fully checked the extension signature.
16811720
bool hasValidSignature() const {
1682-
return hasValidationStarted() && !isBeingValidated();
1721+
return getValidationState() > ValidationState::CheckingWithValidSignature;
16831722
}
16841723

16851724
bool hasDefaultAccessLevel() const {

lib/AST/Builtins.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ getBuiltinFunction(Identifier Id, ArrayRef<Type> argTypes, Type ResType,
164164
ParamDecl(VarDecl::Specifier::Default, SourceLoc(), SourceLoc(),
165165
Identifier(), SourceLoc(), Identifier(), argType, DC);
166166
PD->setInterfaceType(argType);
167-
PD->setValidationStarted();
167+
PD->setValidationToChecked();
168168
PD->setImplicit();
169169
params.push_back(PD);
170170
}
@@ -181,7 +181,7 @@ getBuiltinFunction(Identifier Id, ArrayRef<Type> argTypes, Type ResType,
181181
paramList,
182182
TypeLoc::withoutLoc(ResType), DC);
183183
FD->setInterfaceType(FnType);
184-
FD->setValidationStarted();
184+
FD->setValidationToChecked();
185185
FD->setImplicit();
186186
FD->setAccess(AccessLevel::Public);
187187
return FD;
@@ -228,7 +228,7 @@ getBuiltinGenericFunction(Identifier Id,
228228
Identifier(),
229229
paramType->getInOutObjectType(), DC);
230230
PD->setInterfaceType(paramIfaceType->getInOutObjectType());
231-
PD->setValidationStarted();
231+
PD->setValidationToChecked();
232232
PD->setImplicit();
233233
params.push_back(PD);
234234
}
@@ -246,7 +246,7 @@ getBuiltinGenericFunction(Identifier Id,
246246
TypeLoc::withoutLoc(ResBodyType), DC);
247247

248248
func->setInterfaceType(InterfaceType);
249-
func->setValidationStarted();
249+
func->setValidationToChecked();
250250
func->setGenericEnvironment(Env);
251251
func->setImplicit();
252252
func->setAccess(AccessLevel::Public);

lib/AST/Decl.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2123,7 +2123,11 @@ void ValueDecl::setInterfaceType(Type type) {
21232123
}
21242124

21252125
bool ValueDecl::hasValidSignature() const {
2126-
return hasInterfaceType() && !isBeingValidated();
2126+
if (!hasInterfaceType())
2127+
return false;
2128+
// FIXME -- The build blows up if the correct code is used:
2129+
// return getValidationState() > ValidationState::CheckingWithValidSignature;
2130+
return getValidationState() != ValidationState::Checking;
21272131
}
21282132

21292133
Optional<ObjCSelector> ValueDecl::getObjCRuntimeName() const {
@@ -2727,7 +2731,7 @@ SourceRange TypeAliasDecl::getSourceRange() const {
27272731
}
27282732

27292733
void TypeAliasDecl::setUnderlyingType(Type underlying) {
2730-
setValidationStarted();
2734+
setValidationToChecked();
27312735

27322736
// lldb creates global typealiases containing archetypes
27332737
// sometimes...
@@ -2945,7 +2949,7 @@ void ClassDecl::addImplicitDestructor() {
29452949
auto *DD = new (ctx) DestructorDecl(getLoc(), selfDecl, this);
29462950

29472951
DD->setImplicit();
2948-
DD->setValidationStarted();
2952+
DD->setValidationToChecked();
29492953

29502954
// Create an empty body for the destructor.
29512955
DD->setBody(BraceStmt::create(ctx, getLoc(), { }, getLoc(), true));
@@ -4527,7 +4531,7 @@ ParamDecl *ParamDecl::createSelf(SourceLoc loc, DeclContext *DC,
45274531
Identifier(), loc, C.Id_self, Type(), DC);
45284532
selfDecl->setImplicit();
45294533
selfDecl->setInterfaceType(selfInterfaceType);
4530-
selfDecl->setValidationStarted();
4534+
selfDecl->setValidationToChecked();
45314535
return selfDecl;
45324536
}
45334537

lib/AST/Module.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx)
360360
setInterfaceType(ModuleType::get(this));
361361

362362
// validateDecl() should return immediately given a ModuleDecl.
363-
setValidationStarted();
363+
setValidationToChecked();
364364

365365
setAccess(AccessLevel::Public);
366366
}

0 commit comments

Comments
 (0)