]> granicus.if.org Git - clang/commitdiff
constexpr handling improvements. Produce detailed diagnostics when a 'constexpr'
authorRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 19 Dec 2011 06:19:21 +0000 (06:19 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 19 Dec 2011 06:19:21 +0000 (06:19 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@146856 91177308-0d34-0410-b5e6-96231b3b80d8

20 files changed:
include/clang/AST/Decl.h
include/clang/AST/Expr.h
include/clang/Basic/DiagnosticASTKinds.td
lib/AST/ASTImporter.cpp
lib/AST/Decl.cpp
lib/AST/ExprConstant.cpp
lib/Sema/SemaDecl.cpp
lib/Serialization/ASTReaderDecl.cpp
lib/Serialization/ASTWriterDecl.cpp
test/CXX/basic/basic.types/p10.cpp
test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp
test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp
test/CXX/expr/expr.const/p2-0x.cpp
test/PCH/cxx-constexpr.cpp [new file with mode: 0644]
test/SemaCXX/constant-expression-cxx11.cpp
test/SemaCXX/constant-expression.cpp
test/SemaCXX/constexpr-depth.cpp
test/SemaCXX/constexpr-printing.cpp
test/SemaCXX/cxx0x-class.cpp
test/SemaCXX/i-c-e-cxx.cpp

index 92a6a5469c8de9ed56c61dfac3618559523016a5..0a24b72560f5c33df063cd4bfff01ce9cbd8a762 100644 (file)
@@ -664,8 +664,9 @@ struct EvaluatedStmt {
   /// integral constant expression.
   bool CheckingICE : 1;
 
-  /// \brief Whether this statement is an integral constant
-  /// expression. Only valid if CheckedICE is true.
+  /// \brief Whether this statement is an integral constant expression,
+  /// or in C++11, whether the statement is a constant expression. Only
+  /// valid if CheckedICE is true.
   bool IsICE : 1;
 
   Stmt *Value;
@@ -1019,41 +1020,13 @@ public:
   /// \endcode
   bool extendsLifetimeOfTemporary() const;
 
-  EvaluatedStmt *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;
-  }
-
-  /// \brief Check whether we are in the process of checking whether the
-  /// initializer can be evaluated.
-  bool isEvaluatingValue() const {
-    if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>())
-      return Eval->IsEvaluating;
-
-    return false;
-  }
+  EvaluatedStmt *ensureEvaluatedStmt() const;
 
-  /// \brief Note that we now are checking whether the initializer can be
-  /// evaluated.
-  void setEvaluatingValue() const {
-    EvaluatedStmt *Eval = EnsureEvaluatedStmt();
-    Eval->IsEvaluating = true;
-  }
-
-  /// \brief Note that constant evaluation has computed the given
-  /// value for this variable's initializer.
-  void setEvaluatedValue(const APValue &Value) const {
-    EvaluatedStmt *Eval = EnsureEvaluatedStmt();
-    Eval->IsEvaluating = false;
-    Eval->WasEvaluated = true;
-    Eval->Evaluated = Value;
-  }
+  /// \brief Attempt to evaluate the value of the initializer attached to this
+  /// declaration, and produce notes explaining why it cannot be evaluated or is
+  /// not a constant expression. Returns true if evaluation succeeded.
+  /// The value can be obtained by calling getEvaluatedValue.
+  bool evaluateValue(llvm::SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
 
   /// \brief Return the already-evaluated value of this variable's
   /// initializer, or NULL if the value is not yet known. Returns pointer
@@ -1075,8 +1048,9 @@ public:
     return false;
   }
 
-  /// \brief Determines whether the initializer is an integral
-  /// constant expression.
+  /// \brief Determines whether the initializer is an integral constant
+  /// expression, or in C++11, whether the initializer is a constant
+  /// expression.
   ///
   /// \pre isInitKnownICE()
   bool isInitICE() const {
@@ -1085,30 +1059,9 @@ public:
     return Init.get<EvaluatedStmt *>()->IsICE;
   }
 
-  /// \brief Check whether we are in the process of checking the initializer
-  /// is an integral constant expression.
-  bool isCheckingICE() const {
-    if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>())
-      return Eval->CheckingICE;
-
-    return false;
-  }
-
-  /// \brief Note that we now are checking whether the initializer is an
-  /// integral constant expression.
-  void setCheckingICE() const {
-    EvaluatedStmt *Eval = EnsureEvaluatedStmt();
-    Eval->CheckingICE = true;
-  }
-
-  /// \brief Note that we now know whether the initializer is an
-  /// integral constant expression.
-  void setInitKnownICE(bool IsICE) const {
-    EvaluatedStmt *Eval = EnsureEvaluatedStmt();
-    Eval->CheckingICE = false;
-    Eval->CheckedICE = true;
-    Eval->IsICE = IsICE;
-  }
+  /// \brief Determine whether the value of the initializer attached to this
+  /// declaration is an integral constant expression.
+  bool checkInitIsICE() const;
 
   void setCXXDirectInitializer(bool T) { VarDeclBits.HasCXXDirectInit = T; }
 
index ce541f63db314ac2a04bb715880b5dc4e1da03e4..9c86c16c4e7e53292d3a1a02812d0f530892292f 100644 (file)
@@ -495,6 +495,14 @@ public:
   /// lvalue with link time known address, with no side-effects.
   bool EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx) const;
 
+  /// EvaluateAsInitializer - Evaluate an expression as if it were the
+  /// initializer of the given declaration. Returns true if the initializer
+  /// can be folded to a constant, and produces any relevant notes. In C++11,
+  /// notes will be produced if the expression is not a constant expression.
+  bool EvaluateAsInitializer(APValue &Result, const ASTContext &Ctx,
+                             const VarDecl *VD,
+                       llvm::SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
+
   /// \brief Enumeration used to describe the kind of Null pointer constant
   /// returned from \c isNullPointerConstant().
   enum NullPointerConstantKind {
index ae6b59ca9db665865e7094744dd2f42dd7b3aec7..8f0b4938ad67f3cec172a3fc614fe12d5a0a4c10 100644 (file)
@@ -30,6 +30,8 @@ def note_constexpr_non_global : Note<
 def note_constexpr_past_end : Note<
   "dereferenced pointer past the end of %select{|subobject of}0 "
   "%select{temporary|%2}1 is not a constant expression">;
+def note_constexpr_var_init_non_constant : Note<
+  "initializer of %0 is not a constant expression">;
 def note_constexpr_temporary_here : Note<"temporary created here">;
 def note_constexpr_depth_limit_exceeded : Note<
   "constexpr evaluation exceeded maximum depth of %0 calls">;
index 963643e542a2d86c16eb5b00c42d3aca4c32cec6..a95c941e02eb274d0257cd4ddb44300ab1437698 100644 (file)
@@ -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();
+          }
         }
       }
       
index 34eefc0dcc9a00dce9ac5d0265ef2a5e5a82c4e4..305c41c51c46ba932ba2a75de126f2baca7b1969 100644 (file)
@@ -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());
 }
-
index 88a033151a85c0d3454b78beca037dbd1dfdb181..ff556c3094427ac41081ca44e8c11dcb5504010b 100644 (file)
@@ -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());
index a0291afeedb11598b7d606fe8dddbecfda649e00..c31850313ad47ab68ed4e7e9362dabbb0d8201ab 100644 (file)
@@ -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.
index 69f309aef0d0b99c4be2d626cad087ccb6c9cb53..3bcafe9d598a2e6318708e24d888ac2e4c37b6da 100644 (file)
@@ -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);
index 6c6428d55a806e94217785f334a43883b7f91489..26dfd36f77d4c327b2cd8249242a6c0dec564f1c 100644 (file)
@@ -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;
index 1f66e5522df8da7f9d0c64cb14e5ec657cd10e21..725be09c0850bec46d87d92e66f39af8a6423d9e 100644 (file)
@@ -34,13 +34,12 @@ constexpr int f(DerivedFromNonTrivDtor<NonTrivDtorBase>); // expected-error {{co
 struct TrivDtor {
   constexpr TrivDtor();
 };
-// FIXME: when building DefinitionData we look at 'isUserProvided' before it's set up!
-#if 0
+constexpr int f(TrivDtor);
 struct TrivDefaultedDtor {
   constexpr TrivDefaultedDtor();
   ~TrivDefaultedDtor() = default;
 };
-#endif
+constexpr int f(TrivDefaultedDtor);
 
 //  - it is an aggregate type or has at least one constexpr constructor or
 //    constexpr constructor template that is not a copy or move constructor
@@ -100,6 +99,7 @@ struct ArrGood {
   Agg agg[24];
   double d[12];
   TrivDtor td[3];
+  TrivDefaultedDtor tdd[3];
 };
 constexpr int f(ArrGood);
 
@@ -120,8 +120,7 @@ namespace MutableMembers {
   // Here's one reason why allowing this would be a disaster...
   template<int n> struct Id { int k = n; };
   int f() {
-    // FIXME: correctly check whether the initializer is a constant expression.
-    constexpr MM m = { 0 }; // desired-error {{must be a constant expression}}
+    constexpr MM m = { 0 }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-literal type 'const MutableMembers::MM' cannot be used in a constant expression}}
     ++m.n;
     return Id<m.n>().k; // expected-error {{not an integral constant expression}}
   }
index 3f6f160221dee09f9dd2b7b491e692eae96c47c3..0fb6f7def00a0b64a1828807d54be75c4804e61e 100644 (file)
@@ -20,7 +20,7 @@ constexpr int s1::mi2 = 0;
 // not a definition of an object
 constexpr extern int i2; // expected-error {{constexpr variable declaration must be a definition}}
 // not a literal type
-constexpr notlit nl1; // expected-error {{constexpr variable 'nl1' must be initialized by a constant expression}}
+constexpr notlit nl1; // expected-error {{constexpr variable 'nl1' must be initialized by a constant expression}} expected-note {{non-literal type 'const notlit' cannot be used in a constant expression}}
 // function parameters
 void f2(constexpr int i) {} // expected-error {{function parameter cannot be constexpr}}
 // non-static member
@@ -42,12 +42,13 @@ class C2 {} constexpr; // expected-error {{class cannot be marked constexpr}}
 struct S2 {} constexpr; // expected-error {{struct cannot be marked constexpr}}
 union U2 {} constexpr; // expected-error {{union cannot be marked constexpr}}
 enum E2 {} constexpr; // expected-error {{enum cannot be marked constexpr}}
-constexpr class C3 {} c3 = C3();
-constexpr struct S3 {} s3 = S3();
+// FIXME: Mark default constructors as 'constexpr' when appropriate.
+constexpr class C3 {} c3 = C3(); // unexpected-error {{must be initialized by a constant expression}} unexpected-note {{non-constexpr constructor}} unexpected-note {{here}}
+constexpr struct S3 {} s3 = S3(); // unexpected-error {{must be initialized by a constant expression}} unexpected-note {{non-constexpr constructor}} unexpected-note {{here}}
 constexpr union U3 {} u3 = {};
 constexpr enum E3 { V3 } e3 = V3;
-class C4 {} constexpr c4 = C4();
-struct S4 {} constexpr s4 = S4();
+class C4 {} constexpr c4 = C4(); // unexpected-error {{must be initialized by a constant expression}} unexpected-note {{non-constexpr constructor}} unexpected-note {{here}}
+struct S4 {} constexpr s4 = S4(); // unexpected-error {{must be initialized by a constant expression}} unexpected-note {{non-constexpr constructor}} unexpected-note {{here}}
 union U4 {} constexpr u4 = {};
 enum E4 { V4 } constexpr e4 = V4;
 constexpr int; // expected-error {{constexpr can only be used in variable and function declarations}}
@@ -67,7 +68,7 @@ struct ConstexprDtor {
 };
 
 // template stuff
-template <typename T> constexpr T ft(T t) { return t; }
+template <typename T> constexpr T ft(T t) { return t; } // unexpected-note {{here}}
 template <typename T> T gt(T t) { return t; }
 struct S {
   template<typename T> constexpr T f();
@@ -89,7 +90,7 @@ template <> char S::g() { return 0; } // expected-error {{no function template m
 template <> double S::g() const { return 0; } // ok
 
 // FIXME: The initializer is a constant expression.
-constexpr int i3 = ft(1); // unexpected-error {{must be initialized by a constant expression}}
+constexpr int i3 = ft(1); // unexpected-error {{must be initialized by a constant expression}} unexpected-note {{undefined function 'ft<int>'}}
 
 void test() {
   // ignore constexpr when instantiating with non-literal
@@ -98,7 +99,7 @@ void test() {
 }
 
 // Examples from the standard:
-constexpr int square(int x);
+constexpr int square(int x); // expected-note {{declared here}}
 constexpr int bufsz = 1024;
 
 constexpr struct pixel { // expected-error {{struct cannot be marked constexpr}}
@@ -108,10 +109,10 @@ constexpr struct pixel { // expected-error {{struct cannot be marked constexpr}}
 };
 
 constexpr pixel::pixel(int a)
-  : x(square(a)), y(square(a))
+  : x(square(a)), y(square(a)) // expected-note {{undefined function 'square' cannot be used in a constant expression}}
   { }
 
-constexpr pixel small(2); // expected-error {{must be initialized by a constant expression}}
+constexpr pixel small(2); // expected-error {{must be initialized by a constant expression}} expected-note {{in call to 'pixel(2)'}}
 
 constexpr int square(int x) {
   return x * x;
index fe79a0e6fc1dfa192453f1f0f92ad62c21b42aee..4145e9b22450ce855597376ef3a96b6a45c4b05a 100644 (file)
@@ -18,17 +18,17 @@ extern int (*const d)(int);
 // A variable declaration which uses the constexpr specifier shall have an
 // initializer and shall be initialized by a constant expression.
 constexpr int ni1; // expected-error {{default initialization of an object of const type 'const int'}}
-constexpr struct C { C(); } ni2; // expected-error {{constexpr variable 'ni2' must be initialized by a constant expression}}
+constexpr struct C { C(); } ni2; // expected-error {{constexpr variable 'ni2' must be initialized by a constant expression}} expected-note {{non-literal type 'const struct C' cannot be used in a constant expression}}
 constexpr double &ni3; // expected-error {{declaration of reference variable 'ni3' requires an initializer}}
 
 constexpr int nc1 = i; // expected-error {{constexpr variable 'nc1' must be initialized by a constant expression}}
-constexpr C nc2 = C(); // expected-error {{constexpr variable 'nc2' must be initialized by a constant expression}}
-int &f();
-constexpr int &nc3 = f(); // expected-error {{constexpr variable 'nc3' must be initialized by a constant expression}}
+constexpr C nc2 = C(); // expected-error {{constexpr variable 'nc2' must be initialized by a constant expression}} expected-note {{non-literal type}}
+int &f(); // expected-note {{declared here}}
+constexpr int &nc3 = f(); // expected-error {{constexpr variable 'nc3' must be initialized by a constant expression}} expected-note {{non-constexpr function 'f' cannot be used in a constant expression}}
 constexpr int nc4(i); // expected-error {{constexpr variable 'nc4' must be initialized by a constant expression}}
-constexpr C nc5((C())); // expected-error {{constexpr variable 'nc5' must be initialized by a constant expression}}
-int &f();
-constexpr int &nc6(f()); // expected-error {{constexpr variable 'nc6' must be initialized by a constant expression}}
+constexpr C nc5((C())); // expected-error {{constexpr variable 'nc5' must be initialized by a constant expression}} expected-note {{non-literal type 'const C'}}
+int &f(); // expected-note {{here}}
+constexpr int &nc6(f()); // expected-error {{constexpr variable 'nc6' must be initialized by a constant expression}} expected-note {{non-constexpr function 'f'}}
 
 struct pixel {
   int x, y;
index d02db92a26378be1f9084f161726fa5376f9e7b9..aa83fc7fa9911dccf05af0822059e553a8e6498a 100644 (file)
@@ -78,13 +78,13 @@ namespace NonConstExprReturn {
 namespace NonConstExprCtor {
   struct T {
     constexpr T(const int &r) :
-      r(r) { // expected-note {{reference to temporary cannot be used to initialize a member in a constant expression}}
+      r(r) { // expected-note 2{{reference to temporary cannot be used to initialize a member in a constant expression}}
     }
     const int &r;
   };
   constexpr int n = 0;
   constexpr T t1(n); // ok
-  constexpr T t2(0); // expected-error {{must be initialized by a constant expression}}
+  constexpr T t2(0); // expected-error {{must be initialized by a constant expression}} expected-note {{temporary created here}} expected-note {{in call to 'T(0)'}}
 
   struct S {
     int n : T(4).r; // expected-error {{constant expression}} expected-note {{temporary created here}} expected-note {{in call to 'T(4)'}}
@@ -187,8 +187,8 @@ namespace References {
   constexpr int e = 42;
   int &f = const_cast<int&>(e);
   extern int &g;
-  constexpr int &h(); // expected-note {{here}}
-  int &i = h();
+  constexpr int &h(); // expected-note 2{{here}}
+  int &i = h(); // expected-note {{here}} expected-note {{undefined function 'h' cannot be used in a constant expression}}
   constexpr int &j() { return b; }
   int &k = j();
 
@@ -202,7 +202,7 @@ namespace References {
     int F : f - 11;
     int G : g; // expected-error {{constant expression}}
     int H : h(); // expected-error {{constant expression}} expected-note {{undefined function 'h'}}
-    int I : i; // expected-error {{constant expression}}
+    int I : i; // expected-error {{constant expression}} expected-note {{initializer of 'i' is not a constant expression}}
     int J : j();
     int K : k;
   };
diff --git a/test/PCH/cxx-constexpr.cpp b/test/PCH/cxx-constexpr.cpp
new file mode 100644 (file)
index 0000000..8fe48f7
--- /dev/null
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -pedantic-errors -std=c++98 -emit-pch %s -o %t
+// RUN: %clang_cc1 -pedantic-errors -std=c++98 -include-pch %t -verify %s
+
+// RUN: %clang_cc1 -pedantic-errors -std=c++11 -emit-pch %s -o %t-cxx11
+// RUN: %clang_cc1 -pedantic-errors -std=c++11 -include-pch %t-cxx11 -verify %s
+
+#ifndef HEADER_INCLUDED
+
+#define HEADER_INCLUDED
+extern const int a;
+const int b = a;
+
+#else
+
+const int a = 5;
+typedef int T[b]; // expected-error {{variable length array}} expected-error {{must be an integer constant expression}}
+typedef int T[5];
+
+#endif
index 5b053e4ce6788ba43a1b8b6b26ab0fc4718696bf..94da73fcf6a853f7aa51434fe76491591e65c828 100644 (file)
@@ -8,9 +8,7 @@ static_assert(false, "test"); // expected-error {{test}}
 
 }
 
-// FIXME: support const T& parameters here.
-//template<typename T> constexpr T id(const T &t) { return t; }
-template<typename T> constexpr T id(T t) { return t; } // expected-note {{here}}
+template<typename T> constexpr T id(const T &t) { return t; } // expected-note {{here}}
 // FIXME: support templates here.
 //template<typename T> constexpr T min(const T &a, const T &b) {
 //  return a < b ? a : b;
@@ -104,8 +102,14 @@ namespace CaseStatements {
 }
 
 extern int &Recurse1;
-int &Recurse2 = Recurse1, &Recurse1 = Recurse2;
-constexpr int &Recurse3 = Recurse2; // expected-error {{must be initialized by a constant expression}}
+int &Recurse2 = Recurse1; // expected-note 2{{declared here}} expected-note {{initializer of 'Recurse1' is not a constant expression}}
+int &Recurse1 = Recurse2; // expected-note {{declared here}} expected-note {{initializer of 'Recurse2' is not a constant expression}}
+constexpr int &Recurse3 = Recurse2; // expected-error {{must be initialized by a constant expression}} expected-note {{initializer of 'Recurse2' is not a constant expression}}
+
+extern const int RecurseA;
+const int RecurseB = RecurseA; // expected-note {{declared here}}
+const int RecurseA = 10;
+constexpr int RecurseC = RecurseB; // expected-error {{must be initialized by a constant expression}} expected-note {{initializer of 'RecurseB' is not a constant expression}}
 
 namespace MemberEnum {
   struct WithMemberEnum {
@@ -188,26 +192,26 @@ namespace StaticMemberFunction {
 namespace ParameterScopes {
 
   const int k = 42;
-  constexpr const int &ObscureTheTruth(const int &a) { return a; }
-  constexpr const int &MaybeReturnJunk(bool b, const int a) {
-    return ObscureTheTruth(b ? a : k);
+  constexpr const int &ObscureTheTruth(const int &a) { return a; } // expected-note 3{{reference to 'a' cannot be returned from a constexpr function}}
+  constexpr const int &MaybeReturnJunk(bool b, const int a) { // expected-note 2{{declared here}}
+    return ObscureTheTruth(b ? a : k); // expected-note 2{{in call to 'ObscureTheTruth(a)'}}
   }
   static_assert(MaybeReturnJunk(false, 0) == 42, ""); // ok
-  constexpr int a = MaybeReturnJunk(true, 0); // expected-error {{constant expression}}
+  constexpr int a = MaybeReturnJunk(true, 0); // expected-error {{constant expression}} expected-note {{in call to 'MaybeReturnJunk(1, 0)'}}
 
-  constexpr const int MaybeReturnNonstaticRef(bool b, const int a) {
+  constexpr const int MaybeReturnNonstaticRef(bool b, const int a) { // expected-note {{here}}
     // If ObscureTheTruth returns a reference to 'a', the result is not a
     // constant expression even though 'a' is still in scope.
-    return ObscureTheTruth(b ? a : k);
+    return ObscureTheTruth(b ? a : k); // expected-note {{in call to 'ObscureTheTruth(a)'}}
   }
   static_assert(MaybeReturnNonstaticRef(false, 0) == 42, ""); // ok
-  constexpr int b = MaybeReturnNonstaticRef(true, 0); // expected-error {{constant expression}}
+  constexpr int b = MaybeReturnNonstaticRef(true, 0); // expected-error {{constant expression}} expected-note {{in call to 'MaybeReturnNonstaticRef(1, 0)'}}
 
   constexpr int InternalReturnJunk(int n) {
     // FIXME: We should reject this: it never produces a constant expression.
-    return MaybeReturnJunk(true, n);
+    return MaybeReturnJunk(true, n); // expected-note {{in call to 'MaybeReturnJunk(1, 0)'}}
   }
-  constexpr int n3 = InternalReturnJunk(0); // expected-error {{must be initialized by a constant expression}}
+  constexpr int n3 = InternalReturnJunk(0); // expected-error {{must be initialized by a constant expression}} expected-note {{in call to 'InternalReturnJunk(0)'}}
 
   constexpr int LToR(int &n) { return n; }
   constexpr int GrabCallersArgument(bool which, int a, int b) {
@@ -243,11 +247,11 @@ namespace FunctionPointers {
   constexpr auto Select(int n) -> int (*)(int) {
     return n == 2 ? &Double : n == 3 ? &Triple : n == 4 ? &Quadruple : 0;
   }
-  constexpr int Apply(int (*F)(int), int n) { return F(n); }
+  constexpr int Apply(int (*F)(int), int n) { return F(n); } // expected-note {{subexpression}}
 
   static_assert(1 + Apply(Select(4), 5) + Apply(Select(3), 7) == 42, "");
 
-  constexpr int Invalid = Apply(Select(0), 0); // expected-error {{must be initialized by a constant expression}}
+  constexpr int Invalid = Apply(Select(0), 0); // expected-error {{must be initialized by a constant expression}} expected-note {{in call to 'Apply(0, 0)'}}
 
 }
 
@@ -293,9 +297,7 @@ static_assert(&x < &x, "false"); // expected-error {{false}}
 static_assert(&x > &x, "false"); // expected-error {{false}}
 
 constexpr S* sptr = &s;
-// FIXME: This is not a constant expression; check we reject this and move this
-// test elsewhere.
-constexpr bool dyncast = sptr == dynamic_cast<S*>(sptr);
+constexpr bool dyncast = sptr == dynamic_cast<S*>(sptr); // expected-error {{constant expression}} expected-note {{dynamic_cast}}
 
 struct Str {
   // FIXME: In C++ mode, we should say 'integral' not 'integer'
@@ -323,8 +325,6 @@ struct Str {
 };
 
 extern char externalvar[];
-// FIXME: This is not a constant expression; check we reject this and move this
-// test elsewhere.
 constexpr bool constaddress = (void *)externalvar == (void *)0x4000UL; // expected-error {{must be initialized by a constant expression}}
 constexpr bool litaddress = "foo" == "foo"; // expected-error {{must be initialized by a constant expression}} expected-warning {{unspecified}}
 static_assert(0 != "foo", "");
@@ -509,21 +509,17 @@ struct D {
 };
 static_assert(D().c.n == 42, "");
 
-struct E {
-  constexpr E() : p(&p) {}
+struct E { // expected-note {{here}}
+  constexpr E() : p(&p) {} // expected-note {{pointer to temporary cannot be used to initialize a member in a constant expression}}
   void *p;
 };
-constexpr const E &e1 = E(); // expected-error {{constant expression}}
+constexpr const E &e1 = E(); // expected-error {{constant expression}} expected-note {{in call to 'E()'}} expected-note {{temporary created here}}
 // This is a constant expression if we elide the copy constructor call, and
 // is not a constant expression if we don't! But we do, so it is.
 // FIXME: The move constructor is not currently implicitly defined as constexpr.
-// We notice this when evaluating an expression which uses it, but not when
-// checking its initializer.
-constexpr E e2 = E(); // unexpected-error {{constant expression}}
-static_assert(e2.p == &e2.p, ""); // unexpected-error {{constant expression}} unexpected-note {{subexpression}}
-// FIXME: We don't pass through the fact that 'this' is ::e3 when checking the
-// initializer of this declaration.
-constexpr E e3; // unexpected-error {{constant expression}}
+constexpr E e2 = E(); // unexpected-error {{constant expression}} unexpected-note {{here}} unexpected-note {{non-constexpr constructor 'E' cannot be used in a constant expression}}
+static_assert(e2.p == &e2.p, ""); // unexpected-error {{constant expression}} unexpected-note {{initializer of 'e2' is not a constant expression}}
+constexpr E e3;
 static_assert(e3.p == &e3.p, "");
 
 extern const class F f;
@@ -531,7 +527,7 @@ struct F {
   constexpr F() : p(&f.p) {}
   const void *p;
 };
-constexpr F f = F();
+constexpr F f;
 
 struct G {
   struct T {
@@ -624,10 +620,7 @@ struct Base : Bottom {
 struct Base2 : Bottom {
   constexpr Base2(const int &r) : r(r) {}
   int q = 123;
-  // FIXME: When we track the global for which we are computing the initializer,
-  // use a reference here.
-  //const int &r;
-  int r;
+  const int &r;
 };
 struct Derived : Base, Base2 {
   constexpr Derived() : Base(76), Base2(a) {}
@@ -646,7 +639,7 @@ static_assert(derived.b[2] == 's', "");
 static_assert(derived.c == 76 + 'e', "");
 static_assert(derived.q == 123, "");
 static_assert(derived.r == 76, "");
-static_assert(&derived.r == &derived.a, ""); // expected-error {{}}
+static_assert(&derived.r == &derived.a, "");
 
 static_assert(!(derived == base), "");
 static_assert(derived == base2, "");
index 0367cc554687a5b0d6a16fa201b0bd6bc4d2c591..061e77591b1e7f5777583f61ab433542a2e9b5b5 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 -pedantic %s
 // C++ [expr.const]p1:
 //   In several places, C++ requires expressions that evaluate to an integral
 //   or enumeration constant: as array bounds, as case expressions, as
@@ -103,8 +103,17 @@ namespace IntOrEnum {
   S<p> s; // expected-error {{not an integral constant expression}}
 }
 
+extern const int recurse1;
+// recurse2 cannot be used in a constant expression because it is not
+// initialized by a constant expression. The same expression appearing later in
+// the TU would be a constant expression, but here it is not.
+const int recurse2 = recurse1;
+const int recurse1 = 1;
+int array1[recurse1]; // ok
+int array2[recurse2]; // expected-warning {{variable length array}} expected-warning {{integer constant expression}}
+
 namespace FloatConvert {
   typedef int a[(int)42.3];
   typedef int a[(int)42.997];
-  typedef int b[(int)4e10]; // expected-error {{variable length}}
+  typedef int b[(int)4e10]; // expected-warning {{variable length}} expected-error {{variable length}}
 }
index b8ae6682c5669988d0c095c84b4757746c7e745b..feba6fde3fbd7d9ab83c148ba17bca55e2e12978 100644 (file)
@@ -1,8 +1,8 @@
 // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s -DMAX=128 -fconstexpr-depth 128
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s -DMAX=1 -fconstexpr-depth 1
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s -DMAX=2 -fconstexpr-depth 2
 // RUN: %clang -std=c++11 -fsyntax-only -Xclang -verify %s -DMAX=10 -fconstexpr-depth=10
 
-constexpr int depth(int n) { return n > 1 ? depth(n-1) : 0; }
+constexpr int depth(int n) { return n > 1 ? depth(n-1) : 0; } // expected-note {{exceeded maximum depth}} expected-note +{{}}
 
-constexpr int kBad = depth(MAX + 1); // expected-error {{must be initialized by a constant expression}}
+constexpr int kBad = depth(MAX + 1); // expected-error {{must be initialized by a constant expression}} expected-note {{in call to 'depth(}}
 constexpr int kGood = depth(MAX);
index 341495ccecd4abc68379ac396f5f98e2cbe7755f..726d1770faefaecd9a767dd1b679e30f1f1a7055 100644 (file)
@@ -1,18 +1,19 @@
 // RUN: %clang_cc1 %s -std=c++11 -fsyntax-only -verify
 
-constexpr int extract(struct S &s);
+struct S;
+constexpr int extract(const S &s);
 
 struct S {
-  constexpr S() : n(extract(*this)), m(0) {}
+  constexpr S() : n(extract(*this)), m(0) {} // expected-note {{in call to 'extract(s1)'}}
   constexpr S(int k) : n(k), m(extract(*this)) {}
   int n, m;
 };
 
-constexpr int extract(S &s) { return s.n; }
+constexpr int extract(const S &s) { return s.n; } // expected-note {{subexpression}}
 
 // FIXME: once we produce notes for constexpr variable declarations, this should
 // produce a note indicating that S.n is used uninitialized.
-constexpr S s1; // expected-error {{constant expression}}
+constexpr S s1; // expected-error {{constant expression}} expected-note {{in call to 'S()'}}
 constexpr S s2(10);
 
 typedef __attribute__((vector_size(16))) int vector_int;
index 3527ccb55577588ab3ae3d8f1ea912f8b3f5996e..2ceacfc9b2fd9623439ab1f337d0b8d64941c3d8 100644 (file)
@@ -17,12 +17,12 @@ public:
 };
 
 namespace rdar8367341 {
-  float foo();
+  float foo(); // expected-note {{here}}
 
   struct A {
     static const float x = 5.0f; // expected-warning {{GNU extension}} expected-note {{use 'constexpr' specifier to silence this warning}}
     static const float y = foo(); // expected-warning {{GNU extension}} expected-note {{use 'constexpr' specifier to silence this warning}} expected-error {{in-class initializer is not a constant expression}}
     static constexpr float x2 = 5.0f;
-    static constexpr float y2 = foo(); // expected-error {{must be initialized by a constant expression}}
+    static constexpr float y2 = foo(); // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr function 'foo'}}
   };
 }
index ba60054f2e0077ba48e2ae29e1c0ad4f57084e1e..eae354814b514420f91f84a1b76c115530249e75 100644 (file)
@@ -16,9 +16,9 @@ void f() {
 }
 
 int a() {
-  const int t=t;
+  const int t=t; // expected-note {{declared here}}
   switch(1) { // expected-warning {{no case matching constant switch condition '1'}}
-    case t:; // expected-error {{not an integer constant expression}}
+    case t:; // expected-error {{not an integer constant expression}} expected-note {{initializer of 't' is not a constant expression}}
   }
 }