Skip to content

Commit 3692d20

Browse files
committed
Refactor tracking of constant initializers for variables.
Instead of framing the interface around whether the variable is an ICE (which is only interesting in C++98), primarily track whether the initializer is a constant initializer (which is interesting in all C++ language modes). No functionality change intended.
1 parent 0f0ff33 commit 3692d20

File tree

9 files changed

+104
-84
lines changed

9 files changed

+104
-84
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -803,14 +803,11 @@ struct EvaluatedStmt {
803803
/// Whether this statement is being evaluated.
804804
bool IsEvaluating : 1;
805805

806-
/// Whether we already checked whether this statement was an
807-
/// integral constant expression.
808-
bool CheckedICE : 1;
809-
810-
/// Whether this statement is an integral constant expression,
811-
/// or in C++11, whether the statement is a constant expression. Only
812-
/// valid if CheckedICE is true.
813-
bool IsICE : 1;
806+
/// Whether this variable is known to have constant initialization. This is
807+
/// currently only computed in C++, for static / thread storage duration
808+
/// variables that might have constant initialization and for variables that
809+
/// are usable in constant expressions.
810+
bool HasConstantInitialization : 1;
814811

815812
/// Whether this variable is known to have constant destruction. That is,
816813
/// whether running the destructor on the initial value is a side-effect
@@ -819,12 +816,18 @@ struct EvaluatedStmt {
819816
/// non-trivial.
820817
bool HasConstantDestruction : 1;
821818

819+
/// In C++98, whether the initializer is an ICE. This affects whether the
820+
/// variable is usable in constant expressions.
821+
bool HasICEInit : 1;
822+
bool CheckedForICEInit : 1;
823+
822824
Stmt *Value;
823825
APValue Evaluated;
824826

825827
EvaluatedStmt()
826-
: WasEvaluated(false), IsEvaluating(false), CheckedICE(false),
827-
IsICE(false), HasConstantDestruction(false) {}
828+
: WasEvaluated(false), IsEvaluating(false),
829+
HasConstantInitialization(false), HasConstantDestruction(false),
830+
HasICEInit(false), CheckedForICEInit(false) {}
828831
};
829832

830833
/// Represents a variable declaration or definition.
@@ -1284,25 +1287,29 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
12841287
/// Evaluate the destruction of this variable to determine if it constitutes
12851288
/// constant destruction.
12861289
///
1287-
/// \pre isInitICE()
1290+
/// \pre hasConstantInitialization()
12881291
/// \return \c true if this variable has constant destruction, \c false if
12891292
/// not.
12901293
bool evaluateDestruction(SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
12911294

1292-
/// Determines whether it is already known whether the
1293-
/// initializer is an integral constant expression or not.
1294-
bool isInitKnownICE() const;
1295-
1296-
/// Determines whether the initializer is an integral constant
1297-
/// expression, or in C++11, whether the initializer is a constant
1298-
/// expression.
1295+
/// Determine whether this variable has constant initialization.
12991296
///
1300-
/// \pre isInitKnownICE()
1301-
bool isInitICE() const;
1302-
1303-
/// Determine whether the value of the initializer attached to this
1304-
/// declaration is an integral constant expression.
1305-
bool checkInitIsICE(SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
1297+
/// This is only set in two cases: when the language semantics require
1298+
/// constant initialization (globals in C and some globals in C++), and when
1299+
/// the variable is usable in constant expressions (constexpr, const int, and
1300+
/// reference variables in C++).
1301+
bool hasConstantInitialization() const;
1302+
1303+
/// Determine whether the initializer of this variable is an integer constant
1304+
/// expression. For use in C++98, where this affects whether the variable is
1305+
/// usable in constant expressions.
1306+
bool hasICEInitializer(const ASTContext &Context) const;
1307+
1308+
/// Evaluate the initializer of this variable to determine whether it's a
1309+
/// constant initializer. Should only be called once, after completing the
1310+
/// definition of the variable.
1311+
bool checkForConstantInitialization(
1312+
SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
13061313

13071314
void setInitStyle(InitializationStyle Style) {
13081315
VarDeclBits.InitStyle = Style;

clang/lib/AST/ASTImporter.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,10 +2014,11 @@ Error ASTNodeImporter::ImportInitializer(VarDecl *From, VarDecl *To) {
20142014
return ToInitOrErr.takeError();
20152015

20162016
To->setInit(*ToInitOrErr);
2017-
if (From->isInitKnownICE()) {
2018-
EvaluatedStmt *Eval = To->ensureEvaluatedStmt();
2019-
Eval->CheckedICE = true;
2020-
Eval->IsICE = From->isInitICE();
2017+
if (EvaluatedStmt *FromEval = From->getEvaluatedStmt()) {
2018+
EvaluatedStmt *ToEval = To->ensureEvaluatedStmt();
2019+
ToEval->HasConstantInitialization = FromEval->HasConstantInitialization;
2020+
ToEval->HasConstantDestruction = FromEval->HasConstantDestruction;
2021+
// FIXME: Also import the initializer value.
20212022
}
20222023

20232024
// FIXME: Other bits to merge?

clang/lib/AST/Decl.cpp

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2325,7 +2325,16 @@ bool VarDecl::isUsableInConstantExpressions(const ASTContext &Context) const {
23252325
if (!DefVD->mightBeUsableInConstantExpressions(Context))
23262326
return false;
23272327
// ... and its initializer is a constant initializer.
2328-
return DefVD->isInitKnownICE() && DefVD->isInitICE();
2328+
if (!DefVD->hasConstantInitialization())
2329+
return false;
2330+
// C++98 [expr.const]p1:
2331+
// An integral constant-expression can involve only [...] const variables
2332+
// or static data members of integral or enumeration types initialized with
2333+
// [integer] constant expressions (dcl.init)
2334+
if (Context.getLangOpts().CPlusPlus && !Context.getLangOpts().CPlusPlus11 &&
2335+
!DefVD->hasICEInitializer(Context))
2336+
return false;
2337+
return true;
23292338
}
23302339

23312340
/// Convert the initializer for this declaration to the elaborated EvaluatedStmt
@@ -2399,49 +2408,47 @@ APValue *VarDecl::getEvaluatedValue() const {
23992408
return nullptr;
24002409
}
24012410

2402-
bool VarDecl::isInitKnownICE() const {
2403-
if (EvaluatedStmt *Eval = getEvaluatedStmt())
2404-
return Eval->CheckedICE;
2411+
bool VarDecl::hasICEInitializer(const ASTContext &Context) const {
2412+
const Expr *Init = getInit();
2413+
assert(Init && "no initializer");
24052414

2406-
return false;
2415+
EvaluatedStmt *Eval = ensureEvaluatedStmt();
2416+
if (!Eval->CheckedForICEInit) {
2417+
Eval->CheckedForICEInit = true;
2418+
Eval->HasICEInit = Init->isIntegerConstantExpr(Context);
2419+
}
2420+
return Eval->HasICEInit;
24072421
}
24082422

2409-
bool VarDecl::isInitICE() const {
2410-
assert(isInitKnownICE() &&
2411-
"Check whether we already know that the initializer is an ICE");
2412-
return Init.get<EvaluatedStmt *>()->IsICE;
2423+
bool VarDecl::hasConstantInitialization() const {
2424+
// In C, all globals (and only globals) have constant initialization.
2425+
if (hasGlobalStorage() && !getASTContext().getLangOpts().CPlusPlus)
2426+
return true;
2427+
2428+
// In C++, it depends on whether the evaluation at the point of definition
2429+
// was evaluatable as a constant initializer.
2430+
if (EvaluatedStmt *Eval = getEvaluatedStmt())
2431+
return Eval->HasConstantInitialization;
2432+
2433+
return false;
24132434
}
24142435

2415-
bool VarDecl::checkInitIsICE(
2436+
bool VarDecl::checkForConstantInitialization(
24162437
SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
24172438
EvaluatedStmt *Eval = ensureEvaluatedStmt();
2418-
assert(!Eval->CheckedICE &&
2419-
"should check whether var has constant init at most once");
24202439
// If we ask for the value before we know whether we have a constant
24212440
// initializer, we can compute the wrong value (for example, due to
24222441
// std::is_constant_evaluated()).
24232442
assert(!Eval->WasEvaluated &&
24242443
"already evaluated var value before checking for constant init");
2444+
assert(getASTContext().getLangOpts().CPlusPlus && "only meaningful in C++");
24252445

24262446
const auto *Init = cast<Expr>(Eval->Value);
24272447
assert(!Init->isValueDependent());
24282448

2429-
// In C++11, evaluate the initializer to check whether it's a constant
2430-
// expression.
2431-
if (getASTContext().getLangOpts().CPlusPlus11) {
2432-
Eval->IsICE = evaluateValue(Notes) && Notes.empty();
2433-
Eval->CheckedICE = true;
2434-
return Eval->IsICE;
2435-
}
2436-
2437-
// It's an ICE whether or not the definition we found is
2438-
// out-of-line. See DR 721 and the discussion in Clang PR
2439-
// 6206 for details.
2440-
2441-
Eval->IsICE = getType()->isIntegralOrEnumerationType() &&
2442-
Init->isIntegerConstantExpr(getASTContext());
2443-
Eval->CheckedICE = true;
2444-
return Eval->IsICE;
2449+
// Evaluate the initializer to check whether it's a constant expression.
2450+
Eval->HasConstantInitialization = evaluateValue(Notes) && Notes.empty();
2451+
return Eval->HasConstantInitialization;
24452452
}
24462453

24472454
bool VarDecl::isParameterPack() const {

clang/lib/AST/ExprConstant.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3281,12 +3281,16 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
32813281
// Check that the variable is actually usable in constant expressions. For a
32823282
// const integral variable or a reference, we might have a non-constant
32833283
// initializer that we can nonetheless evaluate the initializer for. Such
3284-
// variables are not usable in constant expressions.
3284+
// variables are not usable in constant expressions. In C++98, the
3285+
// initializer also syntactically needs to be an ICE.
32853286
//
3286-
// FIXME: It would be cleaner to check VD->isUsableInConstantExpressions
3287-
// here, but that regresses diagnostics for things like reading from a
3288-
// volatile constexpr variable.
3289-
if (VD->isInitKnownICE() && !VD->isInitICE()) {
3287+
// FIXME: We don't diagnose cases that aren't potentially usable in constant
3288+
// expressions here; doing so would regress diagnostics for things like
3289+
// reading from a volatile constexpr variable.
3290+
if ((!VD->hasConstantInitialization() &&
3291+
VD->mightBeUsableInConstantExpressions(Info.Ctx)) ||
3292+
(Info.getLangOpts().CPlusPlus && !Info.getLangOpts().CPlusPlus11 &&
3293+
!VD->hasICEInitializer(Info.Ctx))) {
32903294
Info.CCEDiag(E, diag::note_constexpr_var_init_non_constant, 1) << VD;
32913295
NoteLValueLocation(Info, Base);
32923296
}

clang/lib/CodeGen/ItaniumCXXABI.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
365365
// variable being constant-initialized in every translation unit if it's
366366
// constant-initialized in any translation unit, which isn't actually
367367
// guaranteed by the standard but is necessary for sanity.
368-
return InitDecl->isInitKnownICE() && InitDecl->isInitICE();
368+
return InitDecl->hasConstantInitialization();
369369
}
370370

371371
bool usesThreadWrapperFunction(const VarDecl *VD) const override {

clang/lib/Sema/SemaDecl.cpp

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12983,21 +12983,28 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
1298312983
// do this lazily, because the result might depend on things that change
1298412984
// later, such as which constexpr functions happen to be defined.
1298512985
SmallVector<PartialDiagnosticAt, 8> Notes;
12986-
bool HasConstInit = var->checkInitIsICE(Notes);
12987-
12988-
// Prior to C++11, in contexts where a constant initializer is required,
12989-
// additional kinds of constant expression are permitted beyond ICEs, as
12990-
// described in [expr.const]p2-6.
12991-
// FIXME: Stricter checking for these rules would be useful for constinit /
12992-
// -Wglobal-constructors.
12993-
if (!getLangOpts().CPlusPlus11 && !HasConstInit) {
12986+
bool HasConstInit;
12987+
if (!getLangOpts().CPlusPlus11) {
12988+
// Prior to C++11, in contexts where a constant initializer is required,
12989+
// the set of valid constant initializers is described by syntactic rules
12990+
// in [expr.const]p2-6.
12991+
// FIXME: Stricter checking for these rules would be useful for constinit /
12992+
// -Wglobal-constructors.
1299412993
HasConstInit = checkConstInit();
12995-
Notes.clear();
12996-
if (CacheCulprit) {
12994+
12995+
// Compute and cache the constant value, and remember that we have a
12996+
// constant initializer.
12997+
if (HasConstInit) {
12998+
(void)var->checkForConstantInitialization(Notes);
12999+
Notes.clear();
13000+
} else if (CacheCulprit) {
1299713001
Notes.emplace_back(CacheCulprit->getExprLoc(),
1299813002
PDiag(diag::note_invalid_subexpr_in_const_expr));
1299913003
Notes.back().second << CacheCulprit->getSourceRange();
1300013004
}
13005+
} else {
13006+
// Evaluate the initializer to see if it's a constant initializer.
13007+
HasConstInit = var->checkForConstantInitialization(Notes);
1300113008
}
1300213009

1300313010
if (HasConstInit) {

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,10 +1423,9 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) {
14231423

14241424
if (uint64_t Val = Record.readInt()) {
14251425
VD->setInit(Record.readExpr());
1426-
if (Val > 1) {
1426+
if (Val != 1) {
14271427
EvaluatedStmt *Eval = VD->ensureEvaluatedStmt();
1428-
Eval->CheckedICE = (Val & 2) != 0;
1429-
Eval->IsICE = (Val & 3) == 3;
1428+
Eval->HasConstantInitialization = (Val & 2) != 0;
14301429
Eval->HasConstantDestruction = (Val & 4) != 0;
14311430
}
14321431
}
@@ -4440,8 +4439,7 @@ void ASTDeclReader::UpdateDecl(Decl *D,
44404439
VD->setInit(Record.readExpr());
44414440
if (Val != 1) {
44424441
EvaluatedStmt *Eval = VD->ensureEvaluatedStmt();
4443-
Eval->CheckedICE = (Val & 2) != 0;
4444-
Eval->IsICE = (Val & 3) == 3;
4442+
Eval->HasConstantInitialization = (Val & 2) != 0;
44454443
Eval->HasConstantDestruction = (Val & 4) != 0;
44464444
}
44474445
}

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5747,15 +5747,11 @@ void ASTRecordWriter::AddVarDeclInit(const VarDecl *VD) {
57475747
return;
57485748
}
57495749

5750-
// Bottom two bits are as follows:
5751-
// 01 -- initializer not checked for ICE
5752-
// 10 -- initializer not ICE
5753-
// 11 -- initializer ICE
57545750
unsigned Val = 1;
57555751
if (EvaluatedStmt *ES = VD->getEvaluatedStmt()) {
5756-
if (ES->CheckedICE)
5757-
Val = 2 | ES->IsICE;
5752+
Val |= (ES->HasConstantInitialization ? 2 : 0);
57585753
Val |= (ES->HasConstantDestruction ? 4 : 0);
5754+
// FIXME: Also emit the constant initializer value.
57595755
}
57605756
push_back(Val);
57615757
writeStmtRef(Init);

clang/lib/Serialization/ASTWriterDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2187,7 +2187,7 @@ void ASTWriter::WriteDeclAbbrevs() {
21872187
Abv->Add(BitCodeAbbrevOp(0)); // ImplicitParamKind
21882188
Abv->Add(BitCodeAbbrevOp(0)); // EscapingByref
21892189
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage
2190-
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // IsInitICE (local)
2190+
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // HasConstant*
21912191
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // VarKind (local enum)
21922192
// Type Source Info
21932193
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));

0 commit comments

Comments
 (0)