constexpr: diagnostic improvements for invalid lvalue-to-rvalue conversions in
constant expressions. llvm-svn: 147035
This commit is contained in:
parent
f243e0df95
commit
f2b681b4d8
|
@ -9,6 +9,7 @@
|
|||
|
||||
let Component = "AST" in {
|
||||
|
||||
// Constant expression diagnostics. These (and their users) belong in Sema.
|
||||
//def note_comma_in_ice : Note<
|
||||
// "C does not permit evaluated commas in an integer constant expression">;
|
||||
def note_expr_divide_by_zero : Note<"division by zero">;
|
||||
|
@ -33,8 +34,26 @@ def note_constexpr_past_end : Note<
|
|||
def note_constexpr_var_init_non_constant : Note<
|
||||
"initializer of %0 is not a constant expression">;
|
||||
def note_constexpr_temporary_here : Note<"temporary created here">;
|
||||
def note_constexpr_literal_here : Note<"literal written here">;
|
||||
def note_constexpr_depth_limit_exceeded : Note<
|
||||
"constexpr evaluation exceeded maximum depth of %0 calls">;
|
||||
def note_constexpr_ltor_volatile_type : Note<
|
||||
"read of volatile-qualified type %0 is not allowed in a constant expression">;
|
||||
def note_constexpr_ltor_volatile_obj : Note<
|
||||
"read of volatile %select{temporary|object %1|member %1}0 is not allowed in "
|
||||
"a constant expression">;
|
||||
def note_constexpr_ltor_non_const_int : Note<
|
||||
"read of non-const variable %0 is not allowed in a constant expression">;
|
||||
def note_constexpr_ltor_non_constexpr : Note<
|
||||
"read of non-constexpr variable %0 is not allowed in a constant expression">;
|
||||
def note_constexpr_read_past_end : Note<
|
||||
"read of dereferenced one-past-the-end pointer is not allowed in a "
|
||||
"constant expression">;
|
||||
def note_constexpr_read_inactive_union_member : Note<
|
||||
"read of member %0 of union with %select{active member %2|no active member}1 "
|
||||
"is not allowed in a constant expression">;
|
||||
def note_constexpr_read_uninit : Note<
|
||||
"read of uninitialized object is not allowed in a constant expression">;
|
||||
def note_constexpr_calls_suppressed : Note<
|
||||
"(skipping %0 call%s0 in backtrace; use -fconstexpr-backtrace-limit=0 to "
|
||||
"see all)">;
|
||||
|
|
|
@ -344,7 +344,8 @@ namespace {
|
|||
|
||||
public:
|
||||
/// Diagnose that the evaluation cannot be folded.
|
||||
OptionalDiagnostic Diag(SourceLocation Loc, diag::kind DiagId,
|
||||
OptionalDiagnostic Diag(SourceLocation Loc, diag::kind DiagId
|
||||
= diag::note_invalid_subexpr_in_const_expr,
|
||||
unsigned ExtraNotes = 0) {
|
||||
// If we have a prior diagnostic, it will be noting that the expression
|
||||
// isn't a constant expression. This diagnostic is more important.
|
||||
|
@ -368,7 +369,8 @@ namespace {
|
|||
|
||||
/// Diagnose that the evaluation does not produce a C++11 core constant
|
||||
/// expression.
|
||||
OptionalDiagnostic CCEDiag(SourceLocation Loc, diag::kind DiagId,
|
||||
OptionalDiagnostic CCEDiag(SourceLocation Loc, diag::kind DiagId
|
||||
= diag::note_invalid_subexpr_in_const_expr,
|
||||
unsigned ExtraNotes = 0) {
|
||||
// Don't override a previous diagnostic.
|
||||
if (!EvalStatus.Diag || !EvalStatus.Diag->empty())
|
||||
|
@ -729,7 +731,7 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, const Expr *E,
|
|||
Info.Note(Base.dyn_cast<const Expr*>()->getExprLoc(),
|
||||
diag::note_constexpr_temporary_here);
|
||||
} else {
|
||||
Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
Info.Diag(E->getExprLoc());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1142,10 +1144,16 @@ static unsigned getBaseIndex(const CXXRecordDecl *Derived,
|
|||
static bool ExtractSubobject(EvalInfo &Info, const Expr *E,
|
||||
CCValue &Obj, QualType ObjType,
|
||||
const SubobjectDesignator &Sub, QualType SubType) {
|
||||
if (Sub.Invalid || Sub.OnePastTheEnd) {
|
||||
if (Sub.Invalid) {
|
||||
Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
return false;
|
||||
}
|
||||
if (Sub.OnePastTheEnd) {
|
||||
Info.Diag(E->getExprLoc(), Info.getLangOpts().CPlusPlus0x ?
|
||||
diag::note_constexpr_read_past_end :
|
||||
diag::note_invalid_subexpr_in_const_expr);
|
||||
return false;
|
||||
}
|
||||
if (Sub.Entries.empty())
|
||||
return true;
|
||||
|
||||
|
@ -1159,7 +1167,11 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E,
|
|||
assert(CAT && "vla in literal type?");
|
||||
uint64_t Index = Sub.Entries[I].ArrayIndex;
|
||||
if (CAT->getSize().ule(Index)) {
|
||||
Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
// Note, it should not be possible to form a pointer with a valid
|
||||
// designator which points more than one past the end of the array.
|
||||
Info.Diag(E->getExprLoc(), Info.getLangOpts().CPlusPlus0x ?
|
||||
diag::note_constexpr_read_past_end :
|
||||
diag::note_invalid_subexpr_in_const_expr);
|
||||
return false;
|
||||
}
|
||||
if (O->getArrayInitializedElts() > Index)
|
||||
|
@ -1174,13 +1186,27 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E,
|
|||
const FieldDecl *UnionField = O->getUnionField();
|
||||
if (!UnionField ||
|
||||
UnionField->getCanonicalDecl() != Field->getCanonicalDecl()) {
|
||||
Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
Info.Diag(E->getExprLoc(),
|
||||
diag::note_constexpr_read_inactive_union_member)
|
||||
<< Field << !UnionField << UnionField;
|
||||
return false;
|
||||
}
|
||||
O = &O->getUnionValue();
|
||||
} else
|
||||
O = &O->getStructField(Field->getFieldIndex());
|
||||
ObjType = Field->getType();
|
||||
|
||||
if (ObjType.isVolatileQualified()) {
|
||||
if (Info.getLangOpts().CPlusPlus) {
|
||||
// FIXME: Include a description of the path to the volatile subobject.
|
||||
Info.Diag(E->getExprLoc(), diag::note_constexpr_ltor_volatile_obj, 1)
|
||||
<< 2 << Field;
|
||||
Info.Note(Field->getLocation(), diag::note_declared_at);
|
||||
} else {
|
||||
Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Next subobject is a base class.
|
||||
const CXXRecordDecl *Derived = ObjType->getAsCXXRecordDecl();
|
||||
|
@ -1190,7 +1216,7 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E,
|
|||
}
|
||||
|
||||
if (O->isUninit()) {
|
||||
Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
Info.Diag(E->getExprLoc(), diag::note_constexpr_read_uninit);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1212,12 +1238,29 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E,
|
|||
static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
|
||||
QualType Type,
|
||||
const LValue &LVal, CCValue &RVal) {
|
||||
// In C, an lvalue-to-rvalue conversion is never a constant expression.
|
||||
if (!Info.getLangOpts().CPlusPlus)
|
||||
Info.CCEDiag(Conv->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
|
||||
const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
|
||||
CallStackFrame *Frame = LVal.Frame;
|
||||
SourceLocation Loc = Conv->getExprLoc();
|
||||
|
||||
if (!LVal.Base) {
|
||||
// FIXME: Indirection through a null pointer deserves a specific diagnostic.
|
||||
Info.Diag(Conv->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
Info.Diag(Loc, diag::note_invalid_subexpr_in_const_expr);
|
||||
return false;
|
||||
}
|
||||
|
||||
// C++11 DR1311: An lvalue-to-rvalue conversion on a volatile-qualified type
|
||||
// is not a constant expression (even if the object is non-volatile). We also
|
||||
// apply this rule to C++98, in order to conform to the expected 'volatile'
|
||||
// semantics.
|
||||
if (Type.isVolatileQualified()) {
|
||||
if (Info.getLangOpts().CPlusPlus)
|
||||
Info.Diag(Loc, diag::note_constexpr_ltor_volatile_type) << Type;
|
||||
else
|
||||
Info.Diag(Loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1227,31 +1270,60 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
|
|||
// expressions are constant expressions too. Inside constexpr functions,
|
||||
// parameters are constant expressions even if they're non-const.
|
||||
// In C, such things can also be folded, although they are not ICEs.
|
||||
//
|
||||
// FIXME: volatile-qualified ParmVarDecls need special handling. A literal
|
||||
// interpretation of C++11 suggests that volatile parameters are OK if
|
||||
// they're never read (there's no prohibition against constructing volatile
|
||||
// objects in constant expressions), but lvalue-to-rvalue conversions on
|
||||
// them are not permitted.
|
||||
const VarDecl *VD = dyn_cast<VarDecl>(D);
|
||||
if (!VD || VD->isInvalidDecl()) {
|
||||
Info.Diag(Conv->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
Info.Diag(Loc);
|
||||
return false;
|
||||
}
|
||||
|
||||
// DR1313: If the object is volatile-qualified but the glvalue was not,
|
||||
// behavior is undefined so the result is not a constant expression.
|
||||
QualType VT = VD->getType();
|
||||
if (!isa<ParmVarDecl>(VD)) {
|
||||
if (!IsConstNonVolatile(VT)) {
|
||||
Info.Diag(Conv->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
return false;
|
||||
if (VT.isVolatileQualified()) {
|
||||
if (Info.getLangOpts().CPlusPlus) {
|
||||
Info.Diag(Loc, diag::note_constexpr_ltor_volatile_obj, 1) << 1 << VD;
|
||||
Info.Note(VD->getLocation(), diag::note_declared_at);
|
||||
} else {
|
||||
Info.Diag(Loc);
|
||||
}
|
||||
// FIXME: Allow folding of values of any literal type in all languages.
|
||||
if (!VT->isIntegralOrEnumerationType() && !VT->isRealFloatingType() &&
|
||||
!VD->isConstexpr()) {
|
||||
Info.Diag(Conv->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isa<ParmVarDecl>(VD)) {
|
||||
if (VD->isConstexpr()) {
|
||||
// OK, we can read this variable.
|
||||
} else if (VT->isIntegralOrEnumerationType()) {
|
||||
if (!VT.isConstQualified()) {
|
||||
if (Info.getLangOpts().CPlusPlus) {
|
||||
Info.Diag(Loc, diag::note_constexpr_ltor_non_const_int, 1) << VD;
|
||||
Info.Note(VD->getLocation(), diag::note_declared_at);
|
||||
} else {
|
||||
Info.Diag(Loc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else if (VT->isFloatingType() && VT.isConstQualified()) {
|
||||
// We support folding of const floating-point types, in order to make
|
||||
// static const data members of such types (supported as an extension)
|
||||
// more useful.
|
||||
if (Info.getLangOpts().CPlusPlus0x) {
|
||||
Info.CCEDiag(Loc, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
|
||||
Info.Note(VD->getLocation(), diag::note_declared_at);
|
||||
} else {
|
||||
Info.CCEDiag(Loc);
|
||||
}
|
||||
} else {
|
||||
// FIXME: Allow folding of values of any literal type in all languages.
|
||||
if (Info.getLangOpts().CPlusPlus0x) {
|
||||
Info.Diag(Loc, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
|
||||
Info.Note(VD->getLocation(), diag::note_declared_at);
|
||||
} else {
|
||||
Info.Diag(Loc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!EvaluateVarDeclInit(Info, Conv, VD, Frame, RVal))
|
||||
return false;
|
||||
|
||||
|
@ -1269,6 +1341,17 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
|
|||
Frame = RVal.getLValueFrame();
|
||||
}
|
||||
|
||||
// Volatile temporary objects cannot be read in constant expressions.
|
||||
if (Base->getType().isVolatileQualified()) {
|
||||
if (Info.getLangOpts().CPlusPlus) {
|
||||
Info.Diag(Loc, diag::note_constexpr_ltor_volatile_obj, 1) << 0;
|
||||
Info.Note(Base->getExprLoc(), diag::note_constexpr_temporary_here);
|
||||
} else {
|
||||
Info.Diag(Loc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: Support PredefinedExpr, ObjCEncodeExpr, MakeStringConstant
|
||||
if (const StringLiteral *S = dyn_cast<StringLiteral>(Base)) {
|
||||
const SubobjectDesignator &Designator = LVal.Designator;
|
||||
|
@ -1279,8 +1362,13 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
|
|||
|
||||
assert(Type->isIntegerType() && "string element not integer type");
|
||||
uint64_t Index = Designator.Entries[0].ArrayIndex;
|
||||
if (Index > S->getLength()) {
|
||||
Info.Diag(Conv->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
const ConstantArrayType *CAT =
|
||||
Info.Ctx.getAsConstantArrayType(S->getType());
|
||||
if (Index >= CAT->getSize().getZExtValue()) {
|
||||
// Note, it should not be possible to form a pointer which points more
|
||||
// than one past the end of the array without producing a prior const expr
|
||||
// diagnostic.
|
||||
Info.Diag(Loc, diag::note_constexpr_read_past_end);
|
||||
return false;
|
||||
}
|
||||
APSInt Value(S->getCharByteWidth() * Info.Ctx.getCharWidth(),
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
constexpr int a = 0;
|
||||
extern const int a;
|
||||
|
||||
int i;
|
||||
int i; // expected-note 2{{here}}
|
||||
constexpr int *b = &i;
|
||||
extern int *const b;
|
||||
|
||||
|
@ -21,11 +21,11 @@ constexpr int ni1; // expected-error {{default initialization of an object of co
|
|||
constexpr struct C { C(); } ni2; // expected-error {{constexpr variable 'ni2' must be initialized by a constant expression}} expected-note {{non-literal type 'const struct C' cannot be used in a constant expression}}
|
||||
constexpr double &ni3; // expected-error {{declaration of reference variable 'ni3' requires an initializer}}
|
||||
|
||||
constexpr int nc1 = i; // expected-error {{constexpr variable 'nc1' must be initialized by a constant expression}}
|
||||
constexpr int nc1 = i; // expected-error {{constexpr variable 'nc1' must be initialized by a constant expression}} expected-note {{read of non-const variable 'i' is not allowed in a constant expression}}
|
||||
constexpr C nc2 = C(); // expected-error {{constexpr variable 'nc2' must be initialized by a constant expression}} expected-note {{non-literal type}}
|
||||
int &f(); // expected-note {{declared here}}
|
||||
constexpr int &nc3 = f(); // expected-error {{constexpr variable 'nc3' must be initialized by a constant expression}} expected-note {{non-constexpr function 'f' cannot be used in a constant expression}}
|
||||
constexpr int nc4(i); // expected-error {{constexpr variable 'nc4' must be initialized by a constant expression}}
|
||||
constexpr int nc4(i); // expected-error {{constexpr variable 'nc4' must be initialized by a constant expression}} expected-note {{read of non-const variable 'i' is not allowed in a constant expression}}
|
||||
constexpr C nc5((C())); // expected-error {{constexpr variable 'nc5' must be initialized by a constant expression}} expected-note {{non-literal type 'const C'}}
|
||||
int &f(); // expected-note {{here}}
|
||||
constexpr int &nc6(f()); // expected-error {{constexpr variable 'nc6' must be initialized by a constant expression}} expected-note {{non-constexpr function 'f'}}
|
||||
|
|
|
@ -146,26 +146,50 @@ struct Lambda {
|
|||
//int n : []{ return 1; }();
|
||||
};
|
||||
|
||||
// FIXME:
|
||||
// - an lvalue-to-rvalue conversion (4.1) unless it is applied to
|
||||
//
|
||||
// - a non-volatile glvalue of integral or enumeration type that refers to a
|
||||
// non-volatile const object with a preceding initialization, initialized with
|
||||
// a constant expression [Note: a string literal (2.14.5 [lex.string])
|
||||
// corresponds to an array of such objects. -end note], or
|
||||
//
|
||||
// - a non-volatile glvalue of literal type that refers to a non-volatile
|
||||
// object defined with constexpr, or that refers to a sub-object of such an
|
||||
// object, or
|
||||
//
|
||||
// - a non-volatile glvalue of literal type that refers to a non-volatile
|
||||
// temporary object whose lifetime has not ended, initialized with a constant
|
||||
// expression;
|
||||
namespace LValueToRValue {
|
||||
// - a non-volatile glvalue of integral or enumeration type that refers to a
|
||||
// non-volatile const object with a preceding initialization, initialized
|
||||
// with a constant expression [Note: a string literal (2.14.5 [lex.string])
|
||||
// corresponds to an array of such objects. -end note], or
|
||||
volatile const int vi = 1; // expected-note {{here}}
|
||||
const int ci = 1;
|
||||
volatile const int &vrci = ci;
|
||||
static_assert(vi, ""); // expected-error {{constant expression}} expected-note {{read of volatile-qualified type 'const volatile int'}}
|
||||
static_assert(const_cast<int&>(vi), ""); // expected-error {{constant expression}} expected-note {{read of volatile object 'vi'}}
|
||||
static_assert(vrci, ""); // expected-error {{constant expression}} expected-note {{read of volatile-qualified type}}
|
||||
|
||||
// - a non-volatile glvalue of literal type that refers to a non-volatile
|
||||
// object defined with constexpr, or that refers to a sub-object of such an
|
||||
// object, or
|
||||
struct S {
|
||||
constexpr S(int=0) : i(1), v(1) {}
|
||||
constexpr S(const S &s) : i(2), v(2) {}
|
||||
int i;
|
||||
volatile int v;
|
||||
};
|
||||
constexpr S s;
|
||||
constexpr volatile S vs; // expected-note {{here}}
|
||||
constexpr const volatile S &vrs = s;
|
||||
static_assert(s.i, "");
|
||||
static_assert(s.v, ""); // expected-error {{constant expression}} expected-note {{read of volatile-qualified type}}
|
||||
static_assert(vs.i, ""); // expected-error {{constant expression}} expected-note {{read of volatile-qualified type}}
|
||||
static_assert(const_cast<int&>(vs.i), ""); // expected-error {{constant expression}} expected-note {{read of volatile object 'vs'}}
|
||||
static_assert(vrs.i, ""); // expected-error {{constant expression}} expected-note {{read of volatile-qualified type}}
|
||||
|
||||
// - a non-volatile glvalue of literal type that refers to a non-volatile
|
||||
// temporary object whose lifetime has not ended, initialized with a
|
||||
// constant expression;
|
||||
constexpr volatile S f() { return S(); }
|
||||
static_assert(f().i, ""); // ok! there's no lvalue-to-rvalue conversion here!
|
||||
static_assert(((volatile const S&&)(S)0).i, ""); // expected-error {{constant expression}} expected-note {{subexpression}}
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
//
|
||||
// DR1312: The proposed wording for this defect has issues, so we instead
|
||||
// prohibit casts from pointers to cv void (see core-20842 and core-20845).
|
||||
// DR1312: The proposed wording for this defect has issues, so we ignore this
|
||||
// bullet and instead prohibit casts from pointers to cv void (see core-20842
|
||||
// and core-20845).
|
||||
//
|
||||
// - an lvalue-to-rvalue conversion (4.1 [conv.lval]) that is applied to a
|
||||
// glvalue of type cv1 T that refers to an object of type cv2 U, where T and U
|
||||
|
@ -175,14 +199,13 @@ struct Lambda {
|
|||
// - an lvalue-to-rvalue conversion (4.1) that is applied to a glvalue that
|
||||
// refers to a non-active member of a union or a subobject thereof;
|
||||
|
||||
// FIXME:
|
||||
// - an id-expression that refers to a variable or data member of reference type
|
||||
// unless the reference has a preceding initialization, initialized with a
|
||||
// constant expression;
|
||||
namespace References {
|
||||
const int a = 2;
|
||||
int &b = *const_cast<int*>(&a);
|
||||
int c = 10;
|
||||
int c = 10; // expected-note 2 {{here}}
|
||||
int &d = c;
|
||||
constexpr int e = 42;
|
||||
int &f = const_cast<int&>(e);
|
||||
|
@ -195,8 +218,8 @@ namespace References {
|
|||
struct S {
|
||||
int A : a;
|
||||
int B : b;
|
||||
int C : c; // expected-error {{constant expression}}
|
||||
int D : d; // expected-error {{constant expression}}
|
||||
int C : c; // expected-error {{constant expression}} expected-note {{read of non-const variable 'c'}}
|
||||
int D : d; // expected-error {{constant expression}} expected-note {{read of non-const variable 'c'}}
|
||||
int D2 : &d - &c + 1;
|
||||
int E : e / 2;
|
||||
int F : f - 11;
|
||||
|
|
|
@ -143,8 +143,8 @@ static_assert(F(0) == 0, "");
|
|||
static_assert(F(1, 0) == 1, "");
|
||||
static_assert(F(2, "test") == 2, "");
|
||||
static_assert(F(3, &F) == 3, "");
|
||||
int k = 0;
|
||||
static_assert(F(4, k) == 3, ""); // expected-error {{constant expression}} expected-note {{subexpression}}
|
||||
int k = 0; // expected-note {{here}}
|
||||
static_assert(F(4, k) == 3, ""); // expected-error {{constant expression}} expected-note {{read of non-const variable 'k'}}
|
||||
|
||||
}
|
||||
|
||||
|
@ -362,8 +362,9 @@ static_assert(MangleChars(U"constexpr!") == 1768383, "");
|
|||
|
||||
constexpr char c0 = "nought index"[0];
|
||||
constexpr char c1 = "nice index"[10];
|
||||
constexpr char c2 = "nasty index"[12]; // expected-error {{must be initialized by a constant expression}} expected-warning {{is past the end}}
|
||||
constexpr char c3 = "negative index"[-1]; // expected-error {{must be initialized by a constant expression}} expected-warning {{is before the beginning}}
|
||||
constexpr char c2 = "nasty index"[12]; // expected-error {{must be initialized by a constant expression}} expected-warning {{is past the end}} expected-note {{read of dereferenced one-past-the-end pointer}}
|
||||
// FIXME: block the pointer arithmetic with undefined behavior here
|
||||
constexpr char c3 = "negative index"[-1]; // expected-error {{must be initialized by a constant expression}} expected-warning {{is before the beginning}} expected-note {{read of dereferenced one-past-the-end pointer}}
|
||||
constexpr char c4 = ((char*)(int*)"no reinterpret_casts allowed")[14]; // expected-error {{must be initialized by a constant expression}}
|
||||
|
||||
constexpr const char *p = "test" + 2;
|
||||
|
@ -386,6 +387,10 @@ static_assert(strcmp_ce("hello world", "hello clang") > 0, "");
|
|||
static_assert(strcmp_ce("constexpr", "test") < 0, "");
|
||||
static_assert(strcmp_ce("", " ") < 0, "");
|
||||
|
||||
struct S {
|
||||
int n : "foo"[4]; // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end pointer is not allowed in a constant expression}}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace Array {
|
||||
|
@ -403,7 +408,7 @@ static_assert(sum_xs == 15, "");
|
|||
constexpr int ZipFoldR(int (*F)(int x, int y, int c), int n,
|
||||
const int *xs, const int *ys, int c) {
|
||||
return n ? F(
|
||||
*xs, // expected-note {{subexpression not valid}}
|
||||
*xs, // expected-note {{read of dereferenced one-past-the-end pointer}}
|
||||
*ys,
|
||||
ZipFoldR(F, n-1, xs+1, ys+1, c)) // \
|
||||
expected-note {{in call to 'ZipFoldR(&SubMul, 2, &xs[4], &ys[4], 1)'}} \
|
||||
|
@ -423,20 +428,21 @@ static_assert(ZipFoldR(SubMul, 3, xs+3, ys+3, 1), ""); // \
|
|||
|
||||
constexpr const int *p = xs + 3;
|
||||
constexpr int xs4 = p[1]; // ok
|
||||
constexpr int xs5 = p[2]; // expected-error {{constant expression}}
|
||||
constexpr int xs5 = p[2]; // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}
|
||||
constexpr int xs0 = p[-3]; // ok
|
||||
constexpr int xs_1 = p[-4]; // expected-error {{constant expression}}
|
||||
// FIXME: check pointer arithmetic here
|
||||
constexpr int xs_1 = p[-4]; // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}
|
||||
|
||||
constexpr int zs[2][2][2][2] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
|
||||
static_assert(zs[0][0][0][0] == 1, "");
|
||||
static_assert(zs[1][1][1][1] == 16, "");
|
||||
static_assert(zs[0][0][0][2] == 3, ""); // expected-error {{constant expression}} expected-note {{subexpression}}
|
||||
static_assert(zs[0][0][0][2] == 3, ""); // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}
|
||||
static_assert((&zs[0][0][0][2])[-1] == 2, "");
|
||||
static_assert(**(**(zs + 1) + 1) == 11, "");
|
||||
static_assert(*(&(&(*(*&(&zs[2] - 1)[0] + 2 - 2))[2])[-1][-1] + 1) == 11, "");
|
||||
|
||||
constexpr int fail(const int &p) {
|
||||
return (&p)[64]; // expected-note {{subexpression}}
|
||||
return (&p)[64]; // expected-note {{read of dereferenced one-past-the-end pointer}}
|
||||
}
|
||||
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}} \
|
||||
|
@ -532,10 +538,10 @@ struct G {
|
|||
constexpr G() : t(&t) {}
|
||||
} constexpr g;
|
||||
|
||||
static_assert(g.t.u1.a == 42, ""); // expected-error {{constant expression}} expected-note {{subexpression}}
|
||||
static_assert(g.t.u1.a == 42, ""); // expected-error {{constant expression}} expected-note {{read of member 'a' of union with active member 'b'}}
|
||||
static_assert(g.t.u1.b == 42, "");
|
||||
static_assert(g.t.u2.c == 42, "");
|
||||
static_assert(g.t.u2.d == 42, ""); // expected-error {{constant expression}} expected-note {{subexpression}}
|
||||
static_assert(g.t.u2.d == 42, ""); // expected-error {{constant expression}} expected-note {{read of member 'd' of union with active member 'c'}}
|
||||
|
||||
struct S {
|
||||
int a, b;
|
||||
|
@ -579,10 +585,11 @@ constexpr AggregateInit agg1 = { "hello"[0] };
|
|||
static_assert(strcmp_ce(&agg1.c, "hello") == 0, "");
|
||||
static_assert(agg1.n == 0, "");
|
||||
static_assert(agg1.d == 0.0, "");
|
||||
static_assert(agg1.arr[-1] == 0, ""); // expected-error {{constant expression}} expected-note {{subexpression}}
|
||||
// FIXME: check pointer arithmetic here.
|
||||
static_assert(agg1.arr[-1] == 0, ""); // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end}}
|
||||
static_assert(agg1.arr[0] == 0, "");
|
||||
static_assert(agg1.arr[4] == 0, "");
|
||||
static_assert(agg1.arr[5] == 0, ""); // expected-error {{constant expression}} expected-note {{subexpression}}
|
||||
static_assert(agg1.arr[5] == 0, ""); // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end}}
|
||||
static_assert(agg1.p == nullptr, "");
|
||||
|
||||
namespace SimpleDerivedClass {
|
||||
|
@ -700,9 +707,9 @@ union U {
|
|||
|
||||
constexpr U u[4] = { { .a = 0 }, { .b = 1 }, { .a = 2 }, { .b = 3 } }; // expected-warning 4{{extension}}
|
||||
static_assert(u[0].a == 0, "");
|
||||
static_assert(u[0].b, ""); // expected-error {{constant expression}}
|
||||
static_assert(u[0].b, ""); // expected-error {{constant expression}} expected-note {{read of member 'b' of union with active member 'a'}}
|
||||
static_assert(u[1].b == 1, "");
|
||||
static_assert((&u[1].b)[1] == 2, ""); // expected-error {{constant expression}} expected-note {{subexpression}}
|
||||
static_assert((&u[1].b)[1] == 2, ""); // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}
|
||||
static_assert(*(&(u[1].b) + 1 + 1) == 3, ""); // expected-error {{constant expression}} expected-note {{subexpression}}
|
||||
static_assert((&(u[1]) + 1 + 1)->b == 3, "");
|
||||
|
||||
|
@ -830,9 +837,10 @@ namespace ArrayBaseDerived {
|
|||
constexpr Derived *pd9 = pd6 + 3;
|
||||
constexpr Derived *pd10 = pd6 + 4;
|
||||
constexpr int pd9n = pd9->n; // ok
|
||||
constexpr int err_pd10n = pd10->n; // expected-error {{constant expression}}
|
||||
constexpr int err_pd10n = pd10->n; // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}
|
||||
constexpr int pd0n = pd10[-10].n;
|
||||
constexpr int err_pdminus1n = pd10[-11].n; // expected-error {{constant expression}}
|
||||
// FIXME: check pointer arithmetic here.
|
||||
constexpr int err_pdminus1n = pd10[-11].n; // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end pointer}}
|
||||
|
||||
constexpr Base *pb9 = pd9;
|
||||
constexpr const int *(Base::*pfb)() const =
|
||||
|
@ -917,3 +925,26 @@ namespace ExprWithCleanups {
|
|||
constexpr int get(bool FromA) { return FromA ? A().get() : 1; }
|
||||
constexpr int n = get(false);
|
||||
}
|
||||
|
||||
namespace Volatile {
|
||||
|
||||
volatile constexpr int n1 = 0; // expected-note {{here}}
|
||||
volatile const int n2 = 0; // expected-note {{here}}
|
||||
int n3 = 37; // expected-note {{declared here}}
|
||||
|
||||
constexpr int m1 = n1; // expected-error {{constant expression}} expected-note {{read of volatile object 'n1'}}
|
||||
constexpr int m2 = n2; // expected-error {{constant expression}} expected-note {{read of volatile object 'n2'}}
|
||||
|
||||
struct T { int n; };
|
||||
const T t = { 42 }; // expected-note {{declared here}}
|
||||
|
||||
constexpr int f(volatile int &&r) {
|
||||
return r; // expected-note {{read of volatile temporary is not allowed in a constant expression}}
|
||||
}
|
||||
struct S {
|
||||
int k : f(0); // expected-error {{constant expression}} expected-note {{temporary created here}} expected-note {{in call to 'f(0)'}}
|
||||
int l : n3; // expected-error {{constant expression}} expected-note {{read of non-const variable}}
|
||||
int m : t.n; // expected-error {{constant expression}} expected-note {{read of non-constexpr variable}}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -9,10 +9,8 @@ struct S {
|
|||
int n, m;
|
||||
};
|
||||
|
||||
constexpr int extract(const S &s) { return s.n; } // expected-note {{subexpression}}
|
||||
constexpr int extract(const S &s) { return s.n; } // expected-note {{read of uninitialized object is not allowed in a constant expression}}
|
||||
|
||||
// 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}} expected-note {{in call to 'S()'}}
|
||||
constexpr S s2(10);
|
||||
|
||||
|
@ -32,9 +30,9 @@ 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}}
|
||||
return *e; // expected-note {{read of non-constexpr variable 'u2'}}
|
||||
}
|
||||
U u2(0);
|
||||
U u2(0); // expected-note {{here}}
|
||||
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]})'}}
|
||||
|
@ -47,7 +45,7 @@ struct V {
|
|||
int arr[256];
|
||||
};
|
||||
constexpr V v;
|
||||
constexpr int get(const int *p) { return *p; } // expected-note {{subexpression}}
|
||||
constexpr int get(const int *p) { return *p; } // expected-note {{read of dereferenced one-past-the-end pointer}}
|
||||
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, ...}})'}}
|
||||
|
||||
|
@ -58,7 +56,7 @@ union Union {
|
|||
};
|
||||
constexpr Union myUnion = 76;
|
||||
|
||||
constexpr int badness(Union u) { return u.a + u.b; } // expected-note {{subexpression}}
|
||||
constexpr int badness(Union u) { return u.a + u.b; } // expected-note {{read of member 'a' of union with active member 'b'}}
|
||||
static_assert(badness(myUnion), ""); // expected-error {{constant expression}} \
|
||||
expected-note {{in call to 'badness({.b = 76})'}}
|
||||
|
||||
|
@ -66,9 +64,9 @@ struct MemPtrTest {
|
|||
int n;
|
||||
void f();
|
||||
};
|
||||
MemPtrTest mpt;
|
||||
MemPtrTest mpt; // expected-note {{here}}
|
||||
constexpr int MemPtr(int (MemPtrTest::*a), void (MemPtrTest::*b)(), int &c) {
|
||||
return c; // expected-note {{subexpression}}
|
||||
return c; // expected-note {{read of non-constexpr variable 'mpt'}}
|
||||
}
|
||||
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)'}}
|
||||
|
|
Loading…
Reference in New Issue