summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2011-12-19 06:19:21 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2011-12-19 06:19:21 +0000
commitd0b4dd656d11d60855bf5b78c7059a3a6c5724d4 (patch)
tree35d257022b6b7e00a6cf5e0cc038a8a1c7b588ac /clang/lib
parentdec792ebb5de0a6b9ff7b9f5ae25682c7031e529 (diff)
downloadbcm5719-llvm-d0b4dd656d11d60855bf5b78c7059a3a6c5724d4.tar.gz
bcm5719-llvm-d0b4dd656d11d60855bf5b78c7059a3a6c5724d4.zip
constexpr handling improvements. Produce detailed diagnostics when a 'constexpr'
variable is initialized by a non-constant expression, and pass in the variable being declared so that earlier-initialized fields' values can be used. Rearrange VarDecl init evaluation to make this possible, and in so doing fix a long-standing issue in our C++ constant expression handling, where we would mishandle cases like: extern const int a; const int n = a; const int a = 5; int arr[n]; Here, n is not initialized by a constant expression, so can't be used in an ICE, even though the initialization expression would be an ICE if it appeared later in the TU. This requires computing whether the initializer is an ICE eagerly, and saving that information in PCH files. llvm-svn: 146856
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/ASTImporter.cpp5
-rw-r--r--clang/lib/AST/Decl.cpp89
-rw-r--r--clang/lib/AST/ExprConstant.cpp119
-rw-r--r--clang/lib/Sema/SemaDecl.cpp40
-rw-r--r--clang/lib/Serialization/ASTReaderDecl.cpp8
-rw-r--r--clang/lib/Serialization/ASTWriterDecl.cpp7
6 files changed, 190 insertions, 78 deletions
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 963643e542a..a95c941e02e 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -2802,6 +2802,11 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) {
} else {
Expr *Init = Importer.Import(DDef->getInit());
MergeWithVar->setInit(Init);
+ if (DDef->isInitKnownICE()) {
+ EvaluatedStmt *Eval = MergeWithVar->ensureEvaluatedStmt();
+ Eval->CheckedICE = true;
+ Eval->IsICE = DDef->isInitICE();
+ }
}
}
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 34eefc0dcc9..305c41c51c4 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -1337,6 +1337,94 @@ void VarDecl::setInit(Expr *I) {
Init = I;
}
+/// Convert the initializer for this declaration to the elaborated EvaluatedStmt
+/// form, which contains extra information on the evaluated value of the
+/// initializer.
+EvaluatedStmt *VarDecl::ensureEvaluatedStmt() const {
+ EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>();
+ if (!Eval) {
+ Stmt *S = Init.get<Stmt *>();
+ Eval = new (getASTContext()) EvaluatedStmt;
+ Eval->Value = S;
+ Init = Eval;
+ }
+ return Eval;
+}
+
+bool VarDecl::evaluateValue(
+ llvm::SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
+ EvaluatedStmt *Eval = ensureEvaluatedStmt();
+
+ // We only produce notes indicating why an initializer is non-constant the
+ // first time it is evaluated. FIXME: The notes won't always be emitted the
+ // first time we try evaluation, so might not be produced at all.
+ if (Eval->WasEvaluated)
+ return !Eval->Evaluated.isUninit();
+
+ const Expr *Init = cast<Expr>(Eval->Value);
+ assert(!Init->isValueDependent());
+
+ if (Eval->IsEvaluating) {
+ // FIXME: Produce a diagnostic for self-initialization.
+ Eval->CheckedICE = true;
+ Eval->IsICE = false;
+ return false;
+ }
+
+ Eval->IsEvaluating = true;
+
+ bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, getASTContext(),
+ this, Notes);
+
+ // Ensure the result is an uninitialized APValue if evaluation fails.
+ if (!Result)
+ Eval->Evaluated = APValue();
+
+ Eval->IsEvaluating = false;
+ Eval->WasEvaluated = true;
+
+ // In C++11, we have determined whether the initializer was a constant
+ // expression as a side-effect.
+ if (getASTContext().getLangOptions().CPlusPlus0x && !Eval->CheckedICE) {
+ Eval->CheckedICE = true;
+ Eval->IsICE = Notes.empty();
+ }
+
+ return Result;
+}
+
+bool VarDecl::checkInitIsICE() const {
+ EvaluatedStmt *Eval = ensureEvaluatedStmt();
+ if (Eval->CheckedICE)
+ // We have already checked whether this subexpression is an
+ // integral constant expression.
+ return Eval->IsICE;
+
+ const Expr *Init = cast<Expr>(Eval->Value);
+ assert(!Init->isValueDependent());
+
+ // In C++11, evaluate the initializer to check whether it's a constant
+ // expression.
+ if (getASTContext().getLangOptions().CPlusPlus0x) {
+ llvm::SmallVector<PartialDiagnosticAt, 8> Notes;
+ evaluateValue(Notes);
+ return Eval->IsICE;
+ }
+
+ // It's an ICE whether or not the definition we found is
+ // out-of-line. See DR 721 and the discussion in Clang PR
+ // 6206 for details.
+
+ if (Eval->CheckingICE)
+ return false;
+ Eval->CheckingICE = true;
+
+ Eval->IsICE = Init->isIntegerConstantExpr(getASTContext());
+ Eval->CheckingICE = false;
+ Eval->CheckedICE = true;
+ return Eval->IsICE;
+}
+
bool VarDecl::extendsLifetimeOfTemporary() const {
assert(getType()->isReferenceType() &&"Non-references never extend lifetime");
@@ -2687,4 +2775,3 @@ SourceRange ImportDecl::getSourceRange() const {
return SourceRange(getLocation(), getIdentifierLocs().back());
}
-
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 88a033151a8..ff556c30944 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -382,6 +382,14 @@ namespace {
return OptionalDiagnostic();
return OptionalDiagnostic(&addDiag(Loc, DiagId));
}
+
+ /// Add a stack of notes to a prior diagnostic.
+ void addNotes(ArrayRef<PartialDiagnosticAt> Diags) {
+ if (HasActiveDiagnostic) {
+ EvalStatus.Diag->insert(EvalStatus.Diag->end(),
+ Diags.begin(), Diags.end());
+ }
+ }
};
}
@@ -1069,6 +1077,13 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,
return true;
}
+ // Dig out the initializer, and use the declaration which it's attached to.
+ const Expr *Init = VD->getAnyInitializer(VD);
+ if (!Init || Init->isValueDependent()) {
+ Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
+ return false;
+ }
+
// If we're currently evaluating the initializer of this declaration, use that
// in-flight value.
if (Info.EvaluatingDecl == VD) {
@@ -1083,47 +1098,23 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,
return false;
}
- const Expr *Init = VD->getAnyInitializer();
- if (!Init || Init->isValueDependent()) {
- Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
- return false;
- }
-
- if (APValue *V = VD->getEvaluatedValue()) {
- Result = CCValue(*V, CCValue::GlobalValue());
- return !Result.isUninit();
- }
-
- if (VD->isEvaluatingValue()) {
- Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
- return false;
- }
-
- VD->setEvaluatingValue();
-
- Expr::EvalStatus EStatus;
- EvalInfo InitInfo(Info.Ctx, EStatus);
- APValue EvalResult;
- InitInfo.setEvaluatingDecl(VD, EvalResult);
- LValue LVal;
- LVal.set(VD);
- // FIXME: The caller will need to know whether the value was a constant
- // expression. If not, we should propagate up a diagnostic.
- if (!EvaluateConstantExpression(EvalResult, InitInfo, LVal, Init)) {
- // FIXME: If the evaluation failure was not permanent (for instance, if we
- // hit a variable with no declaration yet, or a constexpr function with no
- // definition yet), the standard is unclear as to how we should behave.
- //
- // Either the initializer should be evaluated when the variable is defined,
- // or a failed evaluation of the initializer should be reattempted each time
- // it is used.
- VD->setEvaluatedValue(APValue());
- Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
+ // Check that we can fold the initializer. In C++, we will have already done
+ // this in the cases where it matters for conformance.
+ llvm::SmallVector<PartialDiagnosticAt, 8> Notes;
+ if (!VD->evaluateValue(Notes)) {
+ Info.Diag(E->getExprLoc(), diag::note_constexpr_var_init_non_constant,
+ Notes.size() + 1) << VD;
+ Info.Note(VD->getLocation(), diag::note_declared_at);
+ Info.addNotes(Notes);
return false;
+ } else if (!VD->checkInitIsICE()) {
+ Info.CCEDiag(E->getExprLoc(), diag::note_constexpr_var_init_non_constant,
+ Notes.size() + 1) << VD;
+ Info.Note(VD->getLocation(), diag::note_declared_at);
+ Info.addNotes(Notes);
}
- VD->setEvaluatedValue(EvalResult);
- Result = CCValue(EvalResult, CCValue::GlobalValue());
+ Result = CCValue(*VD->getEvaluatedValue(), CCValue::GlobalValue());
return true;
}
@@ -1523,6 +1514,8 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
if (Info.getLangOpts().CPlusPlus0x) {
const FunctionDecl *DiagDecl = Definition ? Definition : Declaration;
+ // FIXME: If DiagDecl is an implicitly-declared special member function, we
+ // should be much more explicit about why it's not constexpr.
Info.Diag(CallLoc, diag::note_constexpr_invalid_function, 1)
<< DiagDecl->isConstexpr() << isa<CXXConstructorDecl>(DiagDecl)
<< DiagDecl;
@@ -4887,6 +4880,22 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx) const {
CCEK_Constant);
}
+bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
+ const VarDecl *VD,
+ llvm::SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
+ Expr::EvalStatus EStatus;
+ EStatus.Diag = &Notes;
+
+ EvalInfo InitInfo(Ctx, EStatus);
+ InitInfo.setEvaluatingDecl(VD, Value);
+
+ LValue LVal;
+ LVal.set(VD);
+
+ return EvaluateConstantExpression(Value, InitInfo, LVal, this) &&
+ !EStatus.HasSideEffects;
+}
+
/// isEvaluatable - Call EvaluateAsRValue to see if this expression can be
/// constant folded, but discard the result.
bool Expr::isEvaluatable(const ASTContext &Ctx) const {
@@ -5083,33 +5092,13 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) {
if (!Dcl->getType()->isIntegralOrEnumerationType())
return ICEDiag(2, cast<DeclRefExpr>(E)->getLocation());
- // Look for a declaration of this variable that has an initializer.
- const VarDecl *ID = 0;
- const Expr *Init = Dcl->getAnyInitializer(ID);
- if (Init) {
- if (ID->isInitKnownICE()) {
- // We have already checked whether this subexpression is an
- // integral constant expression.
- if (ID->isInitICE())
- return NoDiag();
- else
- return ICEDiag(2, cast<DeclRefExpr>(E)->getLocation());
- }
-
- // It's an ICE whether or not the definition we found is
- // out-of-line. See DR 721 and the discussion in Clang PR
- // 6206 for details.
-
- if (Dcl->isCheckingICE()) {
- return ICEDiag(2, cast<DeclRefExpr>(E)->getLocation());
- }
-
- Dcl->setCheckingICE();
- ICEDiag Result = CheckICE(Init, Ctx);
- // Cache the result of the ICE test.
- Dcl->setInitKnownICE(Result.Val == 0);
- return Result;
- }
+ const VarDecl *VD;
+ // Look for a declaration of this variable that has an initializer, and
+ // check whether it is an ICE.
+ if (Dcl->getAnyInitializer(VD) && VD->checkInitIsICE())
+ return NoDiag();
+ else
+ return ICEDiag(2, cast<DeclRefExpr>(E)->getLocation());
}
}
return ICEDiag(2, E->getLocStart());
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a0291afeedb..c31850313ad 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -6539,17 +6539,39 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
Expr *Init = var->getInit();
bool IsGlobal = var->hasGlobalStorage() && !var->isStaticLocal();
- if (!var->getDeclContext()->isDependentContext() &&
- (var->isConstexpr() || IsGlobal) && Init &&
- !Init->isConstantInitializer(Context, baseType->isReferenceType())) {
- // FIXME: Improve this diagnostic to explain why the initializer is not
- // a constant expression.
- if (var->isConstexpr())
- Diag(var->getLocation(), diag::err_constexpr_var_requires_const_init)
- << var << Init->getSourceRange();
- if (IsGlobal)
+ if (!var->getDeclContext()->isDependentContext() && Init) {
+ if (IsGlobal && !var->isConstexpr() &&
+ getDiagnostics().getDiagnosticLevel(diag::warn_global_constructor,
+ var->getLocation())
+ != DiagnosticsEngine::Ignored &&
+ !Init->isConstantInitializer(Context, baseType->isReferenceType()))
Diag(var->getLocation(), diag::warn_global_constructor)
<< Init->getSourceRange();
+
+ QualType Type = var->getType();
+ if (var->isConstexpr()) {
+ llvm::SmallVector<PartialDiagnosticAt, 8> Notes;
+ if (!var->evaluateValue(Notes) || !var->isInitICE()) {
+ SourceLocation DiagLoc = var->getLocation();
+ // If the note doesn't add any useful information other than a source
+ // location, fold it into the primary diagnostic.
+ if (Notes.size() == 1 && Notes[0].second.getDiagID() ==
+ diag::note_invalid_subexpr_in_const_expr) {
+ DiagLoc = Notes[0].first;
+ Notes.clear();
+ }
+ Diag(DiagLoc, diag::err_constexpr_var_requires_const_init)
+ << var << Init->getSourceRange();
+ for (unsigned I = 0, N = Notes.size(); I != N; ++I)
+ Diag(Notes[I].first, Notes[I].second);
+ }
+ } else if (getLangOptions().CPlusPlus && !Type.isVolatileQualified() &&
+ Type.isConstQualified() && Type->isIntegralOrEnumerationType()) {
+ // Check whether the initializer of a const variable of integral or
+ // enumeration type is an ICE now, since we can't tell whether it was
+ // initialized by a constant expression if we check later.
+ var->checkInitIsICE();
+ }
}
// Require the destructor.
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 69f309aef0d..3bcafe9d598 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -799,8 +799,14 @@ void ASTDeclReader::VisitVarDecl(VarDecl *VD) {
VD->VarDeclBits.NRVOVariable = Record[Idx++];
VD->VarDeclBits.CXXForRangeDecl = Record[Idx++];
VD->VarDeclBits.ARCPseudoStrong = Record[Idx++];
- if (Record[Idx++])
+ if (uint64_t Val = Record[Idx++]) {
VD->setInit(Reader.ReadExpr(F));
+ if (Val > 1) {
+ EvaluatedStmt *Eval = VD->ensureEvaluatedStmt();
+ Eval->CheckedICE = true;
+ Eval->IsICE = Val == 3;
+ }
+ }
if (Record[Idx++]) { // HasMemberSpecializationInfo.
VarDecl *Tmpl = ReadDeclAs<VarDecl>(Record, Idx);
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 6c6428d55a8..26dfd36f77d 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -704,9 +704,12 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
Record.push_back(D->isNRVOVariable());
Record.push_back(D->isCXXForRangeDecl());
Record.push_back(D->isARCPseudoStrong());
- Record.push_back(D->getInit() ? 1 : 0);
- if (D->getInit())
+ if (D->getInit()) {
+ Record.push_back(!D->isInitKnownICE() ? 1 : (D->isInitICE() ? 3 : 2));
Writer.AddStmt(D->getInit());
+ } else {
+ Record.push_back(0);
+ }
MemberSpecializationInfo *SpecInfo
= D->isStaticDataMember() ? D->getMemberSpecializationInfo() : 0;
OpenPOWER on IntegriCloud