diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2011-12-16 19:06:07 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2011-12-16 19:06:07 +0000 |
commit | 08d6e032a2a0a8656d12b3b7b93942987bb12eb7 (patch) | |
tree | 632b74cc7329419d23c53cb941e19ed16a587d5b | |
parent | d3d8548e75f3fb6db53ed0927c1df30d78f4ce1d (diff) |
C++11 constexpr: Add note stacks containing backtraces if constant evaluation
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
-rw-r--r-- | include/clang/AST/APValue.h | 16 | ||||
-rw-r--r-- | include/clang/Basic/Diagnostic.h | 20 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticASTKinds.td | 4 | ||||
-rw-r--r-- | include/clang/Driver/CC1Options.td | 2 | ||||
-rw-r--r-- | include/clang/Driver/Options.td | 4 | ||||
-rw-r--r-- | include/clang/Frontend/DiagnosticOptions.h | 5 | ||||
-rw-r--r-- | lib/AST/APValue.cpp | 249 | ||||
-rw-r--r-- | lib/AST/ExprConstant.cpp | 124 | ||||
-rw-r--r-- | lib/Basic/Diagnostic.cpp | 1 | ||||
-rw-r--r-- | lib/Driver/Tools.cpp | 5 | ||||
-rw-r--r-- | lib/Frontend/CompilerInvocation.cpp | 9 | ||||
-rw-r--r-- | lib/Frontend/Warnings.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaInit.cpp | 2 | ||||
-rw-r--r-- | test/CXX/expr/expr.const/p2-0x.cpp | 18 | ||||
-rw-r--r-- | test/SemaCXX/constant-expression-cxx11.cpp | 17 | ||||
-rw-r--r-- | test/SemaCXX/constexpr-backtrace-limit.cpp | 34 | ||||
-rw-r--r-- | test/SemaCXX/constexpr-printing.cpp | 73 | ||||
-rw-r--r-- | unittests/AST/APValueTest.cpp | 83 | ||||
-rw-r--r-- | unittests/AST/Makefile | 15 | ||||
-rw-r--r-- | unittests/CMakeLists.txt | 5 | ||||
-rw-r--r-- | unittests/Makefile | 2 |
21 files changed, 483 insertions, 207 deletions
diff --git a/include/clang/AST/APValue.h b/include/clang/AST/APValue.h index cca00371c2..151956903c 100644 --- a/include/clang/AST/APValue.h +++ b/include/clang/AST/APValue.h @@ -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 diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h index 6fd81b05ba..e6f30b17dc 100644 --- a/include/clang/Basic/Diagnostic.h +++ b/include/clang/Basic/Diagnostic.h @@ -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; } diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index e9b3147521..ae6b59ca9d 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -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 { diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index e8c5398dec..d275af24f2 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -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">, diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 730a08d3d8..745ee193cd 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -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>; diff --git a/include/clang/Frontend/DiagnosticOptions.h b/include/clang/Frontend/DiagnosticOptions.h index 8433da4fa4..282ca5d366 100644 --- a/include/clang/Frontend/DiagnosticOptions.h +++ b/include/clang/Frontend/DiagnosticOptions.h @@ -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; } }; diff --git a/lib/AST/APValue.cpp b/lib/AST/APValue.cpp index b99a66267c..0d4dea82cc 100644 --- a/lib/AST/APValue.cpp +++ b/lib/AST/APValue.cpp @@ -12,7 +12,11 @@ //===----------------------------------------------------------------------===// #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 { diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index dd1110ab11..31b211eff6 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -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); diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index 7ea296233a..1643572159 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -53,6 +53,7 @@ DiagnosticsEngine::DiagnosticsEngine( ErrorLimit = 0; TemplateBacktraceLimit = 0; + ConstexprBacktraceLimit = 0; Reset(); } diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 60e6d3dd32..798d787c65 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -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)) { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 797147e5af..3ad555bfe8 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -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) { diff --git a/lib/Frontend/Warnings.cpp b/lib/Frontend/Warnings.cpp index e63bc21cb4..6f441112f3 100644 --- a/lib/Frontend/Warnings.cpp +++ b/lib/Frontend/Warnings.cpp @@ -58,6 +58,8 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, Diags.setErrorLimit(Opts.ErrorLimit); if (Opts.TemplateBacktraceLimit) Diags.setTemplateBacktraceLimit(Opts.TemplateBacktraceLimit); |