aboutsummaryrefslogtreecommitdiff
path: root/test/SemaCXX/constant-expression-cxx11.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/SemaCXX/constant-expression-cxx11.cpp')
-rw-r--r--test/SemaCXX/constant-expression-cxx11.cpp388
1 files changed, 374 insertions, 14 deletions
diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp
index 09a9cb5dd8db..6724be7c8204 100644
--- a/test/SemaCXX/constant-expression-cxx11.cpp
+++ b/test/SemaCXX/constant-expression-cxx11.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i686-linux -Wno-string-plus-int -fsyntax-only -fcxx-exceptions -verify -std=c++11 -pedantic %s -Wno-comment
+// RUN: %clang_cc1 -triple i686-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -fsyntax-only -fcxx-exceptions -verify -std=c++11 -pedantic %s -Wno-comment
namespace StaticAssertFoldTest {
@@ -79,6 +79,11 @@ constexpr int **n6 = const_cast<int**>(&n3);
constexpr int n7 = **n5;
constexpr int n8 = **n6;
+// const_cast from prvalue to xvalue.
+struct A { int n; };
+constexpr int n9 = (const_cast<A&&>(A{123})).n;
+static_assert(n9 == 123, "");
+
}
namespace TemplateArgumentConversion {
@@ -339,6 +344,46 @@ static_assert(!same(4, 4), "");
static_assert(same(n, n), "");
static_assert(sameTemporary(9), "");
+struct A { int &&r; };
+struct B { A &&a1; A &&a2; };
+
+constexpr B b1 { { 1 }, { 2 } }; // expected-note {{temporary created here}}
+static_assert(&b1.a1 != &b1.a2, "");
+static_assert(&b1.a1.r != &b1.a2.r, ""); // expected-error {{constant expression}} expected-note {{outside the expression that created the temporary}}
+
+constexpr B &&b2 { { 3 }, { 4 } }; // expected-note {{temporary created here}}
+static_assert(&b1 != &b2, "");
+static_assert(&b1.a1 != &b2.a1, ""); // expected-error {{constant expression}} expected-note {{outside the expression that created the temporary}}
+
+constexpr thread_local B b3 { { 1 }, { 2 } }; // expected-error {{constant expression}} expected-note {{reference to temporary}} expected-note {{here}}
+void foo() {
+ constexpr static B b1 { { 1 }, { 2 } }; // ok
+ constexpr thread_local B b2 { { 1 }, { 2 } }; // expected-error {{constant expression}} expected-note {{reference to temporary}} expected-note {{here}}
+ constexpr B b3 { { 1 }, { 2 } }; // expected-error {{constant expression}} expected-note {{reference to temporary}} expected-note {{here}}
+}
+
+constexpr B &&b4 = ((1, 2), 3, 4, B { {10}, {{20}} }); // expected-warning 4{{unused}}
+static_assert(&b4 != &b2, "");
+
+// Proposed DR: copy-elision doesn't trigger lifetime extension.
+constexpr B b5 = B{ {0}, {0} }; // expected-error {{constant expression}} expected-note {{reference to temporary}} expected-note {{here}}
+
+namespace NestedNonStatic {
+ // Proposed DR: for a reference constant expression to refer to a static
+ // storage duration temporary, that temporary must itself be initialized
+ // by a constant expression (a core constant expression is not enough).
+ struct A { int &&r; };
+ struct B { A &&a; };
+ constexpr B a = { A{0} }; // ok
+ constexpr B b = { A(A{0}) }; // expected-error {{constant expression}} expected-note {{reference to temporary}} expected-note {{here}}
+}
+
+namespace FakeInitList {
+ struct init_list_3_ints { const int (&x)[3]; };
+ struct init_list_2_init_list_3_ints { const init_list_3_ints (&x)[2]; };
+ constexpr init_list_2_init_list_3_ints ils = { { { { 1, 2, 3 } }, { { 4, 5, 6 } } } };
+}
+
}
constexpr int strcmp_ce(const char *p, const char *q) {
@@ -572,7 +617,7 @@ struct E {
constexpr E() : p(&p) {}
void *p;
};
-constexpr const E &e1 = E(); // expected-error {{constant expression}} expected-note {{reference to temporary is not a constant expression}} expected-note {{temporary created here}}
+constexpr const E &e1 = E();
// This is a constant expression if we elide the copy constructor call, and
// is not a constant expression if we don't! But we do, so it is.
constexpr E e2 = E();
@@ -729,15 +774,22 @@ static_assert(&ok2 == pok2, "");
static_assert((Base2*)(Derived*)(Base*)pb1 == pok2, "");
static_assert((Derived*)(Base*)pb1 == (Derived*)pok2, "");
-constexpr Base *nullB = 42 - 6 * 7; // expected-warning {{expression which evaluates to zero treated as a null pointer constant of type 'Class::Base *const'}}
+// Core issue 903: we do not perform constant evaluation when checking for a
+// null pointer in C++11. Just check for an integer literal with value 0.
+constexpr Base *nullB = 42 - 6 * 7; // expected-error {{cannot initialize a variable of type 'Class::Base *const' with an rvalue of type 'int'}}
+constexpr Base *nullB1 = 0;
static_assert((Bottom*)nullB == 0, "");
static_assert((Derived*)nullB == 0, "");
static_assert((void*)(Bottom*)nullB == (void*)(Derived*)nullB, "");
-Base * nullB2 = '\0'; // expected-warning {{expression which evaluates to zero treated as a null pointer constant of type 'Class::Base *'}}
-Base * nullB3 = (0);
-// We suppress the warning in unevaluated contexts to workaround some gtest
-// behavior. Once this becomes an error this isn't a problem anymore.
-static_assert(nullB == (1 - 1), "");
+Base *nullB2 = '\0'; // expected-error {{cannot initialize a variable of type 'Class::Base *' with an rvalue of type 'char'}}
+Base *nullB3 = (0);
+Base *nullB4 = false; // expected-error {{cannot initialize a variable of type 'Class::Base *' with an rvalue of type 'bool'}}
+Base *nullB5 = ((0ULL));
+Base *nullB6 = 0.; // expected-error {{cannot initialize a variable of type 'Class::Base *' with an rvalue of type 'double'}}
+enum Null { kNull };
+Base *nullB7 = kNull; // expected-error {{cannot initialize a variable of type 'Class::Base *' with an rvalue of type 'Class::Null'}}
+static_assert(nullB1 == (1 - 1), ""); // expected-error {{comparison between pointer and integer}}
+
namespace ConversionOperators {
@@ -778,21 +830,26 @@ namespace Temporaries {
struct S {
constexpr S() {}
constexpr int f() const;
+ constexpr int g() const;
};
struct T : S {
constexpr T(int n) : S(), n(n) {}
int n;
};
constexpr int S::f() const {
- // 'this' must be the postfix-expression in a class member access expression,
- // so we can't just use
- // return static_cast<T*>(this)->n;
- return this->*(int(S::*))&T::n;
+ return static_cast<const T*>(this)->n; // expected-note {{cannot cast}}
+}
+constexpr int S::g() const {
+ // FIXME: Better diagnostic for this.
+ return this->*(int(S::*))&T::n; // expected-note {{subexpression}}
}
// The T temporary is implicitly cast to an S subobject, but we can recover the
// T full-object via a base-to-derived cast, or a derived-to-base-casted member
// pointer.
+static_assert(S().f(), ""); // expected-error {{constant expression}} expected-note {{in call to '&Temporaries::S()->f()'}}
+static_assert(S().g(), ""); // expected-error {{constant expression}} expected-note {{in call to '&Temporaries::S()->g()'}}
static_assert(T(3).f() == 3, "");
+static_assert(T(4).g() == 4, "");
constexpr int f(const S &s) {
return static_cast<const T&>(s).n;
@@ -1138,6 +1195,31 @@ namespace ComplexConstexpr {
static_assert(&__imag test6 == &__real test6 + 1, "");
}
+// _Atomic(T) is exactly like T for the purposes of constant expression
+// evaluation..
+namespace Atomic {
+ constexpr _Atomic int n = 3;
+
+ struct S { _Atomic(double) d; };
+ constexpr S s = { 0.5 };
+ constexpr double d1 = s.d;
+ constexpr double d2 = n;
+ constexpr _Atomic double d3 = n;
+
+ constexpr _Atomic(int) n2 = d3;
+ static_assert(d1 == 0.5, "");
+ static_assert(d3 == 3.0, "");
+
+ namespace PR16056 {
+ struct TestVar {
+ _Atomic(int) value;
+ constexpr TestVar(int value) : value(value) {}
+ };
+ constexpr TestVar testVar{-1};
+ static_assert(testVar.value == -1, "");
+ }
+}
+
namespace InstantiateCaseStmt {
template<int x> constexpr int f() { return x; }
template<int x> int g(int c) { switch(c) { case f<x>(): return 1; } return 0; }
@@ -1252,8 +1334,23 @@ struct Wrap {
constexpr const Wrap &g(const Wrap &w) { return w; }
constexpr int k2 = g({0}).value; // ok
-constexpr const int &i = 0; // expected-error {{constant expression}} expected-note {{temporary}} expected-note 2{{here}}
-constexpr const int j = i; // expected-error {{constant expression}} expected-note {{initializer of 'i' is not a constant expression}}
+// The temporary here has static storage duration, so we can bind a constexpr
+// reference to it.
+constexpr const int &i = 1;
+constexpr const int j = i;
+static_assert(j == 1, "");
+
+// The temporary here is not const, so it can't be read outside the expression
+// in which it was created (per the C++14 rules, which we use to avoid a C++11
+// defect).
+constexpr int &&k = 1; // expected-note {{temporary created here}}
+constexpr const int l = k; // expected-error {{constant expression}} expected-note {{read of temporary}}
+
+void f() {
+ // The temporary here has automatic storage duration, so we can't bind a
+ // constexpr reference to it.
+ constexpr const int &i = 1; // expected-error {{constant expression}} expected-note 2{{temporary}}
+}
}
@@ -1503,3 +1600,266 @@ namespace PR15884 {
// expected-note@-3 {{pointer to temporary is not a constant expression}}
// expected-note@-4 {{temporary created here}}
}
+
+namespace AfterError {
+ // FIXME: Suppress the 'no return statements' diagnostic if the body is invalid.
+ constexpr int error() { // expected-error {{no return statement}}
+ return foobar; // expected-error {{undeclared identifier}}
+ }
+ constexpr int k = error(); // expected-error {{must be initialized by a constant expression}}
+}
+
+namespace std {
+ typedef decltype(sizeof(int)) size_t;
+
+ template <class _E>
+ class initializer_list
+ {
+ const _E* __begin_;
+ size_t __size_;
+
+ constexpr initializer_list(const _E* __b, size_t __s)
+ : __begin_(__b),
+ __size_(__s)
+ {}
+
+ public:
+ typedef _E value_type;
+ typedef const _E& reference;
+ typedef const _E& const_reference;
+ typedef size_t size_type;
+
+ typedef const _E* iterator;
+ typedef const _E* const_iterator;
+
+ constexpr initializer_list() : __begin_(nullptr), __size_(0) {}
+
+ constexpr size_t size() const {return __size_;}
+ constexpr const _E* begin() const {return __begin_;}
+ constexpr const _E* end() const {return __begin_ + __size_;}
+ };
+}
+
+namespace InitializerList {
+ constexpr int sum(const int *b, const int *e) {
+ return b != e ? *b + sum(b+1, e) : 0;
+ }
+ constexpr int sum(std::initializer_list<int> ints) {
+ return sum(ints.begin(), ints.end());
+ }
+ static_assert(sum({1, 2, 3, 4, 5}) == 15, "");
+}
+
+namespace StmtExpr {
+ struct A { int k; };
+ void f() {
+ static_assert(({ const int x = 5; x * 3; }) == 15, ""); // expected-warning {{extension}}
+ constexpr auto a = ({ A(); }); // expected-warning {{extension}}
+ }
+ constexpr int g(int k) {
+ return ({ // expected-warning {{extension}}
+ const int x = k;
+ x * x;
+ });
+ }
+ static_assert(g(123) == 15129, "");
+ constexpr int h() { // expected-error {{never produces a constant}}
+ return ({ // expected-warning {{extension}}
+ return 0; // expected-note {{not supported}}
+ 1;
+ });
+ }
+}
+
+namespace VirtualFromBase {
+ struct S1 {
+ virtual int f() const;
+ };
+ struct S2 {
+ virtual int f();
+ };
+ template <typename T> struct X : T {
+ constexpr X() {}
+ double d = 0.0;
+ constexpr int f() { return sizeof(T); } // expected-warning {{will not be implicitly 'const' in C++1y}}
+ };
+
+ // Virtual f(), not OK.
+ constexpr X<X<S1>> xxs1;
+ constexpr X<S1> *p = const_cast<X<X<S1>>*>(&xxs1);
+ static_assert(p->f() == sizeof(X<S1>), ""); // expected-error {{constant expression}} expected-note {{virtual function call}}
+
+ // Non-virtual f(), OK.
+ constexpr X<X<S2>> xxs2;
+ constexpr X<S2> *q = const_cast<X<X<S2>>*>(&xxs2);
+ static_assert(q->f() == sizeof(S2), "");
+}
+
+namespace ConstexprConstructorRecovery {
+ class X {
+ public:
+ enum E : short {
+ headers = 0x1,
+ middlefile = 0x2,
+ choices = 0x4
+ };
+ constexpr X() noexcept {};
+ protected:
+ E val{0}; // expected-error {{cannot initialize a member subobject of type 'ConstexprConstructorRecovery::X::E' with an rvalue of type 'int'}}
+ };
+ constexpr X x{};
+}
+
+namespace Lifetime {
+ void f() {
+ constexpr int &n = n; // expected-error {{constant expression}} expected-note {{use of reference outside its lifetime}} expected-warning {{not yet bound to a value}}
+ constexpr int m = m; // expected-error {{constant expression}} expected-note {{read of object outside its lifetime}}
+ }
+
+ constexpr int &get(int &&n) { return n; }
+ struct S {
+ int &&r; // expected-note 2{{declared here}}
+ int &s;
+ int t;
+ constexpr S() : r(0), s(get(0)), t(r) {} // expected-warning {{temporary}}
+ constexpr S(int) : r(0), s(get(0)), t(s) {} // expected-warning {{temporary}} expected-note {{read of object outside its lifetime}}
+ };
+ constexpr int k1 = S().t; // ok, int is lifetime-extended to end of constructor
+ constexpr int k2 = S(0).t; // expected-error {{constant expression}} expected-note {{in call}}
+}
+
+namespace Bitfields {
+ struct A {
+ bool b : 1;
+ unsigned u : 5;
+ int n : 5;
+ bool b2 : 3;
+ unsigned u2 : 74; // expected-warning {{exceeds the size of its type}}
+ int n2 : 81; // expected-warning {{exceeds the size of its type}}
+ };
+
+ constexpr A a = { false, 33, 31, false, 0xffffffff, 0x7fffffff }; // expected-warning 2{{truncation}}
+ static_assert(a.b == 0 && a.u == 1 && a.n == -1 && a.b2 == 0 &&
+ a.u2 + 1 == 0 && a.n2 == 0x7fffffff,
+ "bad truncation of bitfield values");
+
+ struct B {
+ int n : 3;
+ constexpr B(int k) : n(k) {}
+ };
+ static_assert(B(3).n == 3, "");
+ static_assert(B(4).n == -4, "");
+ static_assert(B(7).n == -1, "");
+ static_assert(B(8).n == 0, "");
+ static_assert(B(-1).n == -1, "");
+ static_assert(B(-8889).n == -1, "");
+
+ namespace PR16755 {
+ struct X {
+ int x : 1;
+ constexpr static int f(int x) {
+ return X{x}.x;
+ }
+ };
+ static_assert(X::f(3) == -1, "3 should truncate to -1");
+ }
+}
+
+namespace ZeroSizeTypes {
+ constexpr int (*p1)[0] = 0, (*p2)[0] = 0;
+ constexpr int k = p2 - p1;
+ // expected-error@-1 {{constexpr variable 'k' must be initialized by a constant expression}}
+ // expected-note@-2 {{subtraction of pointers to type 'int [0]' of zero size}}
+
+ int arr[5][0];
+ constexpr int f() { // expected-error {{never produces a constant expression}}
+ return &arr[3] - &arr[0]; // expected-note {{subtraction of pointers to type 'int [0]' of zero size}}
+ }
+}
+
+namespace BadDefaultInit {
+ template<int N> struct X { static const int n = N; };
+
+ struct A { // expected-note {{subexpression}}
+ int k = X<A().k>::n; // expected-error {{defaulted default constructor of 'A' cannot be used}} expected-error {{not a constant expression}} expected-note {{in call to 'A()'}}
+ };
+
+ // FIXME: The "constexpr constructor must initialize all members" diagnostic
+ // here is bogus (we discard the k(k) initializer because the parameter 'k'
+ // has been marked invalid).
+ struct B { // expected-note 2{{candidate}}
+ constexpr B( // expected-error {{must initialize all members}} expected-note {{candidate}}
+ int k = X<B().k>::n) : // expected-error {{no matching constructor}}
+ k(k) {}
+ int k; // expected-note {{not initialized}}
+ };
+}
+
+namespace NeverConstantTwoWays {
+ // If we see something non-constant but foldable followed by something
+ // non-constant and not foldable, we want the first diagnostic, not the
+ // second.
+ constexpr int f(int n) { // expected-error {{never produces a constant expression}}
+ return (int *)(long)&n == &n ? // expected-note {{reinterpret_cast}}
+ 1 / 0 : // expected-warning {{division by zero}}
+ 0;
+ }
+
+ // FIXME: We should diagnose the cast to long here, not the division by zero.
+ constexpr int n = // expected-error {{must be initialized by a constant expression}}
+ (int *)(long)&n == &n ?
+ 1 / 0 : // expected-warning {{division by zero}} expected-note {{division by zero}}
+ 0;
+}
+
+namespace PR17800 {
+ struct A {
+ constexpr int operator()() const { return 0; }
+ };
+ template <typename ...T> constexpr int sink(T ...) {
+ return 0;
+ }
+ template <int ...N> constexpr int run() {
+ return sink(A()() + N ...);
+ }
+ constexpr int k = run<1, 2, 3>();
+}
+
+namespace BuiltinStrlen {
+ constexpr const char *a = "foo\0quux";
+ constexpr char b[] = "foo\0quux";
+ constexpr int f() { return 'u'; }
+ constexpr char c[] = { 'f', 'o', 'o', 0, 'q', f(), 'u', 'x', 0 };
+
+ static_assert(__builtin_strlen("foo") == 3, "");
+ static_assert(__builtin_strlen("foo\0quux") == 3, "");
+ static_assert(__builtin_strlen("foo\0quux" + 4) == 4, "");
+
+ constexpr bool check(const char *p) {
+ return __builtin_strlen(p) == 3 &&
+ __builtin_strlen(p + 1) == 2 &&
+ __builtin_strlen(p + 2) == 1 &&
+ __builtin_strlen(p + 3) == 0 &&
+ __builtin_strlen(p + 4) == 4 &&
+ __builtin_strlen(p + 5) == 3 &&
+ __builtin_strlen(p + 6) == 2 &&
+ __builtin_strlen(p + 7) == 1 &&
+ __builtin_strlen(p + 8) == 0;
+ }
+
+ static_assert(check(a), "");
+ static_assert(check(b), "");
+ static_assert(check(c), "");
+
+ constexpr int over1 = __builtin_strlen(a + 9); // expected-error {{constant expression}} expected-note {{one-past-the-end}}
+ constexpr int over2 = __builtin_strlen(b + 9); // expected-error {{constant expression}} expected-note {{one-past-the-end}}
+ constexpr int over3 = __builtin_strlen(c + 9); // expected-error {{constant expression}} expected-note {{one-past-the-end}}
+
+ constexpr int under1 = __builtin_strlen(a - 1); // expected-error {{constant expression}} expected-note {{cannot refer to element -1}}
+ constexpr int under2 = __builtin_strlen(b - 1); // expected-error {{constant expression}} expected-note {{cannot refer to element -1}}
+ constexpr int under3 = __builtin_strlen(c - 1); // expected-error {{constant expression}} expected-note {{cannot refer to element -1}}
+
+ // FIXME: The diagnostic here could be better.
+ constexpr char d[] = { 'f', 'o', 'o' }; // no nul terminator.
+ constexpr int bad = __builtin_strlen(d); // expected-error {{constant expression}} expected-note {{one-past-the-end}}
+}