PR47805: Use a single object for a function parameter in the caller and

callee in constant evaluation.

We previously made a deep copy of function parameters of class type when
passing them, resulting in the destructor for the parameter applying to
the original argument value, ignoring any modifications made in the
function body. This also meant that the 'this' pointer of the function
parameter could be observed changing between the caller and the callee.

This change completely reimplements how we model function parameters
during constant evaluation. We now model them roughly as if they were
variables living in the caller, albeit with an artificially reduced
scope that covers only the duration of the function call, instead of
modeling them as temporaries in the caller that we partially "reparent"
into the callee at the point of the call. This brings some minor
diagnostic improvements, as well as significantly reduced stack usage
during constant evaluation.
This commit is contained in:
Richard Smith 2020-10-13 10:03:02 -07:00
parent 498c7fa48a
commit 8f8b9f2cca
30 changed files with 396 additions and 216 deletions

View File

@ -490,6 +490,39 @@ namespace {
} }
}; };
/// A scope at the end of which an object can need to be destroyed.
enum class ScopeKind {
Block,
FullExpression,
Call
};
/// A reference to a particular call and its arguments.
struct CallRef {
CallRef() : OrigCallee(), CallIndex(0), Version() {}
CallRef(const FunctionDecl *Callee, unsigned CallIndex, unsigned Version)
: OrigCallee(Callee), CallIndex(CallIndex), Version(Version) {}
explicit operator bool() const { return OrigCallee; }
/// Get the parameter that the caller initialized, corresponding to the
/// given parameter in the callee.
const ParmVarDecl *getOrigParam(const ParmVarDecl *PVD) const {
return OrigCallee ? OrigCallee->getParamDecl(PVD->getFunctionScopeIndex())
: PVD;
}
/// The callee at the point where the arguments were evaluated. This might
/// be different from the actual callee (a different redeclaration, or a
/// virtual override), but this function's parameters are the ones that
/// appear in the parameter map.
const FunctionDecl *OrigCallee;
/// The call index of the frame that holds the argument values.
unsigned CallIndex;
/// The version of the parameters corresponding to this call.
unsigned Version;
};
/// A stack frame in the constexpr call stack. /// A stack frame in the constexpr call stack.
class CallStackFrame : public interp::Frame { class CallStackFrame : public interp::Frame {
public: public:
@ -504,9 +537,10 @@ namespace {
/// This - The binding for the this pointer in this call, if any. /// This - The binding for the this pointer in this call, if any.
const LValue *This; const LValue *This;
/// Arguments - Parameter bindings for this function call, indexed by /// Information on how to find the arguments to this call. Our arguments
/// parameters' function scope indices. /// are stored in our parent's CallStackFrame, using the ParmVarDecl* as a
APValue *Arguments; /// key and this value as the version.
CallRef Arguments;
/// Source location information about the default argument or default /// Source location information about the default argument or default
/// initializer expression we're evaluating, if any. /// initializer expression we're evaluating, if any.
@ -539,6 +573,10 @@ namespace {
TempVersionStack.pop_back(); TempVersionStack.pop_back();
} }
CallRef createCall(const FunctionDecl *Callee) {
return {Callee, Index, ++CurTempVersion};
}
// FIXME: Adding this to every 'CallStackFrame' may have a nontrivial impact // FIXME: Adding this to every 'CallStackFrame' may have a nontrivial impact
// on the overall stack usage of deeply-recursing constexpr evaluations. // on the overall stack usage of deeply-recursing constexpr evaluations.
// (We should cache this map rather than recomputing it repeatedly.) // (We should cache this map rather than recomputing it repeatedly.)
@ -552,7 +590,7 @@ namespace {
CallStackFrame(EvalInfo &Info, SourceLocation CallLoc, CallStackFrame(EvalInfo &Info, SourceLocation CallLoc,
const FunctionDecl *Callee, const LValue *This, const FunctionDecl *Callee, const LValue *This,
APValue *Arguments); CallRef Arguments);
~CallStackFrame(); ~CallStackFrame();
// Return the temporary for Key whose version number is Version. // Return the temporary for Key whose version number is Version.
@ -591,7 +629,10 @@ namespace {
/// bumping the temporary version number. /// bumping the temporary version number.
template<typename KeyT> template<typename KeyT>
APValue &createTemporary(const KeyT *Key, QualType T, APValue &createTemporary(const KeyT *Key, QualType T,
bool IsLifetimeExtended, LValue &LV); ScopeKind Scope, LValue &LV);
/// Allocate storage for a parameter of a function call made in this frame.
APValue &createParam(CallRef Args, const ParmVarDecl *PVD, LValue &LV);
void describe(llvm::raw_ostream &OS) override; void describe(llvm::raw_ostream &OS) override;
@ -605,6 +646,10 @@ namespace {
return true; return true;
return false; return false;
} }
private:
APValue &createLocal(APValue::LValueBase Base, const void *Key, QualType T,
ScopeKind Scope);
}; };
/// Temporarily override 'this'. /// Temporarily override 'this'.
@ -633,16 +678,20 @@ static bool HandleDestruction(EvalInfo &Info, SourceLocation Loc,
namespace { namespace {
/// A cleanup, and a flag indicating whether it is lifetime-extended. /// A cleanup, and a flag indicating whether it is lifetime-extended.
class Cleanup { class Cleanup {
llvm::PointerIntPair<APValue*, 1, bool> Value; llvm::PointerIntPair<APValue*, 2, ScopeKind> Value;
APValue::LValueBase Base; APValue::LValueBase Base;
QualType T; QualType T;
public: public:
Cleanup(APValue *Val, APValue::LValueBase Base, QualType T, Cleanup(APValue *Val, APValue::LValueBase Base, QualType T,
bool IsLifetimeExtended) ScopeKind Scope)
: Value(Val, IsLifetimeExtended), Base(Base), T(T) {} : Value(Val, Scope), Base(Base), T(T) {}
bool isLifetimeExtended() const { return Value.getInt(); } /// Determine whether this cleanup should be performed at the end of the
/// given kind of scope.
bool isDestroyedAtEndOf(ScopeKind K) const {
return (int)Value.getInt() >= (int)K;
}
bool endLifetime(EvalInfo &Info, bool RunDestructors) { bool endLifetime(EvalInfo &Info, bool RunDestructors) {
if (RunDestructors) { if (RunDestructors) {
SourceLocation Loc; SourceLocation Loc;
@ -928,7 +977,7 @@ namespace {
CallStackDepth(0), NextCallIndex(1), CallStackDepth(0), NextCallIndex(1),
StepsLeft(C.getLangOpts().ConstexprStepLimit), StepsLeft(C.getLangOpts().ConstexprStepLimit),
EnableNewConstInterp(C.getLangOpts().EnableNewConstInterp), EnableNewConstInterp(C.getLangOpts().EnableNewConstInterp),
BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr), BottomFrame(*this, SourceLocation(), nullptr, nullptr, CallRef()),
EvaluatingDecl((const ValueDecl *)nullptr), EvaluatingDecl((const ValueDecl *)nullptr),
EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false),
HasFoldFailureDiagnostic(false), InConstantContext(false), HasFoldFailureDiagnostic(false), InConstantContext(false),
@ -997,6 +1046,13 @@ namespace {
return Result; return Result;
} }
/// Get the allocated storage for the given parameter of the given call.
APValue *getParamSlot(CallRef Call, const ParmVarDecl *PVD) {
CallStackFrame *Frame = getCallFrameAndDepth(Call.CallIndex).first;
return Frame ? Frame->getTemporary(Call.getOrigParam(PVD), Call.Version)
: nullptr;
}
/// Information about a stack frame for std::allocator<T>::[de]allocate. /// Information about a stack frame for std::allocator<T>::[de]allocate.
struct StdAllocatorCaller { struct StdAllocatorCaller {
unsigned FrameIndex; unsigned FrameIndex;
@ -1032,10 +1088,13 @@ namespace {
void performLifetimeExtension() { void performLifetimeExtension() {
// Disable the cleanups for lifetime-extended temporaries. // Disable the cleanups for lifetime-extended temporaries.
CleanupStack.erase( CleanupStack.erase(std::remove_if(CleanupStack.begin(),
std::remove_if(CleanupStack.begin(), CleanupStack.end(), CleanupStack.end(),
[](Cleanup &C) { return C.isLifetimeExtended(); }), [](Cleanup &C) {
CleanupStack.end()); return !C.isDestroyedAtEndOf(
ScopeKind::FullExpression);
}),
CleanupStack.end());
} }
/// Throw away any remaining cleanups at the end of evaluation. If any /// Throw away any remaining cleanups at the end of evaluation. If any
@ -1284,7 +1343,7 @@ namespace {
/// RAII object wrapping a full-expression or block scope, and handling /// RAII object wrapping a full-expression or block scope, and handling
/// the ending of the lifetime of temporaries created within it. /// the ending of the lifetime of temporaries created within it.
template<bool IsFullExpression> template<ScopeKind Kind>
class ScopeRAII { class ScopeRAII {
EvalInfo &Info; EvalInfo &Info;
unsigned OldStackSize; unsigned OldStackSize;
@ -1317,8 +1376,7 @@ namespace {
// for a full-expression scope. // for a full-expression scope.
bool Success = true; bool Success = true;
for (unsigned I = Info.CleanupStack.size(); I > OldStackSize; --I) { for (unsigned I = Info.CleanupStack.size(); I > OldStackSize; --I) {
if (!(IsFullExpression && if (Info.CleanupStack[I - 1].isDestroyedAtEndOf(Kind)) {
Info.CleanupStack[I - 1].isLifetimeExtended())) {
if (!Info.CleanupStack[I - 1].endLifetime(Info, RunDestructors)) { if (!Info.CleanupStack[I - 1].endLifetime(Info, RunDestructors)) {
Success = false; Success = false;
break; break;
@ -1326,18 +1384,20 @@ namespace {
} }
} }
// Compact lifetime-extended cleanups. // Compact any retained cleanups.
auto NewEnd = Info.CleanupStack.begin() + OldStackSize; auto NewEnd = Info.CleanupStack.begin() + OldStackSize;
if (IsFullExpression) if (Kind != ScopeKind::Block)
NewEnd = NewEnd =
std::remove_if(NewEnd, Info.CleanupStack.end(), std::remove_if(NewEnd, Info.CleanupStack.end(), [](Cleanup &C) {
[](Cleanup &C) { return !C.isLifetimeExtended(); }); return C.isDestroyedAtEndOf(Kind);
});
Info.CleanupStack.erase(NewEnd, Info.CleanupStack.end()); Info.CleanupStack.erase(NewEnd, Info.CleanupStack.end());
return Success; return Success;
} }
}; };
typedef ScopeRAII<false> BlockScopeRAII; typedef ScopeRAII<ScopeKind::Block> BlockScopeRAII;
typedef ScopeRAII<true> FullExpressionRAII; typedef ScopeRAII<ScopeKind::FullExpression> FullExpressionRAII;
typedef ScopeRAII<ScopeKind::Call> CallScopeRAII;
} }
bool SubobjectDesignator::checkSubobject(EvalInfo &Info, const Expr *E, bool SubobjectDesignator::checkSubobject(EvalInfo &Info, const Expr *E,
@ -1380,9 +1440,9 @@ void SubobjectDesignator::diagnosePointerArithmetic(EvalInfo &Info,
CallStackFrame::CallStackFrame(EvalInfo &Info, SourceLocation CallLoc, CallStackFrame::CallStackFrame(EvalInfo &Info, SourceLocation CallLoc,
const FunctionDecl *Callee, const LValue *This, const FunctionDecl *Callee, const LValue *This,
APValue *Arguments) CallRef Call)
: Info(Info), Caller(Info.CurrentCall), Callee(Callee), This(This), : Info(Info), Caller(Info.CurrentCall), Callee(Callee), This(This),
Arguments(Arguments), CallLoc(CallLoc), Index(Info.NextCallIndex++) { Arguments(Call), CallLoc(CallLoc), Index(Info.NextCallIndex++) {
Info.CurrentCall = this; Info.CurrentCall = this;
++Info.CallStackDepth; ++Info.CallStackDepth;
} }
@ -1795,14 +1855,33 @@ static void negateAsSigned(APSInt &Int) {
template<typename KeyT> template<typename KeyT>
APValue &CallStackFrame::createTemporary(const KeyT *Key, QualType T, APValue &CallStackFrame::createTemporary(const KeyT *Key, QualType T,
bool IsLifetimeExtended, LValue &LV) { ScopeKind Scope, LValue &LV) {
unsigned Version = getTempVersion(); unsigned Version = getTempVersion();
APValue::LValueBase Base(Key, Index, Version); APValue::LValueBase Base(Key, Index, Version);
LV.set(Base); LV.set(Base);
APValue &Result = Temporaries[MapKeyTy(Key, Version)]; return createLocal(Base, Key, T, Scope);
assert(Result.isAbsent() && "temporary created multiple times"); }
// If we're creating a temporary immediately in the operand of a speculative /// Allocate storage for a parameter of a function call made in this frame.
APValue &CallStackFrame::createParam(CallRef Args, const ParmVarDecl *PVD,
LValue &LV) {
assert(Args.CallIndex == Index && "creating parameter in wrong frame");
APValue::LValueBase Base(PVD, Index, Args.Version);
LV.set(Base);
// We always destroy parameters at the end of the call, even if we'd allow
// them to live to the end of the full-expression at runtime, in order to
// give portable results and match other compilers.
return createLocal(Base, PVD, PVD->getType(), ScopeKind::Call);
}
APValue &CallStackFrame::createLocal(APValue::LValueBase Base, const void *Key,
QualType T, ScopeKind Scope) {
assert(Base.getCallIndex() == Index && "lvalue for wrong frame");
unsigned Version = Base.getVersion();
APValue &Result = Temporaries[MapKeyTy(Key, Version)];
assert(Result.isAbsent() && "local created multiple times");
// If we're creating a local immediately in the operand of a speculative
// evaluation, don't register a cleanup to be run outside the speculative // evaluation, don't register a cleanup to be run outside the speculative
// evaluation context, since we won't actually be able to initialize this // evaluation context, since we won't actually be able to initialize this
// object. // object.
@ -1810,7 +1889,7 @@ APValue &CallStackFrame::createTemporary(const KeyT *Key, QualType T,
if (T.isDestructedType()) if (T.isDestructedType())
Info.noteSideEffect(); Info.noteSideEffect();
} else { } else {
Info.CleanupStack.push_back(Cleanup(&Result, Base, T, IsLifetimeExtended)); Info.CleanupStack.push_back(Cleanup(&Result, Base, T, Scope));
} }
return Result; return Result;
} }
@ -1856,12 +1935,11 @@ void CallStackFrame::describe(raw_ostream &Out) {
Out << ", "; Out << ", ";
const ParmVarDecl *Param = *I; const ParmVarDecl *Param = *I;
if (Arguments) { APValue *V = Info.getParamSlot(Arguments, Param);
const APValue &Arg = Arguments[ArgIndex]; if (V)
Arg.printPretty(Out, Info.Ctx, Param->getType()); V->printPretty(Out, Info.Ctx, Param->getType());
} else { else
Out << "<...>"; Out << "<...>";
}
if (ArgIndex == 0 && IsMemberCall) if (ArgIndex == 0 && IsMemberCall)
Out << "->" << *Callee << '('; Out << "->" << *Callee << '(';
@ -1992,6 +2070,22 @@ static bool HasSameBase(const LValue &A, const LValue &B) {
static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) { static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) {
assert(Base && "no location for a null lvalue"); assert(Base && "no location for a null lvalue");
const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>(); const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>();
// For a parameter, find the corresponding call stack frame (if it still
// exists), and point at the parameter of the function definition we actually
// invoked.
if (auto *PVD = dyn_cast_or_null<ParmVarDecl>(VD)) {
unsigned Idx = PVD->getFunctionScopeIndex();
for (CallStackFrame *F = Info.CurrentCall; F; F = F->Caller) {
if (F->Arguments.CallIndex == Base.getCallIndex() &&
F->Arguments.Version == Base.getVersion() && F->Callee &&
Idx < F->Callee->getNumParams()) {
VD = F->Callee->getParamDecl(Idx);
break;
}
}
}
if (VD) if (VD)
Info.Note(VD->getLocation(), diag::note_declared_at); Info.Note(VD->getLocation(), diag::note_declared_at);
else if (const Expr *E = Base.dyn_cast<const Expr*>()) else if (const Expr *E = Base.dyn_cast<const Expr*>())
@ -3077,33 +3171,22 @@ static bool HandleLValueComplexElement(EvalInfo &Info, const Expr *E,
/// \param Info Information about the ongoing evaluation. /// \param Info Information about the ongoing evaluation.
/// \param E An expression to be used when printing diagnostics. /// \param E An expression to be used when printing diagnostics.
/// \param VD The variable whose initializer should be obtained. /// \param VD The variable whose initializer should be obtained.
/// \param Version The version of the variable within the frame.
/// \param Frame The frame in which the variable was created. Must be null /// \param Frame The frame in which the variable was created. Must be null
/// if this variable is not local to the evaluation. /// if this variable is not local to the evaluation.
/// \param Result Filled in with a pointer to the value of the variable. /// \param Result Filled in with a pointer to the value of the variable.
static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
const VarDecl *VD, CallStackFrame *Frame, const VarDecl *VD, CallStackFrame *Frame,
APValue *&Result, const LValue *LVal) { unsigned Version, APValue *&Result) {
APValue::LValueBase Base(VD, Frame ? Frame->Index : 0, Version);
// If this is a parameter to an active constexpr function call, perform
// argument substitution.
if (const ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(VD)) {
// Assume arguments of a potential constant expression are unknown
// constant expressions.
if (Info.checkingPotentialConstantExpression())
return false;
if (!Frame || !Frame->Arguments) {
Info.FFDiag(E, diag::note_constexpr_function_param_value_unknown) << VD;
return false;
}
Result = &Frame->Arguments[PVD->getFunctionScopeIndex()];
return true;
}
// If this is a local variable, dig out its value. // If this is a local variable, dig out its value.
if (Frame) { if (Frame) {
Result = LVal ? Frame->getTemporary(VD, LVal->getLValueVersion()) Result = Frame->getTemporary(VD, Version);
: Frame->getCurrentTemporary(VD); if (Result)
if (!Result) { return true;
if (!isa<ParmVarDecl>(VD)) {
// Assume variables referenced within a lambda's call operator that were // Assume variables referenced within a lambda's call operator that were
// not declared within the call operator are captures and during checking // not declared within the call operator are captures and during checking
// of a potential constant expression, assume they are unknown constant // of a potential constant expression, assume they are unknown constant
@ -3113,13 +3196,30 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
"missing value for local variable"); "missing value for local variable");
if (Info.checkingPotentialConstantExpression()) if (Info.checkingPotentialConstantExpression())
return false; return false;
// FIXME: implement capture evaluation during constant expr evaluation. // FIXME: This diagnostic is bogus; we do support captures. Is this code
// still reachable at all?
Info.FFDiag(E->getBeginLoc(), Info.FFDiag(E->getBeginLoc(),
diag::note_unimplemented_constexpr_lambda_feature_ast) diag::note_unimplemented_constexpr_lambda_feature_ast)
<< "captures not currently allowed"; << "captures not currently allowed";
return false; return false;
} }
return true; }
if (isa<ParmVarDecl>(VD)) {
// Assume parameters of a potential constant expression are usable in
// constant expressions.
if (!Info.checkingPotentialConstantExpression() ||
!Info.CurrentCall->Callee ||
!Info.CurrentCall->Callee->Equals(VD->getDeclContext())) {
if (Info.getLangOpts().CPlusPlus11) {
Info.FFDiag(E, diag::note_constexpr_function_param_value_unknown)
<< VD;
NoteLValueLocation(Info, Base);
} else {
Info.FFDiag(E);
}
}
return false;
} }
// Dig out the initializer, and use the declaration which it's attached to. // Dig out the initializer, and use the declaration which it's attached to.
@ -3132,7 +3232,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
if (!Info.checkingPotentialConstantExpression()) { if (!Info.checkingPotentialConstantExpression()) {
Info.FFDiag(E, diag::note_constexpr_var_init_unknown, 1) Info.FFDiag(E, diag::note_constexpr_var_init_unknown, 1)
<< VD; << VD;
Info.Note(VD->getLocation(), diag::note_declared_at); NoteLValueLocation(Info, Base);
} }
return false; return false;
} }
@ -3149,7 +3249,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
? diag::note_constexpr_ltor_non_constexpr ? diag::note_constexpr_ltor_non_constexpr
: diag::note_constexpr_ltor_non_integral, 1) : diag::note_constexpr_ltor_non_integral, 1)
<< VD << VD->getType(); << VD << VD->getType();
Info.Note(VD->getLocation(), diag::note_declared_at); NoteLValueLocation(Info, Base);
} }
return false; return false;
} }
@ -3168,7 +3268,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
if (!VD->evaluateValue(Notes)) { if (!VD->evaluateValue(Notes)) {
Info.FFDiag(E, diag::note_constexpr_var_init_non_constant, Info.FFDiag(E, diag::note_constexpr_var_init_non_constant,
Notes.size() + 1) << VD; Notes.size() + 1) << VD;
Info.Note(VD->getLocation(), diag::note_declared_at); NoteLValueLocation(Info, Base);
Info.addNotes(Notes); Info.addNotes(Notes);
return false; return false;
} }
@ -3177,7 +3277,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
if (!VD->checkInitIsICE()) { if (!VD->checkInitIsICE()) {
Info.CCEDiag(E, diag::note_constexpr_var_init_non_constant, Info.CCEDiag(E, diag::note_constexpr_var_init_non_constant,
Notes.size() + 1) << VD; Notes.size() + 1) << VD;
Info.Note(VD->getLocation(), diag::note_declared_at); NoteLValueLocation(Info, Base);
Info.addNotes(Notes); Info.addNotes(Notes);
} }
@ -3185,7 +3285,7 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
// folding. We can't be sure that this is the definition that will be used. // folding. We can't be sure that this is the definition that will be used.
if (VD->isWeak()) { if (VD->isWeak()) {
Info.FFDiag(E, diag::note_constexpr_var_init_weak) << VD; Info.FFDiag(E, diag::note_constexpr_var_init_weak) << VD;
Info.Note(VD->getLocation(), diag::note_declared_at); NoteLValueLocation(Info, Base);
return false; return false;
} }
@ -3899,8 +3999,11 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
// Unless we're looking at a local variable or argument in a constexpr call, // Unless we're looking at a local variable or argument in a constexpr call,
// the variable we're reading must be const. // the variable we're reading must be const.
if (!Frame) { if (!Frame) {
if (Info.getLangOpts().CPlusPlus14 && if (isa<ParmVarDecl>(VD)) {
lifetimeStartedInEvaluation(Info, LVal.Base)) { // Allow evaluateVarDeclInit to diagnose this (or permit it during
// potential constant expression checking).
} else if (Info.getLangOpts().CPlusPlus14 &&
lifetimeStartedInEvaluation(Info, LVal.Base)) {
// OK, we can read and modify an object if we're in the process of // OK, we can read and modify an object if we're in the process of
// evaluating its initializer, because its lifetime began in this // evaluating its initializer, because its lifetime began in this
// evaluation. // evaluation.
@ -3957,7 +4060,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
} }
} }
if (!evaluateVarDeclInit(Info, E, VD, Frame, BaseVal, &LVal)) if (!evaluateVarDeclInit(Info, E, VD, Frame, LVal.getLValueVersion(), BaseVal))
return CompleteObject(); return CompleteObject();
} else if (DynamicAllocLValue DA = LVal.Base.dyn_cast<DynamicAllocLValue>()) { } else if (DynamicAllocLValue DA = LVal.Base.dyn_cast<DynamicAllocLValue>()) {
Optional<DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA); Optional<DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA);
@ -4026,13 +4129,19 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
} }
// In C++14, we can't safely access any mutable state when we might be // In C++14, we can't safely access any mutable state when we might be
// evaluating after an unmodeled side effect. // evaluating after an unmodeled side effect. Parameters are modeled as state
// in the caller, but aren't visible once the call returns, so they can be
// modified in a speculatively-evaluated call.
// //
// FIXME: Not all local state is mutable. Allow local constant subobjects // FIXME: Not all local state is mutable. Allow local constant subobjects
// to be read here (but take care with 'mutable' fields). // to be read here (but take care with 'mutable' fields).
unsigned VisibleDepth = Depth;
if (llvm::isa_and_nonnull<ParmVarDecl>(
LVal.Base.dyn_cast<const ValueDecl *>()))
++VisibleDepth;
if ((Frame && Info.getLangOpts().CPlusPlus14 && if ((Frame && Info.getLangOpts().CPlusPlus14 &&
Info.EvalStatus.HasSideEffects) || Info.EvalStatus.HasSideEffects) ||
(isModification(AK) && Depth < Info.SpeculativeEvaluationDepth)) (isModification(AK) && VisibleDepth < Info.SpeculativeEvaluationDepth))
return CompleteObject(); return CompleteObject();
return CompleteObject(LVal.getLValueBase(), BaseVal, BaseType); return CompleteObject(LVal.getLValueBase(), BaseVal, BaseType);
@ -4643,8 +4752,8 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
return true; return true;
LValue Result; LValue Result;
APValue &Val = APValue &Val = Info.CurrentCall->createTemporary(VD, VD->getType(),
Info.CurrentCall->createTemporary(VD, VD->getType(), true, Result); ScopeKind::Block, Result);
const Expr *InitE = VD->getInit(); const Expr *InitE = VD->getInit();
if (!InitE) if (!InitE)
@ -5789,15 +5898,35 @@ static bool HandleUnionActiveMemberChange(EvalInfo &Info, const Expr *LHSExpr,
return true; return true;
} }
namespace { static bool EvaluateCallArg(const ParmVarDecl *PVD, const Expr *Arg,
typedef SmallVector<APValue, 8> ArgVector; CallRef Call, EvalInfo &Info,
bool NonNull = false) {
LValue LV;
// Create the parameter slot and register its destruction. For a vararg
// argument, create a temporary.
// FIXME: For calling conventions that destroy parameters in the callee,
// should we consider performing destruction when the function returns
// instead?
APValue &V = PVD ? Info.CurrentCall->createParam(Call, PVD, LV)
: Info.CurrentCall->createTemporary(Arg, Arg->getType(),
ScopeKind::Call, LV);
if (!EvaluateInPlace(V, Info, LV, Arg))
return false;
// Passing a null pointer to an __attribute__((nonnull)) parameter results in
// undefined behavior, so is non-constant.
if (NonNull && V.isLValue() && V.isNullPointer()) {
Info.CCEDiag(Arg, diag::note_non_null_attribute_failed);
return false;
}
return true;
} }
/// EvaluateArgs - Evaluate the arguments to a function call. /// Evaluate the arguments to a function call.
static bool EvaluateArgs(ArrayRef<const Expr *> Args, ArgVector &ArgValues, static bool EvaluateArgs(ArrayRef<const Expr *> Args, CallRef Call,
EvalInfo &Info, const FunctionDecl *Callee) { EvalInfo &Info, const FunctionDecl *Callee,
ArgValues.resize(Args.size()); bool RightToLeft = false) {
bool Success = true; bool Success = true;
llvm::SmallBitVector ForbiddenNullArgs; llvm::SmallBitVector ForbiddenNullArgs;
if (Callee->hasAttr<NonNullAttr>()) { if (Callee->hasAttr<NonNullAttr>()) {
@ -5815,36 +5944,53 @@ static bool EvaluateArgs(ArrayRef<const Expr *> Args, ArgVector &ArgValues,
} }
} }
} }
for (unsigned Idx = 0; Idx < Args.size(); Idx++) { for (unsigned I = 0; I < Args.size(); I++) {
if (!Evaluate(ArgValues[Idx], Info, Args[Idx])) { unsigned Idx = RightToLeft ? Args.size() - I - 1 : I;
const ParmVarDecl *PVD =
Idx < Callee->getNumParams() ? Callee->getParamDecl(Idx) : nullptr;
bool NonNull = !ForbiddenNullArgs.empty() && ForbiddenNullArgs[Idx];
if (!EvaluateCallArg(PVD, Args[Idx], Call, Info, NonNull)) {
// If we're checking for a potential constant expression, evaluate all // If we're checking for a potential constant expression, evaluate all
// initializers even if some of them fail. // initializers even if some of them fail.
if (!Info.noteFailure()) if (!Info.noteFailure())
return false; return false;
Success = false; Success = false;
} else if (!ForbiddenNullArgs.empty() &&
ForbiddenNullArgs[Idx] &&
ArgValues[Idx].isLValue() &&
ArgValues[Idx].isNullPointer()) {
Info.CCEDiag(Args[Idx], diag::note_non_null_attribute_failed);
if (!Info.noteFailure())
return false;
Success = false;
} }
} }
return Success; return Success;
} }
/// Perform a trivial copy from Param, which is the parameter of a copy or move
/// constructor or assignment operator.
static bool handleTrivialCopy(EvalInfo &Info, const ParmVarDecl *Param,
const Expr *E, APValue &Result,
bool CopyObjectRepresentation) {
// Find the reference argument.
CallStackFrame *Frame = Info.CurrentCall;
APValue *RefValue = Info.getParamSlot(Frame->Arguments, Param);
if (!RefValue) {
Info.FFDiag(E);
return false;
}
// Copy out the contents of the RHS object.
LValue RefLValue;
RefLValue.setFrom(Info.Ctx, *RefValue);
return handleLValueToRValueConversion(
Info, E, Param->getType().getNonReferenceType(), RefLValue, Result,
CopyObjectRepresentation);
}
/// Evaluate a function call. /// Evaluate a function call.
static bool HandleFunctionCall(SourceLocation CallLoc, static bool HandleFunctionCall(SourceLocation CallLoc,
const FunctionDecl *Callee, const LValue *This, const FunctionDecl *Callee, const LValue *This,
ArrayRef<const Expr *> Args, APValue *ArgValues, ArrayRef<const Expr *> Args, CallRef Call,
const Stmt *Body, EvalInfo &Info, const Stmt *Body, EvalInfo &Info,
APValue &Result, const LValue *ResultSlot) { APValue &Result, const LValue *ResultSlot) {
if (!Info.CheckCallLimit(CallLoc)) if (!Info.CheckCallLimit(CallLoc))
return false; return false;
CallStackFrame Frame(Info, CallLoc, Callee, This, ArgValues); CallStackFrame Frame(Info, CallLoc, Callee, This, Call);
// For a trivial copy or move assignment, perform an APValue copy. This is // For a trivial copy or move assignment, perform an APValue copy. This is
// essential for unions, where the operations performed by the assignment // essential for unions, where the operations performed by the assignment
@ -5859,11 +6005,9 @@ static bool HandleFunctionCall(SourceLocation CallLoc,
isReadByLvalueToRvalueConversion(MD->getParent())))) { isReadByLvalueToRvalueConversion(MD->getParent())))) {
assert(This && assert(This &&
(MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator())); (MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()));
LValue RHS;
RHS.setFrom(Info.Ctx, ArgValues[0]);
APValue RHSValue; APValue RHSValue;
if (!handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(), RHS, if (!handleTrivialCopy(Info, MD->getParamDecl(0), Args[0], RHSValue,
RHSValue, MD->getParent()->isUnion())) MD->getParent()->isUnion()))
return false; return false;
if (Info.getLangOpts().CPlusPlus20 && MD->isTrivial() && if (Info.getLangOpts().CPlusPlus20 && MD->isTrivial() &&
!HandleUnionActiveMemberChange(Info, Args[0], *This)) !HandleUnionActiveMemberChange(Info, Args[0], *This))
@ -5897,7 +6041,7 @@ static bool HandleFunctionCall(SourceLocation CallLoc,
/// Evaluate a constructor call. /// Evaluate a constructor call.
static bool HandleConstructorCall(const Expr *E, const LValue &This, static bool HandleConstructorCall(const Expr *E, const LValue &This,
APValue *ArgValues, CallRef Call,
const CXXConstructorDecl *Definition, const CXXConstructorDecl *Definition,
EvalInfo &Info, APValue &Result) { EvalInfo &Info, APValue &Result) {
SourceLocation CallLoc = E->getExprLoc(); SourceLocation CallLoc = E->getExprLoc();
@ -5914,7 +6058,7 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
Info, Info,
ObjectUnderConstruction{This.getLValueBase(), This.Designator.Entries}, ObjectUnderConstruction{This.getLValueBase(), This.Designator.Entries},
RD->getNumBases()); RD->getNumBases());
CallStackFrame Frame(Info, CallLoc, Definition, &This, ArgValues); CallStackFrame Frame(Info, CallLoc, Definition, &This, Call);
// FIXME: Creating an APValue just to hold a nonexistent return value is // FIXME: Creating an APValue just to hold a nonexistent return value is
// wasteful. // wasteful.
@ -5945,11 +6089,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
(Definition->getParent()->isUnion() || (Definition->getParent()->isUnion() ||
(Definition->isTrivial() && (Definition->isTrivial() &&
isReadByLvalueToRvalueConversion(Definition->getParent())))) { isReadByLvalueToRvalueConversion(Definition->getParent())))) {
LValue RHS; return handleTrivialCopy(Info, Definition->getParamDecl(0), E, Result,
RHS.setFrom(Info.Ctx, ArgValues[0]); Definition->getParent()->isUnion());
return handleLValueToRValueConversion(
Info, E, Definition->getParamDecl(0)->getType().getNonReferenceType(),
RHS, Result, Definition->getParent()->isUnion());
} }
// Reserve space for the struct members. // Reserve space for the struct members.
@ -6108,12 +6249,13 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
ArrayRef<const Expr*> Args, ArrayRef<const Expr*> Args,
const CXXConstructorDecl *Definition, const CXXConstructorDecl *Definition,
EvalInfo &Info, APValue &Result) { EvalInfo &Info, APValue &Result) {
ArgVector ArgValues(Args.size()); CallScopeRAII CallScope(Info);
if (!EvaluateArgs(Args, ArgValues, Info, Definition)) CallRef Call = Info.CurrentCall->createCall(Definition);
if (!EvaluateArgs(Args, Call, Info, Definition))
return false; return false;
return HandleConstructorCall(E, This, ArgValues.data(), Definition, return HandleConstructorCall(E, This, Call, Definition, Info, Result) &&
Info, Result); CallScope.destroy();
} }
static bool HandleDestructionImpl(EvalInfo &Info, SourceLocation CallLoc, static bool HandleDestructionImpl(EvalInfo &Info, SourceLocation CallLoc,
@ -6209,7 +6351,7 @@ static bool HandleDestructionImpl(EvalInfo &Info, SourceLocation CallLoc,
if (!CheckConstexprFunction(Info, CallLoc, DD, Definition, Body)) if (!CheckConstexprFunction(Info, CallLoc, DD, Definition, Body))
return false; return false;
CallStackFrame Frame(Info, CallLoc, Definition, &This, nullptr); CallStackFrame Frame(Info, CallLoc, Definition, &This, CallRef());
// We're now in the period of destruction of this object. // We're now in the period of destruction of this object.
unsigned BasesLeft = RD->getNumBases(); unsigned BasesLeft = RD->getNumBases();
@ -7197,8 +7339,8 @@ public:
LValue CommonLV; LValue CommonLV;
if (!Evaluate(Info.CurrentCall->createTemporary( if (!Evaluate(Info.CurrentCall->createTemporary(
E->getOpaqueValue(), E->getOpaqueValue(),
getStorageType(Info.Ctx, E->getOpaqueValue()), false, getStorageType(Info.Ctx, E->getOpaqueValue()),
CommonLV), ScopeKind::FullExpression, CommonLV),
Info, E->getCommon())) Info, E->getCommon()))
return false; return false;
@ -7262,7 +7404,8 @@ public:
LValue LV; LValue LV;
if (!Evaluate(Info.CurrentCall->createTemporary( if (!Evaluate(Info.CurrentCall->createTemporary(
OVE, getStorageType(Info.Ctx, OVE), false, LV), OVE, getStorageType(Info.Ctx, OVE),
ScopeKind::FullExpression, LV),
Info, OVE->getSourceExpr())) Info, OVE->getSourceExpr()))
return false; return false;
} else if (SemE == E->getResultExpr()) { } else if (SemE == E->getResultExpr()) {
@ -7285,6 +7428,8 @@ public:
bool handleCallExpr(const CallExpr *E, APValue &Result, bool handleCallExpr(const CallExpr *E, APValue &Result,
const LValue *ResultSlot) { const LValue *ResultSlot) {
CallScopeRAII CallScope(Info);
const Expr *Callee = E->getCallee()->IgnoreParens(); const Expr *Callee = E->getCallee()->IgnoreParens();
QualType CalleeType = Callee->getType(); QualType CalleeType = Callee->getType();
@ -7293,7 +7438,8 @@ public:
auto Args = llvm::makeArrayRef(E->getArgs(), E->getNumArgs()); auto Args = llvm::makeArrayRef(E->getArgs(), E->getNumArgs());
bool HasQualifier = false; bool HasQualifier = false;
ArgVector ArgValues; CallRef Call;
bool EvaluatedArgs = false;
// Extract function decl and 'this' pointer from the callee. // Extract function decl and 'this' pointer from the callee.
if (CalleeType->isSpecificBuiltinType(BuiltinType::BoundMember)) { if (CalleeType->isSpecificBuiltinType(BuiltinType::BoundMember)) {
@ -7326,14 +7472,14 @@ public:
return Error(Callee); return Error(Callee);
FD = Member; FD = Member;
} else if (CalleeType->isFunctionPointerType()) { } else if (CalleeType->isFunctionPointerType()) {
LValue Call; LValue CalleeLV;
if (!EvaluatePointer(Callee, Call, Info)) if (!EvaluatePointer(Callee, CalleeLV, Info))
return false; return false;
if (!Call.getLValueOffset().isZero()) if (!CalleeLV.getLValueOffset().isZero())
return Error(Callee); return Error(Callee);
FD = dyn_cast_or_null<FunctionDecl>( FD = dyn_cast_or_null<FunctionDecl>(
Call.getLValueBase().dyn_cast<const ValueDecl*>()); CalleeLV.getLValueBase().dyn_cast<const ValueDecl *>());
if (!FD) if (!FD)
return Error(Callee); return Error(Callee);
// Don't call function pointers which have been cast to some other type. // Don't call function pointers which have been cast to some other type.
@ -7348,15 +7494,11 @@ public:
auto *OCE = dyn_cast<CXXOperatorCallExpr>(E); auto *OCE = dyn_cast<CXXOperatorCallExpr>(E);
if (OCE && OCE->isAssignmentOp()) { if (OCE && OCE->isAssignmentOp()) {
assert(Args.size() == 2 && "wrong number of arguments in assignment"); assert(Args.size() == 2 && "wrong number of arguments in assignment");
if (isa<CXXMethodDecl>(FD)) { Call = Info.CurrentCall->createCall(FD);
// Args[0] is the object argument. if (!EvaluateArgs(isa<CXXMethodDecl>(FD) ? Args.slice(1) : Args, Call,
if (!EvaluateArgs({Args[1]}, ArgValues, Info, FD)) Info, FD, /*RightToLeft=*/true))
return false; return false;
} else { EvaluatedArgs = true;
if (!EvaluateArgs({Args[1], Args[0]}, ArgValues, Info, FD))
return false;
std::swap(ArgValues[0], ArgValues[1]);
}
} }
// Overloaded operator calls to member functions are represented as normal // Overloaded operator calls to member functions are represented as normal
@ -7413,18 +7555,20 @@ public:
if (!HandleOperatorNewCall(Info, E, Ptr)) if (!HandleOperatorNewCall(Info, E, Ptr))
return false; return false;
Ptr.moveInto(Result); Ptr.moveInto(Result);
return true; return CallScope.destroy();
} else { } else {
return HandleOperatorDeleteCall(Info, E); return HandleOperatorDeleteCall(Info, E) && CallScope.destroy();
} }
} }
} else } else
return Error(E); return Error(E);
// Evaluate the arguments now if we've not already done so. // Evaluate the arguments now if we've not already done so.
if (ArgValues.empty() && !Args.empty() && if (!Call) {
!EvaluateArgs(Args, ArgValues, Info, FD)) Call = Info.CurrentCall->createCall(FD);
return false; if (!EvaluateArgs(Args, Call, Info, FD))
return false;
}
SmallVector<QualType, 4> CovariantAdjustmentPath; SmallVector<QualType, 4> CovariantAdjustmentPath;
if (This) { if (This) {
@ -7447,17 +7591,17 @@ public:
// Destructor calls are different enough that they have their own codepath. // Destructor calls are different enough that they have their own codepath.
if (auto *DD = dyn_cast<CXXDestructorDecl>(FD)) { if (auto *DD = dyn_cast<CXXDestructorDecl>(FD)) {
assert(This && "no 'this' pointer for destructor call"); assert(This && "no 'this' pointer for destructor call");
assert(ArgValues.empty() && "unexpected destructor arguments");
return HandleDestruction(Info, E, *This, return HandleDestruction(Info, E, *This,
Info.Ctx.getRecordType(DD->getParent())); Info.Ctx.getRecordType(DD->getParent())) &&
CallScope.destroy();
} }
const FunctionDecl *Definition = nullptr; const FunctionDecl *Definition = nullptr;
Stmt *Body = FD->getBody(Definition); Stmt *Body = FD->getBody(Definition);
if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition, Body) || if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition, Body) ||
!HandleFunctionCall(E->getExprLoc(), Definition, This, Args, !HandleFunctionCall(E->getExprLoc(), Definition, This, Args, Call,
ArgValues.data(), Body, Info, Result, ResultSlot)) Body, Info, Result, ResultSlot))
return false; return false;
if (!CovariantAdjustmentPath.empty() && if (!CovariantAdjustmentPath.empty() &&
@ -7465,7 +7609,7 @@ public:
CovariantAdjustmentPath)) CovariantAdjustmentPath))
return false; return false;
return true; return CallScope.destroy();
} }
bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) { bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
@ -7929,31 +8073,45 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
return true; return true;
} }
} }
CallStackFrame *Frame = nullptr; CallStackFrame *Frame = nullptr;
if (VD->hasLocalStorage() && Info.CurrentCall->Index > 1) { unsigned Version = 0;
if (VD->hasLocalStorage()) {
// Only if a local variable was declared in the function currently being // Only if a local variable was declared in the function currently being
// evaluated, do we expect to be able to find its value in the current // evaluated, do we expect to be able to find its value in the current
// frame. (Otherwise it was likely declared in an enclosing context and // frame. (Otherwise it was likely declared in an enclosing context and
// could either have a valid evaluatable value (for e.g. a constexpr // could either have a valid evaluatable value (for e.g. a constexpr
// variable) or be ill-formed (and trigger an appropriate evaluation // variable) or be ill-formed (and trigger an appropriate evaluation
// diagnostic)). // diagnostic)).
if (Info.CurrentCall->Callee && CallStackFrame *CurrFrame = Info.CurrentCall;
Info.CurrentCall->Callee->Equals(VD->getDeclContext())) { if (CurrFrame->Callee && CurrFrame->Callee->Equals(VD->getDeclContext())) {
Frame = Info.CurrentCall; // Function parameters are stored in some caller's frame. (Usually the
// immediate caller, but for an inherited constructor they may be more
// distant.)
if (auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
if (CurrFrame->Arguments) {
VD = CurrFrame->Arguments.getOrigParam(PVD);
Frame =
Info.getCallFrameAndDepth(CurrFrame->Arguments.CallIndex).first;
Version = CurrFrame->Arguments.Version;
}
} else {
Frame = CurrFrame;
Version = CurrFrame->getCurrentTemporaryVersion(VD);
}
} }
} }
if (!VD->getType()->isReferenceType()) { if (!VD->getType()->isReferenceType()) {
if (Frame) { if (Frame) {
Result.set({VD, Frame->Index, Result.set({VD, Frame->Index, Version});
Info.CurrentCall->getCurrentTemporaryVersion(VD)});
return true; return true;
} }
return Success(VD); return Success(VD);
} }
APValue *V; APValue *V;
if (!evaluateVarDeclInit(Info, E, VD, Frame, V, nullptr)) if (!evaluateVarDeclInit(Info, E, VD, Frame, Version, V))
return false; return false;
if (!V->hasValue()) { if (!V->hasValue()) {
// FIXME: Is it possible for V to be indeterminate here? If so, we should // FIXME: Is it possible for V to be indeterminate here? If so, we should
@ -7983,12 +8141,16 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr(
// value for use outside this evaluation. // value for use outside this evaluation.
APValue *Value; APValue *Value;
if (E->getStorageDuration() == SD_Static) { if (E->getStorageDuration() == SD_Static) {
// FIXME: What about SD_Thread?
Value = E->getOrCreateValue(true); Value = E->getOrCreateValue(true);
*Value = APValue(); *Value = APValue();
Result.set(E); Result.set(E);
} else { } else {
Value = &Info.CurrentCall->createTemporary( Value = &Info.CurrentCall->createTemporary(
E, E->getType(), E->getStorageDuration() == SD_Automatic, Result); E, E->getType(),
E->getStorageDuration() == SD_FullExpression ? ScopeKind::FullExpression
: ScopeKind::Block,
Result);
} }
QualType Type = Inner->getType(); QualType Type = Inner->getType();
@ -8542,7 +8704,7 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
return false; return false;
} else { } else {
APValue &Value = Info.CurrentCall->createTemporary( APValue &Value = Info.CurrentCall->createTemporary(
SubExpr, SubExpr->getType(), false, Result); SubExpr, SubExpr->getType(), ScopeKind::FullExpression, Result);
if (!EvaluateInPlace(Value, Info, Result, SubExpr)) if (!EvaluateInPlace(Value, Info, Result, SubExpr))
return false; return false;
} }
@ -9795,8 +9957,8 @@ public:
/// Visit an expression which constructs the value of this temporary. /// Visit an expression which constructs the value of this temporary.
bool VisitConstructExpr(const Expr *E) { bool VisitConstructExpr(const Expr *E) {
APValue &Value = APValue &Value = Info.CurrentCall->createTemporary(
Info.CurrentCall->createTemporary(E, E->getType(), false, Result); E, E->getType(), ScopeKind::FullExpression, Result);
return EvaluateInPlace(Value, Info, Result, E); return EvaluateInPlace(Value, Info, Result, E);
} }
@ -10233,8 +10395,8 @@ bool ArrayExprEvaluator::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E) {
if (E->getCommonExpr() && if (E->getCommonExpr() &&
!Evaluate(Info.CurrentCall->createTemporary( !Evaluate(Info.CurrentCall->createTemporary(
E->getCommonExpr(), E->getCommonExpr(),
getStorageType(Info.Ctx, E->getCommonExpr()), false, getStorageType(Info.Ctx, E->getCommonExpr()),
CommonLV), ScopeKind::FullExpression, CommonLV),
Info, E->getCommonExpr()->getSourceExpr())) Info, E->getCommonExpr()->getSourceExpr()))
return false; return false;
@ -14226,13 +14388,14 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
} else if (T->isArrayType()) { } else if (T->isArrayType()) {
LValue LV; LValue LV;
APValue &Value = APValue &Value =
Info.CurrentCall->createTemporary(E, T, false, LV); Info.CurrentCall->createTemporary(E, T, ScopeKind::FullExpression, LV);
if (!EvaluateArray(E, LV, Value, Info)) if (!EvaluateArray(E, LV, Value, Info))
return false; return false;
Result = Value; Result = Value;
} else if (T->isRecordType()) { } else if (T->isRecordType()) {
LValue LV; LValue LV;
APValue &Value = Info.CurrentCall->createTemporary(E, T, false, LV); APValue &Value =
Info.CurrentCall->createTemporary(E, T, ScopeKind::FullExpression, LV);
if (!EvaluateRecord(E, LV, Value, Info)) if (!EvaluateRecord(E, LV, Value, Info))
return false; return false;
Result = Value; Result = Value;
@ -14246,7 +14409,8 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
QualType Unqual = T.getAtomicUnqualifiedType(); QualType Unqual = T.getAtomicUnqualifiedType();
if (Unqual->isArrayType() || Unqual->isRecordType()) { if (Unqual->isArrayType() || Unqual->isRecordType()) {
LValue LV; LValue LV;
APValue &Value = Info.CurrentCall->createTemporary(E, Unqual, false, LV); APValue &Value = Info.CurrentCall->createTemporary(
E, Unqual, ScopeKind::FullExpression, LV);
if (!EvaluateAtomic(E, &LV, Value, Info)) if (!EvaluateAtomic(E, &LV, Value, Info))
return false; return false;
} else { } else {
@ -15255,14 +15419,20 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
Info.EvalStatus.HasSideEffects = false; Info.EvalStatus.HasSideEffects = false;
} }
ArgVector ArgValues(Args.size()); CallRef Call = Info.CurrentCall->createCall(Callee);
for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end(); for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end();
I != E; ++I) { I != E; ++I) {
unsigned Idx = I - Args.begin();
if (Idx >= Callee->getNumParams())
break;
const ParmVarDecl *PVD = Callee->getParamDecl(Idx);
if ((*I)->isValueDependent() || if ((*I)->isValueDependent() ||
!Evaluate(ArgValues[I - Args.begin()], Info, *I) || !EvaluateCallArg(PVD, *I, Call, Info) ||
Info.EvalStatus.HasSideEffects) Info.EvalStatus.HasSideEffects) {
// If evaluation fails, throw away the argument entirely. // If evaluation fails, throw away the argument entirely.
ArgValues[I - Args.begin()] = APValue(); if (APValue *Slot = Info.getParamSlot(Call, PVD))
*Slot = APValue();
}
// Ignore any side-effects from a failed evaluation. This is safe because // Ignore any side-effects from a failed evaluation. This is safe because
// they can't interfere with any other argument evaluation. // they can't interfere with any other argument evaluation.
@ -15275,8 +15445,7 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
Info.EvalStatus.HasSideEffects = false; Info.EvalStatus.HasSideEffects = false;
// Build fake call to Callee. // Build fake call to Callee.
CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr, CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr, Call);
ArgValues.data());
// FIXME: Missing ExprWithCleanups in enable_if conditions? // FIXME: Missing ExprWithCleanups in enable_if conditions?
FullExpressionRAII Scope(Info); FullExpressionRAII Scope(Info);
return Evaluate(Value, Info, this) && Scope.destroy() && return Evaluate(Value, Info, this) && Scope.destroy() &&
@ -15334,8 +15503,7 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
} else { } else {
SourceLocation Loc = FD->getLocation(); SourceLocation Loc = FD->getLocation();
HandleFunctionCall(Loc, FD, (MD && MD->isInstance()) ? &This : nullptr, HandleFunctionCall(Loc, FD, (MD && MD->isInstance()) ? &This : nullptr,
Args, /*ArgValues*/ nullptr, FD->getBody(), Info, Args, CallRef(), FD->getBody(), Info, Scratch, nullptr);
Scratch, nullptr);
} }
return Diags.empty(); return Diags.empty();
@ -15357,8 +15525,7 @@ bool Expr::isPotentialConstantExprUnevaluated(Expr *E,
Info.CheckingPotentialConstantExpression = true; Info.CheckingPotentialConstantExpression = true;
// Fabricate a call stack frame to give the arguments a plausible cover story. // Fabricate a call stack frame to give the arguments a plausible cover story.
CallStackFrame Frame(Info, SourceLocation(), FD, /*This*/ nullptr, CallStackFrame Frame(Info, SourceLocation(), FD, /*This*/ nullptr, CallRef());
/*ArgValues*/ nullptr);
APValue ResultScratch; APValue ResultScratch;
Evaluate(ResultScratch, Info, E); Evaluate(ResultScratch, Info, E);

View File

@ -10,7 +10,7 @@ namespace M {
struct NonLiteral { struct NonLiteral {
NonLiteral() {} NonLiteral() {}
NonLiteral(int) {} // expected-note 2{{here}} NonLiteral(int) {}
operator int() const { return 0; } operator int() const { return 0; }
}; };
struct Literal { struct Literal {
@ -42,8 +42,8 @@ template<typename ...P> struct ConstexprCtor {
}; };
constexpr ConstexprCtor<> f1() { return {}; } // ok constexpr ConstexprCtor<> f1() { return {}; } // ok
constexpr ConstexprCtor<int> f2() { return 0; } // ok constexpr ConstexprCtor<int> f2() { return 0; } // ok
constexpr ConstexprCtor<NonLiteral> f3() { return { 0 }; } // expected-error {{never produces a constant expression}} expected-note {{non-constexpr constructor 'NonLiteral}} constexpr ConstexprCtor<NonLiteral> f3() { return { 0 }; } // expected-error {{never produces a constant expression}} expected-note {{non-literal type 'NonLiteral}}
constexpr ConstexprCtor<int, NonLiteral> f4() { return { 0, 0 }; } // expected-error {{never produces a constant expression}} expected-note {{non-constexpr constructor 'NonLiteral}} constexpr ConstexprCtor<int, NonLiteral> f4() { return { 0, 0 }; } // expected-error {{never produces a constant expression}} expected-note {{non-literal type 'NonLiteral}}
struct VirtBase : virtual S {}; // expected-note {{here}} struct VirtBase : virtual S {}; // expected-note {{here}}

View File

@ -55,7 +55,7 @@ namespace noex {
struct A {}; struct A {};
void g1() noexcept(A()); // expected-error {{not contextually convertible}} void g1() noexcept(A()); // expected-error {{not contextually convertible}}
void g2(bool b) noexcept(b); // expected-error {{argument to noexcept specifier must be a constant expression}} expected-note {{read of non-const variable 'b'}} expected-note {{here}} void g2(bool b) noexcept(b); // expected-error {{argument to noexcept specifier must be a constant expression}} expected-note {{function parameter 'b' with unknown value}} expected-note {{here}}
} }

View File

@ -62,11 +62,11 @@ namespace NonConstExprReturn {
constexpr const int *address_of(const int &a) { constexpr const int *address_of(const int &a) {
return &a; return &a;
} }
constexpr const int *return_param(int n) { // expected-note {{declared here}} constexpr const int *return_param(int n) {
return address_of(n); return address_of(n);
} }
struct S { struct S {
int n : *return_param(0); // expected-error {{constant expression}} expected-note {{read of variable whose lifetime has ended}} int n : *return_param(0); // expected-error {{constant expression}} expected-note {{read of object outside its lifetime}}
}; };
} }
@ -427,13 +427,12 @@ namespace PseudoDtor {
int n : (k.~I(), 1); // expected-error {{constant expression}} expected-note {{visible outside that expression}} int n : (k.~I(), 1); // expected-error {{constant expression}} expected-note {{visible outside that expression}}
}; };
// FIXME: It's unclear whether this should be accepted in C++20 mode. The parameter is destroyed twice here. constexpr int f(int a = 1) { // cxx11-error {{constant expression}} expected-note {{destroying object 'a' whose lifetime has already ended}}
constexpr int f(int a = 1) { // cxx11-error {{constant expression}}
return ( return (
a.~I(), // cxx11-note 2{{pseudo-destructor}} a.~I(), // cxx11-note {{pseudo-destructor}}
0); 0);
} }
static_assert(f() == 0, ""); // cxx11-error {{constant expression}} cxx11-note {{in call}} static_assert(f() == 0, ""); // expected-error {{constant expression}}
// This is OK in C++20: the union has no active member after the // This is OK in C++20: the union has no active member after the
// pseudo-destructor call, so the union destructor has no effect. // pseudo-destructor call, so the union destructor has no effect.

View File

@ -65,7 +65,7 @@ int tmain(int argc, char **argv) { // expected-note {{declared here}}
foo(); foo();
#pragma omp critical (name2) hint(+ // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} #pragma omp critical (name2) hint(+ // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}}
foo(); foo();
#pragma omp critical (name2) hint(argc) // expected-error {{integral constant expression}} expected-note {{read of non-const variable 'argc' is not allowed in a constant expression}} #pragma omp critical (name2) hint(argc) // expected-error {{integral constant expression}} expected-note 0+{{constant expression}}
foo(); foo();
#pragma omp critical (name) hint(N) // expected-error {{argument to 'hint' clause must be a strictly positive integer value}} expected-error {{constructs with the same name must have a 'hint' clause with the same value}} expected-note {{'hint' clause with value '4'}} #pragma omp critical (name) hint(N) // expected-error {{argument to 'hint' clause must be a strictly positive integer value}} expected-error {{constructs with the same name must have a 'hint' clause with the same value}} expected-note {{'hint' clause with value '4'}}
foo(); foo();
@ -128,7 +128,7 @@ int main(int argc, char **argv) { // expected-note {{declared here}}
foo(); foo();
#pragma omp critical (name2) hint(+ // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} #pragma omp critical (name2) hint(+ // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}}
foo(); foo();
#pragma omp critical (name2) hint(argc) // expected-error {{integral constant expression}} expected-note {{read of non-const variable 'argc' is not allowed in a constant expression}} #pragma omp critical (name2) hint(argc) // expected-error {{integral constant expression}} expected-note 0+{{constant expression}}
foo(); foo();
#pragma omp critical (name) hint(23) // expected-note {{previous 'hint' clause with value '23'}} #pragma omp critical (name) hint(23) // expected-note {{previous 'hint' clause with value '23'}}
foo(); foo();

View File

@ -39,7 +39,7 @@ T tmain(T argc, S **argv) {
#pragma omp target #pragma omp target
#pragma omp teams #pragma omp teams
#pragma omp distribute parallel for simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 2 {{read of non-const variable 'argc' is not allowed in a constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}} #pragma omp distribute parallel for simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}}
for (int i = ST; i < N; i++) for (int i = ST; i < N; i++)
argv[0][i] = argv[0][i] - argv[0][i-ST]; argv[0][i] = argv[0][i] - argv[0][i-ST];

View File

@ -39,7 +39,7 @@ T tmain(T argc, S **argv) {
#pragma omp target #pragma omp target
#pragma omp teams #pragma omp teams
#pragma omp distribute simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 2 {{read of non-const variable 'argc' is not allowed in a constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}} #pragma omp distribute simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}}
for (int i = ST; i < N; i++) for (int i = ST; i < N; i++)
argv[0][i] = argv[0][i] - argv[0][i-ST]; argv[0][i] = argv[0][i] - argv[0][i-ST];

View File

@ -39,7 +39,7 @@ T tmain(T argc, S **argv) {
#pragma omp target #pragma omp target
#pragma omp teams #pragma omp teams
#pragma omp distribute simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 2 {{read of non-const variable 'argc' is not allowed in a constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}} #pragma omp distribute simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}}
for (int i = ST; i < N; i++) for (int i = ST; i < N; i++)
argv[0][i] = argv[0][i] - argv[0][i-ST]; argv[0][i] = argv[0][i] - argv[0][i-ST];

View File

@ -31,7 +31,7 @@ T tmain(T argc, S **argv) {
#pragma omp target teams distribute parallel for simd safelen () // expected-error {{expected expression}} #pragma omp target teams distribute parallel for simd safelen () // expected-error {{expected expression}}
for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST]; for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST];
#pragma omp target teams distribute parallel for simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 2 {{read of non-const variable 'argc' is not allowed in a constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}} #pragma omp target teams distribute parallel for simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}}
for (int i = ST; i < N; i++) for (int i = ST; i < N; i++)
argv[0][i] = argv[0][i] - argv[0][i-ST]; argv[0][i] = argv[0][i] - argv[0][i-ST];

View File

@ -31,7 +31,7 @@ T tmain(T argc, S **argv) {
#pragma omp target teams distribute parallel for simd safelen () // expected-error {{expected expression}} #pragma omp target teams distribute parallel for simd safelen () // expected-error {{expected expression}}
for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST]; for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST];
#pragma omp target teams distribute parallel for simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 2 {{read of non-const variable 'argc' is not allowed in a constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}} #pragma omp target teams distribute parallel for simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}}
for (int i = ST; i < N; i++) for (int i = ST; i < N; i++)
argv[0][i] = argv[0][i] - argv[0][i-ST]; argv[0][i] = argv[0][i] - argv[0][i-ST];

View File

@ -31,7 +31,7 @@ T tmain(T argc, S **argv) {
#pragma omp target teams distribute simd safelen () // expected-error {{expected expression}} #pragma omp target teams distribute simd safelen () // expected-error {{expected expression}}
for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST]; for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST];
#pragma omp target teams distribute simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 2 {{read of non-const variable 'argc' is not allowed in a constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}} #pragma omp target teams distribute simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}}
for (int i = ST; i < N; i++) for (int i = ST; i < N; i++)
argv[0][i] = argv[0][i] - argv[0][i-ST]; argv[0][i] = argv[0][i] - argv[0][i-ST];

View File

@ -31,7 +31,7 @@ T tmain(T argc, S **argv) {
#pragma omp target teams distribute simd safelen () // expected-error {{expected expression}} #pragma omp target teams distribute simd safelen () // expected-error {{expected expression}}
for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST]; for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST];
#pragma omp target teams distribute simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 2 {{read of non-const variable 'argc' is not allowed in a constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}} #pragma omp target teams distribute simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}}
for (int i = ST; i < N; i++) for (int i = ST; i < N; i++)
argv[0][i] = argv[0][i] - argv[0][i-ST]; argv[0][i] = argv[0][i] - argv[0][i-ST];

View File

@ -35,7 +35,7 @@ T tmain(T argc, S **argv) {
for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST]; for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST];
#pragma omp target #pragma omp target
#pragma omp teams distribute parallel for simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 2 {{read of non-const variable 'argc' is not allowed in a constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}} #pragma omp teams distribute parallel for simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}}
for (int i = ST; i < N; i++) for (int i = ST; i < N; i++)
argv[0][i] = argv[0][i] - argv[0][i-ST]; argv[0][i] = argv[0][i] - argv[0][i-ST];

View File

@ -35,7 +35,7 @@ T tmain(T argc, S **argv) {
for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST]; for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST];
#pragma omp target #pragma omp target
#pragma omp teams distribute parallel for simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 2 {{read of non-const variable 'argc' is not allowed in a constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}} #pragma omp teams distribute parallel for simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}}
for (int i = ST; i < N; i++) for (int i = ST; i < N; i++)
argv[0][i] = argv[0][i] - argv[0][i-ST]; argv[0][i] = argv[0][i] - argv[0][i-ST];

View File

@ -35,7 +35,7 @@ T tmain(T argc, S **argv) {
for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST]; for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST];
#pragma omp target #pragma omp target
#pragma omp teams distribute simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 2 {{read of non-const variable 'argc' is not allowed in a constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}} #pragma omp teams distribute simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}}
for (int i = ST; i < N; i++) for (int i = ST; i < N; i++)
argv[0][i] = argv[0][i] - argv[0][i-ST]; argv[0][i] = argv[0][i] - argv[0][i-ST];

View File

@ -35,7 +35,7 @@ T tmain(T argc, S **argv) {
for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST]; for (int i = ST; i < N; i++) argv[0][i] = argv[0][i] - argv[0][i-ST];
#pragma omp target #pragma omp target
#pragma omp teams distribute simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 2 {{read of non-const variable 'argc' is not allowed in a constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}} #pragma omp teams distribute simd safelen (argc // expected-note {{to match this '('}} expected-error 2 {{integral constant expression}} expected-note 0+{{constant expression}} expected-error {{expected ')'}}
for (int i = ST; i < N; i++) for (int i = ST; i < N; i++)
argv[0][i] = argv[0][i] - argv[0][i-ST]; argv[0][i] = argv[0][i] - argv[0][i-ST];

View File

@ -5,7 +5,7 @@ void test(int x, double p) { // expected-note {{declared here}}
dummy = __builtin_expect_with_probability(x > 0, 1, 0.9); dummy = __builtin_expect_with_probability(x > 0, 1, 0.9);
dummy = __builtin_expect_with_probability(x > 0, 1, 1.1); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}} dummy = __builtin_expect_with_probability(x > 0, 1, 1.1); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}}
dummy = __builtin_expect_with_probability(x > 0, 1, -1); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}} dummy = __builtin_expect_with_probability(x > 0, 1, -1); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}}
dummy = __builtin_expect_with_probability(x > 0, 1, p); // expected-error {{probability argument to __builtin_expect_with_probability must be constant floating-point expression}} expected-note {{read of non-constexpr variable 'p' is not allowed in a constant expression}} dummy = __builtin_expect_with_probability(x > 0, 1, p); // expected-error {{probability argument to __builtin_expect_with_probability must be constant floating-point expression}} expected-note {{function parameter 'p' with unknown value}}
dummy = __builtin_expect_with_probability(x > 0, 1, "aa"); // expected-error {{cannot initialize a parameter of type 'double' with an lvalue of type 'const char [3]'}} dummy = __builtin_expect_with_probability(x > 0, 1, "aa"); // expected-error {{cannot initialize a parameter of type 'double' with an lvalue of type 'const char [3]'}}
dummy = __builtin_expect_with_probability(x > 0, 1, __builtin_nan("")); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}} dummy = __builtin_expect_with_probability(x > 0, 1, __builtin_nan("")); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}}
dummy = __builtin_expect_with_probability(x > 0, 1, __builtin_inf()); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}} dummy = __builtin_expect_with_probability(x > 0, 1, __builtin_inf()); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}}

View File

@ -43,7 +43,7 @@ void test(int x, double p) { // expected-note {{declared here}}
dummy = __builtin_expect_with_probability(x > 0, 1, 0.9); dummy = __builtin_expect_with_probability(x > 0, 1, 0.9);
dummy = __builtin_expect_with_probability(x > 0, 1, 1.1); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}} dummy = __builtin_expect_with_probability(x > 0, 1, 1.1); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}}
dummy = __builtin_expect_with_probability(x > 0, 1, -1); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}} dummy = __builtin_expect_with_probability(x > 0, 1, -1); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}}
dummy = __builtin_expect_with_probability(x > 0, 1, p); // expected-error {{probability argument to __builtin_expect_with_probability must be constant floating-point expression}} expected-note {{read of non-constexpr variable 'p' is not allowed in a constant expression}} dummy = __builtin_expect_with_probability(x > 0, 1, p); // expected-error {{probability argument to __builtin_expect_with_probability must be constant floating-point expression}} expected-note {{function parameter 'p'}}
dummy = __builtin_expect_with_probability(x > 0, 1, "aa"); // expected-error {{cannot initialize a parameter of type 'double' with an lvalue of type 'const char [3]'}} dummy = __builtin_expect_with_probability(x > 0, 1, "aa"); // expected-error {{cannot initialize a parameter of type 'double' with an lvalue of type 'const char [3]'}}
dummy = __builtin_expect_with_probability(x > 0, 1, __builtin_nan("")); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}} dummy = __builtin_expect_with_probability(x > 0, 1, __builtin_nan("")); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}}
dummy = __builtin_expect_with_probability(x > 0, 1, __builtin_inf()); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}} dummy = __builtin_expect_with_probability(x > 0, 1, __builtin_inf()); // expected-error {{probability argument to __builtin_expect_with_probability is outside the range [0.0, 1.0]}}

View File

@ -65,7 +65,7 @@ void test10 (int x[*]); /* expected-warning {{variable length arrays are a C99 f
void test11 (int x[static 4]); /* expected-warning {{static array size is a C99 feature}} */ void test11 (int x[static 4]); /* expected-warning {{static array size is a C99 feature}} */
void test12 (int x[const 4]) { /* expected-warning {{qualifier in array size is a C99 feature}} */ void test12 (int x[const 4]) { /* expected-warning {{qualifier in array size is a C99 feature}} */
int Y[x[1]]; /* expected-warning {{variable length arrays are a C99 feature}} expected-note {{parameter 'x'}} */ int Y[x[1]]; /* expected-warning {{variable length arrays are a C99 feature}} */
} }
/* PR4074 */ /* PR4074 */

View File

@ -17,7 +17,7 @@ __host__ __device__ void foo(const T **a) {
constexpr T e = sizeof(a); constexpr T e = sizeof(a);
constexpr T f = **a; constexpr T f = **a;
// expected-error@-1 {{constexpr variable 'f' must be initialized by a constant expression}} // expected-error@-1 {{constexpr variable 'f' must be initialized by a constant expression}}
// expected-note@-2 {{read of non-constexpr variable 'a' is not allowed in a constant expression}} // expected-note@-2 {{}}
a[0] = &b; a[0] = &b;
a[1] = &c; a[1] = &c;
a[2] = &d; a[2] = &d;
@ -30,7 +30,7 @@ __device__ void device_fun(const int **a) {
static constexpr int c = sizeof(a); static constexpr int c = sizeof(a);
constexpr int d = **a; constexpr int d = **a;
// expected-error@-1 {{constexpr variable 'd' must be initialized by a constant expression}} // expected-error@-1 {{constexpr variable 'd' must be initialized by a constant expression}}
// expected-note@-2 {{read of non-constexpr variable 'a' is not allowed in a constant expression}} // expected-note@-2 {{}}
a[0] = &b; a[0] = &b;
a[1] = &c; a[1] = &c;
foo(a); foo(a);
@ -43,7 +43,7 @@ void host_fun(const int **a) {
static constexpr int c = sizeof(a); static constexpr int c = sizeof(a);
constexpr int d = **a; constexpr int d = **a;
// expected-error@-1 {{constexpr variable 'd' must be initialized by a constant expression}} // expected-error@-1 {{constexpr variable 'd' must be initialized by a constant expression}}
// expected-note@-2 {{read of non-constexpr variable 'a' is not allowed in a constant expression}} // expected-note@-2 {{}}
a[0] = &b; a[0] = &b;
a[1] = &c; a[1] = &c;
foo(a); foo(a);
@ -55,7 +55,7 @@ __host__ __device__ void host_device_fun(const int **a) {
static constexpr int c = sizeof(a); static constexpr int c = sizeof(a);
constexpr int d = **a; constexpr int d = **a;
// expected-error@-1 {{constexpr variable 'd' must be initialized by a constant expression}} // expected-error@-1 {{constexpr variable 'd' must be initialized by a constant expression}}
// expected-note@-2 {{read of non-constexpr variable 'a' is not allowed in a constant expression}} // expected-note@-2 {{}}
a[0] = &b; a[0] = &b;
a[1] = &c; a[1] = &c;
foo(a); foo(a);

View File

@ -18,12 +18,12 @@ struct POD {
// We allow VLAs of POD types, only. // We allow VLAs of POD types, only.
void vla(int N) { // expected-note 5{{here}} void vla(int N) { // expected-note 5{{here}}
int array1[N]; // expected-warning{{variable length arrays are a C99 feature}} expected-note {{variable 'N'}} int array1[N]; // expected-warning{{variable length arrays are a C99 feature}} expected-note {{parameter 'N'}}
POD array2[N]; // expected-warning{{variable length arrays are a C99 feature}} expected-note {{variable 'N'}} POD array2[N]; // expected-warning{{variable length arrays are a C99 feature}} expected-note {{parameter 'N'}}
StillPOD array3[N]; // expected-warning{{variable length arrays are a C99 feature}} expected-note {{variable 'N'}} StillPOD array3[N]; // expected-warning{{variable length arrays are a C99 feature}} expected-note {{parameter 'N'}}
StillPOD2 array4[N][3]; // expected-warning{{variable length arrays are a C99 feature}} expected-note {{variable 'N'}} StillPOD2 array4[N][3]; // expected-warning{{variable length arrays are a C99 feature}} expected-note {{parameter 'N'}}
NonPOD array5[N]; // expected-error{{no matching constructor for initialization of 'NonPOD [N]'}} NonPOD array5[N]; // expected-error{{no matching constructor for initialization of 'NonPOD [N]'}}
// expected-warning@-1{{variable length arrays are a C99 feature}} expected-note@-1 {{variable 'N'}} // expected-warning@-1{{variable length arrays are a C99 feature}} expected-note@-1 {{parameter 'N'}}
// expected-note@-16{{candidate constructor not viable}} // expected-note@-16{{candidate constructor not viable}}
// expected-note@-18{{candidate constructor (the implicit copy constructor) not viable}} // expected-note@-18{{candidate constructor (the implicit copy constructor) not viable}}
// expected-note@-19{{candidate constructor (the implicit move constructor) not viable}} // expected-note@-19{{candidate constructor (the implicit move constructor) not viable}}

View File

@ -13,6 +13,7 @@ struct POD {
}; };
// expected-note@* 1+{{read of non-const variable}} // expected-note@* 1+{{read of non-const variable}}
// expected-note@* 1+{{function parameter}}
// expected-note@* 1+{{declared here}} // expected-note@* 1+{{declared here}}
// We allow VLAs of POD types, only. // We allow VLAs of POD types, only.

View File

@ -217,11 +217,11 @@ namespace ParameterScopes {
const int k = 42; const int k = 42;
constexpr const int &ObscureTheTruth(const int &a) { return a; } constexpr const int &ObscureTheTruth(const int &a) { return a; }
constexpr const int &MaybeReturnJunk(bool b, const int a) { // expected-note 2{{declared here}} constexpr const int &MaybeReturnJunk(bool b, const int a) {
return ObscureTheTruth(b ? a : k); return ObscureTheTruth(b ? a : k);
} }
static_assert(MaybeReturnJunk(false, 0) == 42, ""); // ok static_assert(MaybeReturnJunk(false, 0) == 42, ""); // ok
constexpr int a = MaybeReturnJunk(true, 0); // expected-error {{constant expression}} expected-note {{read of variable whose lifetime has ended}} constexpr int a = MaybeReturnJunk(true, 0); // expected-error {{constant expression}} expected-note {{read of object outside its lifetime}}
constexpr const int MaybeReturnNonstaticRef(bool b, const int a) { constexpr const int MaybeReturnNonstaticRef(bool b, const int a) {
return ObscureTheTruth(b ? a : k); return ObscureTheTruth(b ? a : k);
@ -230,7 +230,7 @@ namespace ParameterScopes {
constexpr int b = MaybeReturnNonstaticRef(true, 0); // ok constexpr int b = MaybeReturnNonstaticRef(true, 0); // ok
constexpr int InternalReturnJunk(int n) { constexpr int InternalReturnJunk(int n) {
return MaybeReturnJunk(true, n); // expected-note {{read of variable whose lifetime has ended}} return MaybeReturnJunk(true, n); // expected-note {{read of object outside its lifetime}}
} }
constexpr int n3 = InternalReturnJunk(0); // expected-error {{must be initialized by a constant expression}} expected-note {{in call to 'InternalReturnJunk(0)'}} constexpr int n3 = InternalReturnJunk(0); // expected-error {{must be initialized by a constant expression}} expected-note {{in call to 'InternalReturnJunk(0)'}}
@ -1568,7 +1568,7 @@ namespace RecursiveOpaqueExpr {
namespace VLASizeof { namespace VLASizeof {
void f(int k) { // expected-note {{here}} void f(int k) { // expected-note {{here}}
int arr[k]; // expected-warning {{C99}} expected-note {{non-const variable 'k'}} int arr[k]; // expected-warning {{C99}} expected-note {{function parameter 'k'}}
constexpr int n = 1 + constexpr int n = 1 +
sizeof(arr) // expected-error {{constant expression}} sizeof(arr) // expected-error {{constant expression}}
* 3; * 3;
@ -1928,9 +1928,9 @@ namespace Lifetime {
int n = 0; int n = 0;
constexpr int f() const { return 0; } constexpr int f() const { return 0; }
}; };
constexpr Q *out_of_lifetime(Q q) { return &q; } // expected-warning {{address of stack}} expected-note 2{{declared here}} constexpr Q *out_of_lifetime(Q q) { return &q; } // expected-warning {{address of stack}}
constexpr int k3 = out_of_lifetime({})->n; // expected-error {{constant expression}} expected-note {{read of variable whose lifetime has ended}} constexpr int k3 = out_of_lifetime({})->n; // expected-error {{constant expression}} expected-note {{read of object outside its lifetime}}
constexpr int k4 = out_of_lifetime({})->f(); // expected-error {{constant expression}} expected-note {{member call on variable whose lifetime has ended}} constexpr int k4 = out_of_lifetime({})->f(); // expected-error {{constant expression}} expected-note {{member call on object outside its lifetime}}
constexpr int null = ((Q*)nullptr)->f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced null pointer}} constexpr int null = ((Q*)nullptr)->f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced null pointer}}
@ -2252,7 +2252,7 @@ namespace ns1 {
void f(char c) { //expected-note2{{declared here}} void f(char c) { //expected-note2{{declared here}}
struct X { struct X {
static constexpr char f() { //expected-error{{never produces a constant expression}} static constexpr char f() { //expected-error{{never produces a constant expression}}
return c; //expected-error{{reference to local}} expected-note{{non-const variable}} return c; //expected-error{{reference to local}} expected-note{{function parameter}}
} }
}; };
int I = X::f(); int I = X::f();
@ -2312,7 +2312,7 @@ namespace array_size {
template<typename T> void f1(T t) { template<typename T> void f1(T t) {
constexpr int k = t.size(); constexpr int k = t.size();
} }
template<typename T> void f2(const T &t) { template<typename T> void f2(const T &t) { // expected-note {{declared here}}
constexpr int k = t.size(); // expected-error {{constant}} expected-note {{function parameter 't' with unknown value cannot be used in a constant expression}} constexpr int k = t.size(); // expected-error {{constant}} expected-note {{function parameter 't' with unknown value cannot be used in a constant expression}}
} }
template<typename T> void f3(const T &t) { template<typename T> void f3(const T &t) {

View File

@ -1415,3 +1415,16 @@ namespace PR45350 {
// decreasing address // decreasing address
static_assert(f(6) == 543210); static_assert(f(6) == 543210);
} }
namespace PR47805 {
struct A {
bool bad = true;
constexpr ~A() { if (bad) throw; }
};
constexpr bool f(A a) { a.bad = false; return true; }
constexpr bool b = f(A());
struct B { B *p = this; };
constexpr bool g(B b) { return &b == b.p; }
static_assert(g({}));
}

View File

@ -17,7 +17,7 @@ namespace std {
} }
template<typename T> constexpr bool has_type(...) { return false; } template<typename T> constexpr bool has_type(...) { return false; }
template<typename T> constexpr bool has_type(T) { return true; } template<typename T> constexpr bool has_type(T&) { return true; }
std::initializer_list il = {1, 2, 3, 4, 5}; std::initializer_list il = {1, 2, 3, 4, 5};

View File

@ -238,7 +238,7 @@ constexpr int f_c(int i) {
// expected-note@-1 {{declared here}} // expected-note@-1 {{declared here}}
int t = f(i); int t = f(i);
// expected-error@-1 {{is not a constant expression}} // expected-error@-1 {{is not a constant expression}}
// expected-note@-2 {{read of non-const variable}} // expected-note@-2 {{function parameter}}
return f(0); return f(0);
} }
@ -254,7 +254,7 @@ auto l1 = [](int i) constexpr {
// expected-note@-1 {{declared here}} // expected-note@-1 {{declared here}}
int t = f(i); int t = f(i);
// expected-error@-1 {{is not a constant expression}} // expected-error@-1 {{is not a constant expression}}
// expected-note@-2 {{read of non-const variable}} // expected-note@-2 {{function parameter}}
return f(0); return f(0);
}; };

View File

@ -85,8 +85,7 @@ uint64_t check_integer_overflows(int i) { //expected-note 0+{{declared here}}
// expected-warning@+1 {{overflow in expression; result is 537919488 with type 'int'}} // expected-warning@+1 {{overflow in expression; result is 537919488 with type 'int'}}
case 1 + static_cast<uint64_t>(4609 * 1024 * 1024): case 1 + static_cast<uint64_t>(4609 * 1024 * 1024):
return 7; return 7;
// expected-error@+2 {{expression is not an integral constant expression}} // expected-error@+1 {{expression is not an integral constant expression}}
// expected-note@+1 {{read of non-const variable 'i' is not allowed in a constant expression}}
case ((uint64_t)(4608 * 1024 * 1024 * i)): case ((uint64_t)(4608 * 1024 * 1024 * i)):
return 8; return 8;
// expected-warning@+1 {{overflow in expression; result is 536870912 with type 'int'}} // expected-warning@+1 {{overflow in expression; result is 536870912 with type 'int'}}

View File

@ -23,8 +23,8 @@ void print(int n, int a, int b, int c, int d) {
void test(int n) { void test(int n) {
S array_t[n][n+1]; S array_t[n][n+1];
# ifdef PE # ifdef PE
// expected-error@-2 {{variable length arrays are a C99 feature}} expected-note@-2 {{read of non-const}} expected-note@-3 {{here}} // expected-error@-2 {{variable length arrays are a C99 feature}} expected-note@-2 {{parameter}} expected-note@-3 {{here}}
// expected-error@-3 {{variable length arrays are a C99 feature}} expected-note@-3 {{read of non-const}} expected-note@-4 {{here}} // expected-error@-3 {{variable length arrays are a C99 feature}} expected-note@-3 {{parameter}} expected-note@-4 {{here}}
# endif # endif
int sizeof_S = sizeof(S); int sizeof_S = sizeof(S);
int sizeof_array_t_0_0 = sizeof(array_t[0][0]); int sizeof_array_t_0_0 = sizeof(array_t[0][0]);

View File

@ -1,27 +1,27 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wvla %s // RUN: %clang_cc1 -fsyntax-only -verify -Wvla %s
void test1(int n) { // expected-note {{here}} void test1(int n) { // expected-note {{here}}
int v[n]; // expected-warning {{variable length array}} expected-note {{variable 'n'}} int v[n]; // expected-warning {{variable length array}} expected-note {{parameter 'n'}}
} }
void test2(int n, int v[n]) { // expected-warning {{variable length array}} expected-note {{variable 'n'}} expected-note {{here}} void test2(int n, int v[n]) { // expected-warning {{variable length array}} expected-note {{parameter 'n'}} expected-note {{here}}
} }
void test3(int n, int v[n]); // expected-warning {{variable length array}} expected-note {{variable 'n'}} expected-note {{here}} void test3(int n, int v[n]); // expected-warning {{variable length array}} expected-note {{parameter 'n'}} expected-note {{here}}
template<typename T> template<typename T>
void test4(int n) { // expected-note {{here}} void test4(int n) { // expected-note {{here}}
int v[n]; // expected-warning {{variable length array}} expected-note {{variable 'n'}} int v[n]; // expected-warning {{variable length array}} expected-note {{parameter 'n'}}
} }
template<typename T> template<typename T>
void test5(int n, int v[n]) { // expected-warning {{variable length array}} expected-note {{variable 'n'}} expected-note {{here}} void test5(int n, int v[n]) { // expected-warning {{variable length array}} expected-note {{parameter 'n'}} expected-note {{here}}
} }
template<typename T> template<typename T>
void test6(int n, int v[n]); // expected-warning {{variable length array}} expected-note {{variable 'n'}} expected-note {{here}} void test6(int n, int v[n]); // expected-warning {{variable length array}} expected-note {{parameter 'n'}} expected-note {{here}}
template<typename T> template<typename T>
void test7(int n, T v[n]) { // expected-warning {{variable length array}} expected-note {{variable 'n'}} expected-note {{here}} void test7(int n, T v[n]) { // expected-warning {{variable length array}} expected-note {{parameter 'n'}} expected-note {{here}}
} }

View File

@ -34,12 +34,13 @@ struct Y {
struct Inner : Y { // expected-note {{declared here}} struct Inner : Y { // expected-note {{declared here}}
}; };
bool f(T other) { bool f(T other) { // expected-note {{declared here}}
// We can determine that 'inner' does not exist at parse time, so can // We can determine that 'inner' does not exist at parse time, so can
// perform typo correction in this case. // perform typo correction in this case.
return this->inner<other>::z; // expected-error {{no template named 'inner' in 'Y<T>'; did you mean 'Inner'?}} return this->inner<other>::z; // expected-error {{no template named 'inner' in 'Y<T>'; did you mean 'Inner'?}}
// expected-error@-1 {{constant expression}} expected-note@-1 {{function parameter 'other'}}
} }
}; };
struct Q { constexpr operator int() { return 0; } }; struct Q { constexpr operator int() { return 0; } };
void use_y(Y<Q> x) { x.f(Q()); } void use_y(Y<Q> x) { x.f(Q()); } // expected-note {{instantiation of}}