]> granicus.if.org Git - clang/commitdiff
C++11 constexpr: Add note stacks containing backtraces if constant evaluation
authorRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 16 Dec 2011 19:06:07 +0000 (19:06 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 16 Dec 2011 19:06:07 +0000 (19:06 +0000)
fails within a call to a constexpr function. Add -fconstexpr-backtrace-limit
argument to driver and frontend, to control the maximum number of notes so
produced (default 10). Fix APValue printing to be able to pretty-print all
APValue types, and move the testing for this functionality from a unittest to
a -verify test now that it's visible in clang's output.

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

21 files changed:
include/clang/AST/APValue.h
include/clang/Basic/Diagnostic.h
include/clang/Basic/DiagnosticASTKinds.td
include/clang/Driver/CC1Options.td
include/clang/Driver/Options.td
include/clang/Frontend/DiagnosticOptions.h
lib/AST/APValue.cpp
lib/AST/ExprConstant.cpp
lib/Basic/Diagnostic.cpp
lib/Driver/Tools.cpp
lib/Frontend/CompilerInvocation.cpp
lib/Frontend/Warnings.cpp
lib/Sema/SemaInit.cpp
test/CXX/expr/expr.const/p2-0x.cpp
test/SemaCXX/constant-expression-cxx11.cpp
test/SemaCXX/constexpr-backtrace-limit.cpp [new file with mode: 0644]
test/SemaCXX/constexpr-printing.cpp [new file with mode: 0644]
unittests/AST/APValueTest.cpp [deleted file]
unittests/AST/Makefile [deleted file]
unittests/CMakeLists.txt
unittests/Makefile

index cca00371c2d91dc033fe8e817f27b7c0cd0f350b..151956903c6810aca2e4dfd2981cf4c4b367ada9 100644 (file)
@@ -21,6 +21,7 @@
 #include "llvm/ADT/PointerUnion.h"
 
 namespace clang {
+  class ASTContext;
   class CharUnits;
   class DiagnosticBuilder;
   class Expr;
@@ -28,6 +29,7 @@ namespace clang {
   class Decl;
   class ValueDecl;
   class CXXRecordDecl;
+  class QualType;
 
 /// APValue - This class implements a discriminated union of [uninitialized]
 /// [APSInt] [APFloat], [Complex APSInt] [Complex APFloat], [Expr + Offset],
@@ -171,8 +173,11 @@ public:
   bool isUnion() const { return Kind == Union; }
   bool isMemberPointer() const { return Kind == MemberPointer; }
 
-  void print(raw_ostream &OS) const;
   void dump() const;
+  void dump(raw_ostream &OS) const;
+
+  void printPretty(raw_ostream &OS, ASTContext &Ctx, QualType Ty) const;
+  std::string getAsString(ASTContext &Ctx, QualType Ty) const;
 
   APSInt &getInt() {
     assert(isInt() && "Invalid accessor");
@@ -394,15 +399,6 @@ private:
                          ArrayRef<const CXXRecordDecl*> Path);
 };
 
-inline raw_ostream &operator<<(raw_ostream &OS, const APValue &V) {
-  V.print(OS);
-  return OS;
-}
-
-// Writes a concise representation of V to DB, in a single << operation.
-const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
-                                    const APValue &V);
-
 } // end namespace clang.
 
 #endif
index 6fd81b05ba5eea4fda8aa8741314fc930febb8c9..e6f30b17dc5b085fa14b35d04f5bbfdf293b76dd 100644 (file)
@@ -158,6 +158,8 @@ private:
   unsigned ErrorLimit;           // Cap of # errors emitted, 0 -> no limit.
   unsigned TemplateBacktraceLimit; // Cap on depth of template backtrace stack,
                                    // 0 -> no limit.
+  unsigned ConstexprBacktraceLimit; // Cap on depth of constexpr evaluation
+                                    // backtrace stack, 0 -> no limit.
   ExtensionHandling ExtBehavior; // Map extensions onto warnings or errors?
   llvm::IntrusiveRefCntPtr<DiagnosticIDs> Diags;
   DiagnosticConsumer *Client;
@@ -363,13 +365,25 @@ public:
   void setTemplateBacktraceLimit(unsigned Limit) {
     TemplateBacktraceLimit = Limit;
   }
-  
+
   /// \brief Retrieve the maximum number of template instantiation
-  /// nodes to emit along with a given diagnostic.
+  /// notes to emit along with a given diagnostic.
   unsigned getTemplateBacktraceLimit() const {
     return TemplateBacktraceLimit;
   }
-  
+
+  /// \brief Specify the maximum number of constexpr evaluation
+  /// notes to emit along with a given diagnostic.
+  void setConstexprBacktraceLimit(unsigned Limit) {
+    ConstexprBacktraceLimit = Limit;
+  }
+
+  /// \brief Retrieve the maximum number of constexpr evaluation
+  /// notes to emit along with a given diagnostic.
+  unsigned getConstexprBacktraceLimit() const {
+    return ConstexprBacktraceLimit;
+  }
+
   /// setIgnoreAllWarnings - When set to true, any unmapped warnings are
   /// ignored.  If this and WarningsAsErrors are both set, then this one wins.
   void setIgnoreAllWarnings(bool Val) { IgnoreAllWarnings = Val; }
index e9b31475219161fd352b2f7b0e53aac7cc724b77..ae6b59ca9db665865e7094744dd2f42dd7b3aec7 100644 (file)
@@ -33,6 +33,10 @@ def note_constexpr_past_end : Note<
 def note_constexpr_temporary_here : Note<"temporary created here">;
 def note_constexpr_depth_limit_exceeded : Note<
   "constexpr evaluation exceeded maximum depth of %0 calls">;
+def note_constexpr_calls_suppressed : Note<
+  "(skipping %0 call%s0 in backtrace; use -fconstexpr-backtrace-limit=0 to "
+  "see all)">;
+def note_constexpr_call_here : Note<"in call to '%0'">;
 
 // inline asm related.
 let CategoryName = "Inline Assembly Issue" in {
index e8c5398dec8410f6b857f975353618857e636641..d275af24f2f38d8268889421c2fc35192cd269fa 100644 (file)
@@ -291,6 +291,8 @@ def fmacro_backtrace_limit : Separate<"-fmacro-backtrace-limit">, MetaVarName<"<
   HelpText<"Set the maximum number of entries to print in a macro expansion backtrace (0 = no limit).">;
 def ftemplate_backtrace_limit : Separate<"-ftemplate-backtrace-limit">, MetaVarName<"<N>">,
   HelpText<"Set the maximum number of entries to print in a template instantiation backtrace (0 = no limit).">;
+def fconstexpr_backtrace_limit : Separate<"-fconstexpr-backtrace-limit">, MetaVarName<"<N>">,
+  HelpText<"Set the maximum number of entries to print in a constexpr evaluation backtrace (0 = no limit).">;
 def fmessage_length : Separate<"-fmessage-length">, MetaVarName<"<N>">,
   HelpText<"Format message diagnostics so that they fit within N columns or fewer, when possible.">;
 def fcolor_diagnostics : Flag<"-fcolor-diagnostics">,
index 730a08d3d84a59989cec7a3f12ccb32b2100fcf6..745ee193cd4286073acee552b326e9e1177ac7dd 100644 (file)
@@ -290,6 +290,9 @@ def fcommon : Flag<"-fcommon">, Group<f_Group>;
 def fcompile_resource_EQ : Joined<"-fcompile-resource=">, Group<f_Group>;
 def fconstant_cfstrings : Flag<"-fconstant-cfstrings">, Group<f_Group>;
 def fconstant_string_class_EQ : Joined<"-fconstant-string-class=">, Group<f_Group>;
+def fconstexpr_depth_EQ : Joined<"-fconstexpr-depth=">, Group<f_Group>;
+def fconstexpr_backtrace_limit_EQ : Joined<"-fconstexpr-backtrace-limit=">,
+                                    Group<f_Group>;
 def fcreate_profile : Flag<"-fcreate-profile">, Group<f_Group>;
 def fcxx_exceptions: Flag<"-fcxx-exceptions">, Group<f_Group>;
 def fdebug_pass_arguments : Flag<"-fdebug-pass-arguments">, Group<f_Group>;
@@ -490,7 +493,6 @@ def fsyntax_only : Flag<"-fsyntax-only">, Flags<[DriverOption]>;
 def ftabstop_EQ : Joined<"-ftabstop=">, Group<f_Group>;
 def ftemplate_depth_EQ : Joined<"-ftemplate-depth=">, Group<f_Group>;
 def ftemplate_depth_ : Joined<"-ftemplate-depth-">, Group<f_Group>;
-def fconstexpr_depth_EQ : Joined<"-fconstexpr-depth=">, Group<f_Group>;
 def ftemplate_backtrace_limit_EQ : Joined<"-ftemplate-backtrace-limit=">,
                                    Group<f_Group>;
 def ftest_coverage : Flag<"-ftest-coverage">, Group<f_Group>;
index 8433da4fa4391f14386996ec2def8753275d79ef..282ca5d3668239984f2d99e4892dc5897b95616a 100644 (file)
@@ -51,12 +51,14 @@ public:
   unsigned ErrorLimit;           /// Limit # errors emitted.
   unsigned MacroBacktraceLimit;  /// Limit depth of macro expansion backtrace.
   unsigned TemplateBacktraceLimit; /// Limit depth of instantiation backtrace.
+  unsigned ConstexprBacktraceLimit; /// Limit depth of constexpr backtrace.
 
   /// The distance between tab stops.
   unsigned TabStop;
   enum { DefaultTabStop = 8, MaxTabStop = 100, 
          DefaultMacroBacktraceLimit = 6,
-         DefaultTemplateBacktraceLimit = 10 };
+         DefaultTemplateBacktraceLimit = 10,
+         DefaultConstexprBacktraceLimit = 10 };
 
   /// Column limit for formatting message diagnostics, or 0 if unused.
   unsigned MessageLength;
@@ -99,6 +101,7 @@ public:
     ErrorLimit = 0;
     TemplateBacktraceLimit = DefaultTemplateBacktraceLimit;
     MacroBacktraceLimit = DefaultMacroBacktraceLimit;
+    ConstexprBacktraceLimit = DefaultConstexprBacktraceLimit;
   }
 };
 
index b99a66267cd6b46e24f605b17149c9e498ce9e00..0d4dea82cc871249e2b19059bb1f19c4e0e521fc 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #include "clang/AST/APValue.h"
+#include "clang/AST/ASTContext.h"
 #include "clang/AST/CharUnits.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Type.h"
 #include "clang/Basic/Diagnostic.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/raw_ostream.h"
@@ -203,7 +207,7 @@ void APValue::MakeUninit() {
 }
 
 void APValue::dump() const {
-  print(llvm::errs());
+  dump(llvm::errs());
   llvm::errs() << '\n';
 }
 
@@ -215,7 +219,7 @@ static double GetApproxValue(const llvm::APFloat &F) {
   return V.convertToDouble();
 }
 
-void APValue::print(raw_ostream &OS) const {
+void APValue::dump(raw_ostream &OS) const {
   switch (getKind()) {
   case Uninitialized:
     OS << "Uninitialized";
@@ -227,9 +231,12 @@ void APValue::print(raw_ostream &OS) const {
     OS << "Float: " << GetApproxValue(getFloat());
     return;
   case Vector:
-    OS << "Vector: " << getVectorElt(0);
-    for (unsigned i = 1; i != getVectorLength(); ++i)
-      OS << ", " << getVectorElt(i);
+    OS << "Vector: ";
+    getVectorElt(0).dump(OS);
+    for (unsigned i = 1; i != getVectorLength(); ++i) {
+      OS << ", ";
+      getVectorElt(i).dump(OS);
+    }
     return;
   case ComplexInt:
     OS << "ComplexInt: " << getComplexIntReal() << ", " << getComplexIntImag();
@@ -244,28 +251,36 @@ void APValue::print(raw_ostream &OS) const {
   case Array:
     OS << "Array: ";
     for (unsigned I = 0, N = getArrayInitializedElts(); I != N; ++I) {
-      OS << getArrayInitializedElt(I);
+      getArrayInitializedElt(I).dump(OS);
       if (I != getArraySize() - 1) OS << ", ";
     }
-    if (hasArrayFiller())
-      OS << getArraySize() - getArrayInitializedElts() << " x "
-         << getArrayFiller();
+    if (hasArrayFiller()) {
+      OS << getArraySize() - getArrayInitializedElts() << " x ";
+      getArrayFiller().dump(OS);
+    }
     return;
   case Struct:
     OS << "Struct ";
     if (unsigned N = getStructNumBases()) {
-      OS << " bases: " << getStructBase(0);
-      for (unsigned I = 1; I != N; ++I)
-        OS << ", " << getStructBase(I);
+      OS << " bases: ";
+      getStructBase(0).dump(OS);
+      for (unsigned I = 1; I != N; ++I) {
+        OS << ", ";
+        getStructBase(I).dump(OS);
+      }
     }
     if (unsigned N = getStructNumFields()) {
-      OS << " fields: " << getStructField(0);
-      for (unsigned I = 1; I != N; ++I)
-        OS << ", " << getStructField(I);
+      OS << " fields: ";
+      getStructField(0).dump(OS);
+      for (unsigned I = 1; I != N; ++I) {
+        OS << ", ";
+        getStructField(I).dump(OS);
+      }
     }
     return;
   case Union:
-    OS << "Union: " << getUnionValue();
+    OS << "Union: ";
+    getUnionValue().dump(OS);
     return;
   case MemberPointer:
     OS << "MemberPointer: <todo>";
@@ -274,78 +289,198 @@ void APValue::print(raw_ostream &OS) const {
   llvm_unreachable("Unknown APValue kind!");
 }
 
-static void WriteShortAPValueToStream(raw_ostream& Out,
-                                      const APValue& V) {
-  switch (V.getKind()) {
+void APValue::printPretty(raw_ostream &Out, ASTContext &Ctx, QualType Ty) const{
+  switch (getKind()) {
   case APValue::Uninitialized:
-    Out << "Uninitialized";
+    Out << "<uninitialized>";
     return;
   case APValue::Int:
-    Out << V.getInt();
+    Out << getInt();
     return;
   case APValue::Float:
-    Out << GetApproxValue(V.getFloat());
+    Out << GetApproxValue(getFloat());
     return;
-  case APValue::Vector:
-    Out << '[';
-    WriteShortAPValueToStream(Out, V.getVectorElt(0));
-    for (unsigned i = 1; i != V.getVectorLength(); ++i) {
+  case APValue::Vector: {
+    Out << '{';
+    QualType ElemTy = Ty->getAs<VectorType>()->getElementType();
+    getVectorElt(0).printPretty(Out, Ctx, ElemTy);
+    for (unsigned i = 1; i != getVectorLength(); ++i) {
       Out << ", ";
-      WriteShortAPValueToStream(Out, V.getVectorElt(i));
+      getVectorElt(i).printPretty(Out, Ctx, ElemTy);
     }
-    Out << ']';
+    Out << '}';
     return;
+  }
   case APValue::ComplexInt:
-    Out << V.getComplexIntReal() << "+" << V.getComplexIntImag() << "i";
+    Out << getComplexIntReal() << "+" << getComplexIntImag() << "i";
     return;
   case APValue::ComplexFloat:
-    Out << GetApproxValue(V.getComplexFloatReal()) << "+"
-        << GetApproxValue(V.getComplexFloatImag()) << "i";
+    Out << GetApproxValue(getComplexFloatReal()) << "+"
+        << GetApproxValue(getComplexFloatImag()) << "i";
     return;
-  case APValue::LValue:
-    Out << "LValue: <todo>";
+  case APValue::LValue: {
+    LValueBase Base = getLValueBase();
+    if (!Base) {
+      Out << "0";
+      return;
+    }
+
+    bool IsReference = Ty->isReferenceType();
+    QualType InnerTy
+      = IsReference ? Ty.getNonReferenceType() : Ty->getPointeeType();
+
+    if (!hasLValuePath()) {
+      // No lvalue path: just print the offset.
+      CharUnits O = getLValueOffset();
+      CharUnits S = Ctx.getTypeSizeInChars(InnerTy);
+      if (!O.isZero()) {
+        if (IsReference)
+          Out << "*(";
+        if (O % S) {
+          Out << "(char*)";
+          S = CharUnits::One();
+        }
+        Out << '&';
+      } else if (!IsReference)
+        Out << '&';
+
+      if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>())
+        Out << *VD;
+      else
+        Base.get<const Expr*>()->printPretty(Out, Ctx, 0,
+                                             Ctx.getPrintingPolicy());
+      if (!O.isZero()) {
+        Out << " + " << (O / S);
+        if (IsReference)
+          Out << ')';
+      }
+      return;
+    }
+
+    // We have an lvalue path. Print it out nicely.
+    if (!IsReference)
+      Out << '&';
+    else if (isLValueOnePastTheEnd())
+      Out << "*(&";
+
+    QualType ElemTy;
+    if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) {
+      Out << *VD;
+      ElemTy = VD->getType();
+    } else {
+      const Expr *E = Base.get<const Expr*>();
+      E->printPretty(Out, Ctx, 0,Ctx.getPrintingPolicy());
+      ElemTy = E->getType();
+    }
+
+    ArrayRef<LValuePathEntry> Path = getLValuePath();
+    const CXXRecordDecl *CastToBase = 0;
+    for (unsigned I = 0, N = Path.size(); I != N; ++I) {
+      if (ElemTy->getAs<RecordType>()) {
+        // The lvalue refers to a class type, so the next path entry is a base
+        // or member.
+        const Decl *BaseOrMember =
+        BaseOrMemberType::getFromOpaqueValue(Path[I].BaseOrMember).getPointer();
+        if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(BaseOrMember)) {
+          CastToBase = RD;
+          ElemTy = Ctx.getRecordType(RD);
+        } else {
+          const ValueDecl *VD = cast<ValueDecl>(BaseOrMember);
+          Out << ".";
+          if (CastToBase)
+            Out << *CastToBase << "::";
+          Out << *VD;
+          ElemTy = VD->getType();
+        }
+      } else {
+        // The lvalue must refer to an array.
+        Out << '[' << Path[I].ArrayIndex << ']';
+        ElemTy = Ctx.getAsArrayType(ElemTy)->getElementType();
+      }
+    }
+
+    // Handle formatting of one-past-the-end lvalues.
+    if (isLValueOnePastTheEnd()) {
+      // FIXME: If CastToBase is non-0, we should prefix the output with
+      // "(CastToBase*)".
+      Out << " + 1";
+      if (IsReference)
+        Out << ')';
+    }
     return;
-  case APValue::Array:
+  }
+  case APValue::Array: {
+    const ArrayType *AT = Ctx.getAsArrayType(Ty);
+    QualType ElemTy = AT->getElementType();
     Out << '{';
-    if (unsigned N = V.getArrayInitializedElts()) {
-      Out << V.getArrayInitializedElt(0);
-      for (unsigned I = 1; I != N; ++I)
-        Out << ", " << V.getArrayInitializedElt(I);
+    if (unsigned N = getArrayInitializedElts()) {
+      getArrayInitializedElt(0).printPretty(Out, Ctx, ElemTy);
+      for (unsigned I = 1; I != N; ++I) {
+        Out << ", ";
+        if (I == 10) {
+          // Avoid printing out the entire contents of large arrays.
+          Out << "...";
+          break;
+        }
+        getArrayInitializedElt(I).printPretty(Out, Ctx, ElemTy);
+      }
     }
     Out << '}';
     return;
-  case APValue::Struct:
+  }
+  case APValue::Struct: {
     Out << '{';
-    if (unsigned N = V.getStructNumBases()) {
-      Out << V.getStructBase(0);
-      for (unsigned I = 1; I != N; ++I)
-        Out << ", " << V.getStructBase(I);
-      if (V.getStructNumFields())
-        Out << ", ";
+    const RecordDecl *RD = Ty->getAs<RecordType>()->getDecl();
+    bool First = true;
+    if (unsigned N = getStructNumBases()) {
+      const CXXRecordDecl *CD = cast<CXXRecordDecl>(RD);
+      CXXRecordDecl::base_class_const_iterator BI = CD->bases_begin();
+      for (unsigned I = 0; I != N; ++I, ++BI) {
+        assert(BI != CD->bases_end());
+        if (!First)
+          Out << ", ";
+        getStructBase(I).printPretty(Out, Ctx, BI->getType());
+        First = false;
+      }
     }
-    if (unsigned N = V.getStructNumFields()) {
-      Out << V.getStructField(0);
-      for (unsigned I = 1; I != N; ++I)
-        Out << ", " << V.getStructField(I);
+    for (RecordDecl::field_iterator FI = RD->field_begin();
+         FI != RD->field_end(); ++FI) {
+      if (!First)
+        Out << ", ";
+      if ((*FI)->isUnnamedBitfield()) continue;
+      getStructField((*FI)->getFieldIndex()).
+        printPretty(Out, Ctx, (*FI)->getType());
+      First = false;
     }
     Out << '}';
     return;
+  }
   case APValue::Union:
-    Out << '{' << V.getUnionValue() << '}';
+    Out << '{';
+    if (const FieldDecl *FD = getUnionField()) {
+      Out << "." << *FD << " = ";
+      getUnionValue().printPretty(Out, Ctx, FD->getType());
+    }
+    Out << '}';
     return;
   case APValue::MemberPointer:
-    Out << "MemberPointer: <todo>";
+    // FIXME: This is not enough to unambiguously identify the member in a
+    // multiple-inheritance scenario.
+    if (const ValueDecl *VD = getMemberPointerDecl()) {
+      Out << '&' << *cast<CXXRecordDecl>(VD->getDeclContext()) << "::" << *VD;
+      return;
+    }
+    Out << "0";
     return;
   }
   llvm_unreachable("Unknown APValue kind!");
 }
 
-const DiagnosticBuilder &clang::operator<<(const DiagnosticBuilder &DB,
-                                           const APValue &V) {
-  llvm::SmallString<64> Buffer;
-  llvm::raw_svector_ostream Out(Buffer);
-  WriteShortAPValueToStream(Out, V);
-  return DB << Out.str();
+std::string APValue::getAsString(ASTContext &Ctx, QualType Ty) const {
+  std::string Result;
+  llvm::raw_string_ostream Out(Result);
+  printPretty(Out, Ctx, Ty);
+  return Result;
 }
 
 const APValue::LValueBase APValue::getLValueBase() const {
index dd1110ab11c3590c8be6d90d042f2c77e07db05c..31b211eff6cedb6d6c29dbe26586d5fc2d856777 100644 (file)
@@ -231,6 +231,12 @@ namespace {
     /// Parent - The caller of this stack frame.
     CallStackFrame *Caller;
 
+    /// CallLoc - The location of the call expression for this call.
+    SourceLocation CallLoc;
+
+    /// Callee - The function which was called.
+    const FunctionDecl *Callee;
+
     /// This - The binding for the this pointer in this call, if any.
     const LValue *This;
 
@@ -243,7 +249,8 @@ namespace {
     /// Temporaries - Temporary lvalues materialized within this stack frame.
     MapTy Temporaries;
 
-    CallStackFrame(EvalInfo &Info, const LValue *This,
+    CallStackFrame(EvalInfo &Info, SourceLocation CallLoc,
+                   const FunctionDecl *Callee, const LValue *This,
                    const CCValue *Arguments);
     ~CallStackFrame();
   };
@@ -300,8 +307,8 @@ namespace {
 
     EvalInfo(const ASTContext &C, Expr::EvalStatus &S)
       : Ctx(const_cast<ASTContext&>(C)), EvalStatus(S), CurrentCall(0),
-        CallStackDepth(0), BottomFrame(*this, 0, 0), EvaluatingDecl(0),
-        EvaluatingDeclValue(0), HasActiveDiagnostic(false) {}
+        CallStackDepth(0), BottomFrame(*this, SourceLocation(), 0, 0, 0),
+        EvaluatingDecl(0), EvaluatingDeclValue(0), HasActiveDiagnostic(false) {}
 
     const CCValue *getOpaqueValue(const OpaqueValueExpr *e) const {
       MapTy::const_iterator i = OpaqueValues.find(e);
@@ -332,6 +339,9 @@ namespace {
       return EvalStatus.Diag->back().second;
     }
 
+    /// Add notes containing a call stack to the current point of evaluation.
+    void addCallStack(unsigned Limit);
+
   public:
     /// Diagnose that the evaluation cannot be folded.
     OptionalDiagnostic Diag(SourceLocation Loc, diag::kind DiagId,
@@ -340,11 +350,17 @@ namespace {
       // isn't a constant expression. This diagnostic is more important.
       // FIXME: We might want to show both diagnostics to the user.
       if (EvalStatus.Diag) {
+        unsigned CallStackNotes = CallStackDepth - 1;
+        unsigned Limit = Ctx.getDiagnostics().getConstexprBacktraceLimit();
+        if (Limit)
+          CallStackNotes = std::min(CallStackNotes, Limit + 1);
+
         HasActiveDiagnostic = true;
         EvalStatus.Diag->clear();
-        EvalStatus.Diag->reserve(1 + ExtraNotes);
-        // FIXME: Add a call stack for constexpr evaluation.
-        return OptionalDiagnostic(&addDiag(Loc, DiagId));
+        EvalStatus.Diag->reserve(1 + ExtraNotes + CallStackNotes);
+        addDiag(Loc, DiagId);
+        addCallStack(Limit);
+        return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second);
       }
       HasActiveDiagnostic = false;
       return OptionalDiagnostic();
@@ -367,20 +383,87 @@ namespace {
       return OptionalDiagnostic(&addDiag(Loc, DiagId));
     }
   };
+}
+
+CallStackFrame::CallStackFrame(EvalInfo &Info, SourceLocation CallLoc,
+                               const FunctionDecl *Callee, const LValue *This,
+                               const CCValue *Arguments)
+    : Info(Info), Caller(Info.CurrentCall), CallLoc(CallLoc), Callee(Callee),
+      This(This), Arguments(Arguments) {
+  Info.CurrentCall = this;
+  ++Info.CallStackDepth;
+}
+
+CallStackFrame::~CallStackFrame() {
+  assert(Info.CurrentCall == this && "calls retired out of order");
+  --Info.CallStackDepth;
+  Info.CurrentCall = Caller;
+}
+
+/// Produce a string describing the given constexpr call.
+static void describeCall(CallStackFrame *Frame, llvm::raw_ostream &Out) {
+  unsigned ArgIndex = 0;
+  bool IsMemberCall = isa<CXXMethodDecl>(Frame->Callee) &&
+                      !isa<CXXConstructorDecl>(Frame->Callee);
+
+  if (!IsMemberCall)
+    Out << *Frame->Callee << '(';
+
+  for (FunctionDecl::param_const_iterator I = Frame->Callee->param_begin(),
+       E = Frame->Callee->param_end(); I != E; ++I, ++ArgIndex) {
+    if (ArgIndex > IsMemberCall)
+      Out << ", ";
 
-  CallStackFrame::CallStackFrame(EvalInfo &Info, const LValue *This,
-                                 const CCValue *Arguments)
-      : Info(Info), Caller(Info.CurrentCall), This(This), Arguments(Arguments) {
-    Info.CurrentCall = this;
-    ++Info.CallStackDepth;
+    const ParmVarDecl *Param = *I;
+    const CCValue &Arg = Frame->Arguments[ArgIndex];
+    if (!Arg.isLValue() || Arg.getLValueDesignator().Invalid)
+      Arg.printPretty(Out, Frame->Info.Ctx, Param->getType());
+    else {
+      // Deliberately slice off the frame to form an APValue we can print.
+      APValue Value(Arg.getLValueBase(), Arg.getLValueOffset(),
+                    Arg.getLValueDesignator().Entries,
+                    Arg.getLValueDesignator().OnePastTheEnd);
+      Value.printPretty(Out, Frame->Info.Ctx, Param->getType());
+    }
+
+    if (ArgIndex == 0 && IsMemberCall)
+      Out << "->" << *Frame->Callee << '(';
   }
 
-  CallStackFrame::~CallStackFrame() {
-    assert(Info.CurrentCall == this && "calls retired out of order");
-    --Info.CallStackDepth;
-    Info.CurrentCall = Caller;
+  Out << ')';
+}
+
+void EvalInfo::addCallStack(unsigned Limit) {
+  // Determine which calls to skip, if any.
+  unsigned ActiveCalls = CallStackDepth - 1;
+  unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart;
+  if (Limit && Limit < ActiveCalls) {
+    SkipStart = Limit / 2 + Limit % 2;
+    SkipEnd = ActiveCalls - Limit / 2;
+  }
+
+  // Walk the call stack and add the diagnostics.
+  unsigned CallIdx = 0;
+  for (CallStackFrame *Frame = CurrentCall; Frame != &BottomFrame;
+       Frame = Frame->Caller, ++CallIdx) {
+    // Skip this call?
+    if (CallIdx >= SkipStart && CallIdx < SkipEnd) {
+      if (CallIdx == SkipStart) {
+        // Note that we're skipping calls.
+        addDiag(Frame->CallLoc, diag::note_constexpr_calls_suppressed)
+          << unsigned(ActiveCalls - Limit);
+      }
+      continue;
+    }
+
+    llvm::SmallVector<char, 128> Buffer;
+    llvm::raw_svector_ostream Out(Buffer);
+    describeCall(Frame, Out);
+    addDiag(Frame->CallLoc, diag::note_constexpr_call_here) << Out.str();
   }
+}
 
+namespace {
   struct ComplexValue {
   private:
     bool IsInt;
@@ -1465,7 +1548,8 @@ static bool EvaluateArgs(ArrayRef<const Expr*> Args, ArgVector &ArgValues,
 }
 
 /// Evaluate a function call.
-static bool HandleFunctionCall(const Expr *CallExpr, const LValue *This,
+static bool HandleFunctionCall(const Expr *CallExpr, const FunctionDecl *Callee,
+                               const LValue *This,
                                ArrayRef<const Expr*> Args, const Stmt *Body,
                                EvalInfo &Info, APValue &Result) {
   if (!Info.CheckCallLimit(CallExpr->getExprLoc()))
@@ -1475,7 +1559,8 @@ static bool HandleFunctionCall(const Expr *CallExpr, const LValue *This,
   if (!EvaluateArgs(Args, ArgValues, Info))
     return false;
 
-  CallStackFrame Frame(Info, This, ArgValues.data());
+  CallStackFrame Frame(Info, CallExpr->getExprLoc(), Callee, This,
+                       ArgValues.data());
   return EvaluateStmt(Result, Info, Body) == ESR_Returned;
 }
 
@@ -1492,7 +1577,8 @@ static bool HandleConstructorCall(const Expr *CallExpr, const LValue &This,
   if (!EvaluateArgs(Args, ArgValues, Info))
     return false;
 
-  CallStackFrame Frame(Info, &This, ArgValues.data());
+  CallStackFrame Frame(Info, CallExpr->getExprLoc(), Definition,
+                       &This, ArgValues.data());
 
   // If it's a delegating constructor, just delegate.
   if (Definition->isDelegatingConstructor()) {
@@ -1855,7 +1941,7 @@ public:
     APValue Result;
 
     if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition) ||
-        !HandleFunctionCall(E, This, Args, Body, Info, Result))
+        !HandleFunctionCall(E, Definition, This, Args, Body, Info, Result))
       return false;
 
     return DerivedSuccess(CCValue(Result, CCValue::GlobalValue()), E);
index 7ea296233a24510549a392efd32908809c46749f..164357215900235cb9ff148c181b26814febc62b 100644 (file)
@@ -53,6 +53,7 @@ DiagnosticsEngine::DiagnosticsEngine(
 
   ErrorLimit = 0;
   TemplateBacktraceLimit = 0;
+  ConstexprBacktraceLimit = 0;
 
   Reset();
 }
index 60e6d3dd32fc2b30022b84917ce4ac2d23094dac..798d787c65e6f8bd6ab72780b12f1c483e51ca43 100644 (file)
@@ -1808,6 +1808,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
     CmdArgs.push_back(A->getValue(Args));
   }
 
+  if (Arg *A = Args.getLastArg(options::OPT_fconstexpr_backtrace_limit_EQ)) {
+    CmdArgs.push_back("-fconstexpr-backtrace-limit");
+    CmdArgs.push_back(A->getValue(Args));
+  }
+
   // Pass -fmessage-length=.
   CmdArgs.push_back("-fmessage-length");
   if (Arg *A = Args.getLastArg(options::OPT_fmessage_length_EQ)) {
index 797147e5af29fe4ffc66151b1904bc63839c7a34..3ad555bfe8b40f6af61a0615b5fa16aa00e850b7 100644 (file)
@@ -354,6 +354,11 @@ static void DiagnosticOptsToArgs(const DiagnosticOptions &Opts,
     Res.push_back("-ftemplate-backtrace-limit");
     Res.push_back(llvm::utostr(Opts.TemplateBacktraceLimit));
   }
+  if (Opts.ConstexprBacktraceLimit
+                        != DiagnosticOptions::DefaultConstexprBacktraceLimit) {
+    Res.push_back("-fconstexpr-backtrace-limit");
+    Res.push_back(llvm::utostr(Opts.ConstexprBacktraceLimit));
+  }
 
   if (Opts.TabStop != DiagnosticOptions::DefaultTabStop) {
     Res.push_back("-ftabstop");
@@ -1229,6 +1234,10 @@ static void ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
     = Args.getLastArgIntValue(OPT_ftemplate_backtrace_limit,
                          DiagnosticOptions::DefaultTemplateBacktraceLimit,
                          Diags);
+  Opts.ConstexprBacktraceLimit
+    = Args.getLastArgIntValue(OPT_fconstexpr_backtrace_limit,
+                         DiagnosticOptions::DefaultConstexprBacktraceLimit,
+                         Diags);
   Opts.TabStop = Args.getLastArgIntValue(OPT_ftabstop,
                                     DiagnosticOptions::DefaultTabStop, Diags);
   if (Opts.TabStop == 0 || Opts.TabStop > DiagnosticOptions::MaxTabStop) {
index e63bc21cb4bd3d4e7e6008920b0297bce17e21fa..6f441112f361df80f461adbdbf5b6437f79e667f 100644 (file)
@@ -58,6 +58,8 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
     Diags.setErrorLimit(Opts.ErrorLimit);
   if (Opts.TemplateBacktraceLimit)
     Diags.setTemplateBacktraceLimit(Opts.TemplateBacktraceLimit);
+  if (Opts.ConstexprBacktraceLimit)
+    Diags.setConstexprBacktraceLimit(Opts.ConstexprBacktraceLimit);
 
   // If -pedantic or -pedantic-errors was specified, then we want to map all
   // extension diagnostics onto WARNING or ERROR unless the user has futz'd
index a95a2571319fc6d8b3c850f858850867ef4540b0..7dbf83043156f8b38e89885bf74477a40e16383c 100644 (file)
@@ -5707,7 +5707,7 @@ static void DiagnoseNarrowingInInitList(
            ? diag::err_init_list_constant_narrowing
            : diag::warn_init_list_constant_narrowing)
       << InitE->getSourceRange()
-      << ConstantValue
+      << ConstantValue.getAsString(S.getASTContext(), EntityType)
       << EntityType.getLocalUnqualifiedType();
   } else
     S.Diag(InitE->getLocStart(),
index 2e5e7f2ca419c99f613e2874ed7734ec47d44da2..0e6d69dd123b96208c89c5850cd7e052d426a976 100644 (file)
@@ -56,7 +56,7 @@ namespace NonConstExprReturn {
     return n; // expected-note {{reference to temporary cannot be returned from a constexpr function}}
   }
   struct NonConstExprFunction {
-    int n : id_ref( // expected-error {{constant expression}}
+    int n : id_ref( // expected-error {{constant expression}} expected-note {{in call to 'id_ref(16)'}}
         16 // expected-note {{temporary created here}}
         );
   };
@@ -64,10 +64,10 @@ namespace NonConstExprReturn {
     return &a; // expected-note {{pointer to 'n' cannot be returned from a constexpr function}}
   }
   constexpr const int *return_param(int n) { // expected-note {{declared here}}
-    return address_of(n);
+    return address_of(n); // expected-note {{in call to 'address_of(n)'}}
   }
   struct S {
-    int n : *return_param(0); // expected-error {{constant expression}}
+    int n : *return_param(0); // expected-error {{constant expression}} expected-note {{in call to 'return_param(0)'}}
   };
 }
 
@@ -87,7 +87,7 @@ namespace NonConstExprCtor {
   constexpr T t2(0); // expected-error {{must be initialized by a constant expression}}
 
   struct S {
-    int n : T(4).r; // expected-error {{constant expression}} expected-note {{temporary created here}}
+    int n : T(4).r; // expected-error {{constant expression}} expected-note {{temporary created here}} expected-note {{in call to 'T(4)'}}
   };
 }
 
@@ -95,17 +95,17 @@ namespace NonConstExprCtor {
 //   exceed the implementation-defined recursion limits (see Annex B);
 namespace RecursionLimits {
   constexpr int RecurseForever(int n) {
-    return n + RecurseForever(n+1); // expected-note {{constexpr evaluation exceeded maximum depth of 256 calls}}
+    return n + RecurseForever(n+1); // expected-note {{constexpr evaluation exceeded maximum depth of 256 calls}} expected-note 9{{in call to 'RecurseForever(}} expected-note {{skipping 246 calls}}
   }
   struct AlsoRecurseForever {
     constexpr AlsoRecurseForever(int n) :
-      n(AlsoRecurseForever(n+1).n) // expected-note {{constexpr evaluation exceeded maximum depth of 256 calls}}
+      n(AlsoRecurseForever(n+1).n) // expected-note {{constexpr evaluation exceeded maximum depth of 256 calls}} expected-note 9{{in call to 'AlsoRecurseForever(}} expected-note {{skipping 246 calls}}
     {}
     int n;
   };
   struct S {
-    int k : RecurseForever(0); // expected-error {{constant expression}}
-    int l : AlsoRecurseForever(0).n; // expected-error {{constant expression}}
+    int k : RecurseForever(0); // expected-error {{constant expression}} expected-note {{in call to}}
+    int l : AlsoRecurseForever(0).n; // expected-error {{constant expression}} expected-note {{in call to}}
   };
 }
 
@@ -135,7 +135,7 @@ namespace UndefinedBehavior {
     return q[0]; // expected-note {{dereferenced pointer past the end of subobject of 's' is not a constant expression}}
   }
   struct T {
-    int n : f(p); // expected-error {{not an integer constant expression}}
+    int n : f(p); // expected-error {{not an integer constant expression}} expected-note {{in call to 'f(&s.m + 1)'}}
   };
 }
 
index 4b59157ea684d5b7497d06d076c2129db4b64588..5105418af17d9e38af4438a594f91ae62670120d 100644 (file)
@@ -416,7 +416,11 @@ constexpr int ZipFoldR(int (*F)(int x, int y, int c), int n,
                        const int *xs, const int *ys, int c) {
   return n ? F(
                *xs, // expected-note {{subexpression not valid}}
-               *ys, ZipFoldR(F, n-1, xs+1, ys+1, c)) : c;
+               *ys,
+               ZipFoldR(F, n-1, xs+1, ys+1, c)) // \
+      expected-note {{in call to 'ZipFoldR(&SubMul, 2, &xs[4], &ys[4], 1)'}} \
+      expected-note {{in call to 'ZipFoldR(&SubMul, 1, &xs[5], &ys[5], 1)'}}
+           : c;
 }
 constexpr int MulAdd(int x, int y, int c) { return x * y + c; }
 constexpr int InnerProduct = ZipFoldR(MulAdd, 5, xs, ys, 0);
@@ -425,7 +429,9 @@ static_assert(InnerProduct == 35, "");
 constexpr int SubMul(int x, int y, int c) { return (x - y) * c; }
 constexpr int DiffProd = ZipFoldR(SubMul, 2, xs+3, ys+3, 1);
 static_assert(DiffProd == 8, "");
-static_assert(ZipFoldR(SubMul, 3, xs+3, ys+3, 1), ""); // expected-error {{constant expression}}
+static_assert(ZipFoldR(SubMul, 3, xs+3, ys+3, 1), ""); // \
+      expected-error {{constant expression}} \
+      expected-note {{in call to 'ZipFoldR(&SubMul, 3, &xs[3], &ys[3], 1)'}}
 
 constexpr const int *p = xs + 3;
 constexpr int xs4 = p[1]; // ok
@@ -441,6 +447,13 @@ static_assert((&zs[0][0][0][2])[-1] == 2, "");
 static_assert(**(**(zs + 1) + 1) == 11, "");
 static_assert(*(&(&(*(*&(&zs[2] - 1)[0] + 2 - 2))[2])[-1][-1] + 1) == 11, "");
 
+constexpr int fail(const int &p) {
+  return (&p)[64]; // expected-note {{subexpression}}
+}
+static_assert(fail(*(&(&(*(*&(&zs[2] - 1)[0] + 2 - 2))[2])[-1][-1] + 1)) == 11, ""); // \
+expected-error {{static_assert expression is not an integral constant expression}} \
+expected-note {{in call to 'fail(zs[1][0][1][0])'}}
+
 constexpr int arr[40] = { 1, 2, 3, [8] = 4 }; // expected-warning {{extension}}
 constexpr int SumNonzero(const int *p) {
   return *p + (*p ? SumNonzero(p+1) : 0);
diff --git a/test/SemaCXX/constexpr-backtrace-limit.cpp b/test/SemaCXX/constexpr-backtrace-limit.cpp
new file mode 100644 (file)
index 0000000..d2923ab
--- /dev/null
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -fconstexpr-backtrace-limit 0 -fconstexpr-depth 4 -fno-caret-diagnostics 2>&1 | FileCheck %s -check-prefix=TEST1
+// TEST1: constant expression
+// TEST1-NEXT: exceeded maximum depth of 4
+// TEST1-NEXT: in call to 'recurse(2)'
+// TEST1-NEXT: in call to 'recurse(3)'
+// TEST1-NEXT: in call to 'recurse(4)'
+// TEST1-NEXT: in call to 'recurse(5)'
+
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -fconstexpr-backtrace-limit 2 -fconstexpr-depth 4 -fno-caret-diagnostics 2>&1 | FileCheck %s -check-prefix=TEST2
+// TEST2: constant expression
+// TEST2-NEXT: exceeded maximum depth of 4
+// TEST2-NEXT: in call to 'recurse(2)'
+// TEST2-NEXT: skipping 2 calls
+// TEST2-NEXT: in call to 'recurse(5)'
+
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -fconstexpr-backtrace-limit 2 -fconstexpr-depth 8 -fno-caret-diagnostics 2>&1 | FileCheck %s -check-prefix=TEST3
+// TEST3: constant expression
+// TEST3-NEXT: subexpression
+// TEST3-NEXT: in call to 'recurse(0)'
+// TEST3-NEXT: skipping 4 calls
+// TEST3-NEXT: in call to 'recurse(5)'
+
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -fconstexpr-backtrace-limit 8 -fconstexpr-depth 8 -fno-caret-diagnostics 2>&1 | FileCheck %s -check-prefix=TEST4
+// TEST4: constant expression
+// TEST4-NEXT: subexpression
+// TEST4-NEXT: in call to 'recurse(0)'
+// TEST4-NEXT: in call to 'recurse(1)'
+// TEST4-NEXT: in call to 'recurse(2)'
+// TEST4-NEXT: in call to 'recurse(3)'
+// TEST4-NEXT: in call to 'recurse(4)'
+// TEST4-NEXT: in call to 'recurse(5)'
+
+constexpr int recurse(int n) { return n ? recurse(n-1) : *(int*)n; }
+static_assert(recurse(5), "");
diff --git a/test/SemaCXX/constexpr-printing.cpp b/test/SemaCXX/constexpr-printing.cpp
new file mode 100644 (file)
index 0000000..341495c
--- /dev/null
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 %s -std=c++11 -fsyntax-only -verify
+
+constexpr int extract(struct S &s);
+
+struct S {
+  constexpr S() : n(extract(*this)), m(0) {}
+  constexpr S(int k) : n(k), m(extract(*this)) {}
+  int n, m;
+};
+
+constexpr int extract(S &s) { return s.n; }
+
+// 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 s2(10);
+
+typedef __attribute__((vector_size(16))) int vector_int;
+
+struct T {
+  constexpr T() : arr() {}
+  int arr[4];
+};
+struct U : T {
+  constexpr U(const int *p) : T(), another(), p(p) {}
+  constexpr U(const U &u) : T(), another(), p(u.p) {}
+  T another;
+  const int *p;
+};
+constexpr U u1(&u1.arr[2]);
+
+constexpr int test_printing(int a, float b, _Complex int c, _Complex float d,
+                            int *e, int &f, vector_int g, U h) {
+  return *e; // expected-note {{subexpression}}
+}
+U u2(0);
+static_assert(test_printing(12, 39.762, 3 + 4i, 12.9 + 3.6i, &u2.arr[4], u2.another.arr[2], (vector_int){5, 1, 2, 3}, u1) == 0, ""); // \
+expected-error {{constant expression}} \
+expected-note {{in call to 'test_printing(12, 3.976200e+01, 3+4i, 1.290000e+01+3.600000e+00i, &u2.T::arr[4], u2.another.arr[2], {5, 1, 2, 3}, {{{}}, {{}}, &u1.T::arr[2]})'}}
+
+struct V {
+  // FIXME: when we can generate these as constexpr constructors, remove the
+  // explicit definitions.
+  constexpr V() : arr{[255] = 42} {}
+  constexpr V(const V &v) : arr{[255] = 42} {}
+  int arr[256];
+};
+constexpr V v;
+constexpr int get(const int *p) { return *p; } // expected-note {{subexpression}}
+constexpr int passLargeArray(V v) { return get(v.arr+256); } // expected-note {{in call to 'get(&v.arr[256])'}}
+static_assert(passLargeArray(v) == 0, ""); // expected-error {{constant expression}} expected-note {{in call to 'passLargeArray({{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...}})'}}
+
+union Union {
+  constexpr Union(int n) : b(n) {}
+  constexpr Union(const Union &u) : b(u.b) {}
+  int a, b;
+};
+constexpr Union myUnion = 76;
+
+constexpr int badness(Union u) { return u.a + u.b; } // expected-note {{subexpression}}
+static_assert(badness(myUnion), ""); // expected-error {{constant expression}} \
+        expected-note {{in call to 'badness({.b = 76})'}}
+
+struct MemPtrTest {
+  int n;
+  void f();
+};
+MemPtrTest mpt;
+constexpr int MemPtr(int (MemPtrTest::*a), void (MemPtrTest::*b)(), int &c) {
+  return c; // expected-note {{subexpression}}
+}
+static_assert(MemPtr(&MemPtrTest::n, &MemPtrTest::f, mpt.*&MemPtrTest::n), ""); // expected-error {{constant expression}} \
+expected-note {{in call to 'MemPtr(&MemPtrTest::n, &MemPtrTest::f, mpt.n)'}}
diff --git a/unittests/AST/APValueTest.cpp b/unittests/AST/APValueTest.cpp
deleted file mode 100644 (file)
index 5ac454d..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-//===- unittests/AST/APValueTest.cpp - APValue tests ---===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/AST/APValue.h"
-
-#include "clang/Basic/Diagnostic.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SmallString.h"
-
-#include "gtest/gtest.h"
-
-using namespace llvm;
-using namespace clang;
-
-namespace {
-
-class DiagnosticOutputGetter {
-  class LastDiagnosticString : public DiagnosticConsumer {
-    SmallString<64> LastDiagnostic;
-  public:
-    virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
-                                  const Diagnostic &Info) {
-      LastDiagnostic.clear();
-      Info.FormatDiagnostic(LastDiagnostic);
-    }
-
-    StringRef get() const { return LastDiagnostic; }
-
-    virtual DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
-      return new LastDiagnosticString();
-    }
-  };
-
-  const IntrusiveRefCntPtr<DiagnosticIDs> DiagIDs;
-  const unsigned diag_just_format;
-  LastDiagnosticString LastDiagnostic;
-  DiagnosticsEngine Diag;
-
-public:
-  DiagnosticOutputGetter()
-    : DiagIDs(new DiagnosticIDs),
-      diag_just_format(DiagIDs->getCustomDiagID(DiagnosticIDs::Error, "%0")),
-      Diag(DiagIDs, &LastDiagnostic, false) {
-  }
-
-  template<typename T>
-  std::string operator()(const T& value) {
-    Diag.Report(diag_just_format) << value;
-    return LastDiagnostic.get().str();
-  }
-};
-
-TEST(APValue, Diagnostics) {
-  DiagnosticOutputGetter GetDiagnosticOutput;
-
-  EXPECT_EQ("Uninitialized", GetDiagnosticOutput(APValue()));
-  EXPECT_EQ("5", GetDiagnosticOutput(APValue(APSInt(APInt(16, 5)))));
-  EXPECT_EQ("3.141590e+00",
-            GetDiagnosticOutput(APValue(APFloat(APFloat::IEEEdouble,
-                                                "3.14159"))));
-  EXPECT_EQ("3+4i",
-            GetDiagnosticOutput(APValue(APSInt(APInt(16, 3)),
-                                        APSInt(APInt(16, 4)))));
-  EXPECT_EQ("3.200000e+00+5.700000e+00i",
-            GetDiagnosticOutput(APValue(
-                                  APFloat(APFloat::IEEEdouble, "3.2"),
-                                  APFloat(APFloat::IEEEdouble, "5.7"))));
-  APValue V[] = {
-    APValue(APSInt(APInt(16, 3))),
-    APValue(APSInt(APInt(16, 4))),
-    APValue(APSInt(APInt(16, 5)))
-  };
-  EXPECT_EQ("[3, 4, 5]",
-            GetDiagnosticOutput(APValue(V, array_lengthof(V))));
-}
-
-} // anonymous namespace
diff --git a/unittests/AST/Makefile b/unittests/AST/Makefile
deleted file mode 100644 (file)
index 74191d0..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-##===- unittests/Frontend/Makefile -------------------------*- Makefile -*-===##
-#
-#                     The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-##===----------------------------------------------------------------------===##
-
-CLANG_LEVEL = ../..
-TESTNAME = AST
-LINK_COMPONENTS := support mc
-USEDLIBS = clangAST.a clangBasic.a
-
-include $(CLANG_LEVEL)/unittests/Makefile
index 901f167f35636e28369adab566960d6d4243924e..cb44dc59dcfc4895e13c0fb7f32cbca182ce82a8 100644 (file)
@@ -50,11 +50,6 @@ if(SUPPORTS_NO_VARIADIC_MACROS_FLAG)
   add_definitions("-Wno-variadic-macros")
 endif()
 
-add_clang_unittest(AST
-  AST/APValueTest.cpp
-  USED_LIBS gtest gtest_main clangAST
- )
-
 add_clang_unittest(Basic
   Basic/FileManagerTest.cpp
   USED_LIBS gtest gtest_main clangBasic
index f4ce6adaa72510ce5272a91ca9295ee4d89d44a3..951e17e21771ee1e9960e7de72678000db155602 100644 (file)
@@ -14,7 +14,7 @@ ifndef CLANG_LEVEL
 
 IS_UNITTEST_LEVEL := 1
 CLANG_LEVEL := ..
-PARALLEL_DIRS = AST Basic Frontend
+PARALLEL_DIRS = Basic Frontend
 
 endif  # CLANG_LEVEL