#include "llvm/ADT/PointerUnion.h"
namespace clang {
+ class ASTContext;
class CharUnits;
class DiagnosticBuilder;
class Expr;
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],
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");
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
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;
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; }
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 {
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">,
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>;
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>;
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;
ErrorLimit = 0;
TemplateBacktraceLimit = DefaultTemplateBacktraceLimit;
MacroBacktraceLimit = DefaultMacroBacktraceLimit;
+ ConstexprBacktraceLimit = DefaultConstexprBacktraceLimit;
}
};
//===----------------------------------------------------------------------===//
#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"
}
void APValue::dump() const {
- print(llvm::errs());
+ dump(llvm::errs());
llvm::errs() << '\n';
}
return V.convertToDouble();
}
-void APValue::print(raw_ostream &OS) const {
+void APValue::dump(raw_ostream &OS) const {
switch (getKind()) {
case Uninitialized:
OS << "Uninitialized";
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();
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>";
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 {
/// 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;
/// 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();
};
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);
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,
// 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();
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;
}
/// 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()))
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;
}
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()) {
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);
ErrorLimit = 0;
TemplateBacktraceLimit = 0;
+ ConstexprBacktraceLimit = 0;
Reset();
}
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)) {
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");
= 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) {
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
? 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(),
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}}
);
};
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)'}}
};
}
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)'}}
};
}
// 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}}
};
}
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)'}}
};
}
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);
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
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);
--- /dev/null
+// 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), "");
--- /dev/null
+// 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)'}}
+++ /dev/null
-//===- 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
+++ /dev/null
-##===- 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
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
IS_UNITTEST_LEVEL := 1
CLANG_LEVEL := ..
-PARALLEL_DIRS = AST Basic Frontend
+PARALLEL_DIRS = Basic Frontend
endif # CLANG_LEVEL