From 7098cbd601ad915aed22d4b5850da99359f25bf3 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 21 Dec 2011 05:04:46 +0000 Subject: [PATCH] constexpr: diagnostic improvements for invalid lvalue-to-rvalue conversions in constant expressions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@147035 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticASTKinds.td | 19 +++ lib/AST/ExprConstant.cpp | 138 ++++++++++++++---- .../CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp | 6 +- test/CXX/expr/expr.const/p2-0x.cpp | 63 +++++--- test/SemaCXX/constant-expression-cxx11.cpp | 65 ++++++--- test/SemaCXX/constexpr-printing.cpp | 16 +- 6 files changed, 233 insertions(+), 74 deletions(-) diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index 8f0b4938ad..d4e5693ff9 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -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)">; diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 3c2ffbd0f7..698761f117 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -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()->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(); 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(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(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(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(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(), diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp index 4145e9b224..34114cb077 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp @@ -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'}} diff --git a/test/CXX/expr/expr.const/p2-0x.cpp b/test/CXX/expr/expr.const/p2-0x.cpp index aa83fc7fa9..157645fc52 100644 --- a/test/CXX/expr/expr.const/p2-0x.cpp +++ b/test/CXX/expr/expr.const/p2-0x.cpp @@ -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(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(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(&a); - int c = 10; + int c = 10; // expected-note 2 {{here}} int &d = c; constexpr int e = 42; int &f = const_cast(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; diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index b470c80619..af0d5f3e1c 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -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}} +}; + +} diff --git a/test/SemaCXX/constexpr-printing.cpp b/test/SemaCXX/constexpr-printing.cpp index 726d1770fa..e6cf209819 100644 --- a/test/SemaCXX/constexpr-printing.cpp +++ b/test/SemaCXX/constexpr-printing.cpp @@ -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)'}} -- 2.40.0