diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2019-05-11 02:00:06 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2019-05-11 02:00:06 +0000 |
commit | d05df0ef4362855405ae1df76572909fb0ff55b2 (patch) | |
tree | c7df3ec9c993ccbddb887b000470e5aa2ea1f3c0 /clang/test | |
parent | 61fb6700a569343ec1908043b9faac69c34168a5 (diff) | |
download | bcm5719-llvm-d05df0ef4362855405ae1df76572909fb0ff55b2.tar.gz bcm5719-llvm-d05df0ef4362855405ae1df76572909fb0ff55b2.zip |
Reject attempts to call non-static member functions on objects outside
their lifetime in constant expressions.
This is undefined behavior per [class.cdtor]p2.
We continue to allow this for objects whose values are not visible
within the constant evaluation, because there's no way we can tell
whether the access is defined or not, existing code relies on the
ability to make such calls, and every other compiler allows such
calls.
llvm-svn: 360499
Diffstat (limited to 'clang/test')
-rw-r--r-- | clang/test/CXX/expr/expr.const/p2-0x.cpp | 4 | ||||
-rw-r--r-- | clang/test/SemaCXX/constant-expression-cxx11.cpp | 76 |
2 files changed, 68 insertions, 12 deletions
diff --git a/clang/test/CXX/expr/expr.const/p2-0x.cpp b/clang/test/CXX/expr/expr.const/p2-0x.cpp index 4daed23bf9c..2496845182f 100644 --- a/clang/test/CXX/expr/expr.const/p2-0x.cpp +++ b/clang/test/CXX/expr/expr.const/p2-0x.cpp @@ -210,8 +210,8 @@ namespace UndefinedBehavior { constexpr int f() const { return 0; } } constexpr c = C(); constexpr int k1 = c.f(); // ok - constexpr int k2 = ((C*)nullptr)->f(); // expected-error {{constant expression}} expected-note {{cannot call member function on null pointer}} - constexpr int k3 = (&c)[1].f(); // expected-error {{constant expression}} expected-note {{cannot call member function on pointer past the end of object}} + constexpr int k2 = ((C*)nullptr)->f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced null pointer}} + constexpr int k3 = (&c)[1].f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced one-past-the-end pointer}} C c2; constexpr int k4 = c2.f(); // ok! diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index 6af43854b52..c136b4d2693 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -192,6 +192,25 @@ namespace StaticMemberFunction { constexpr int (*sf1)(int) = &S::f; constexpr int (*sf2)(int) = &s.f; constexpr const int *sk = &s.k; + + // Note, out_of_lifetime returns an invalid pointer value, but we don't do + // anything with it (other than copy it around), so there's no UB there. + constexpr S *out_of_lifetime(S s) { return &s; } // expected-warning {{address of stack}} + static_assert(out_of_lifetime({})->k == 42, ""); + static_assert(out_of_lifetime({})->f(3) == 128, ""); + + // Similarly, using an inactive union member breaks no rules. + union U { + int n; + S s; + }; + constexpr U u = {0}; + static_assert(u.s.k == 42, ""); + static_assert(u.s.f(1) == 44, ""); + + // And likewise for a past-the-end pointer. + static_assert((&s)[1].k == 42, ""); + static_assert((&s)[1].f(1) == 44, ""); } namespace ParameterScopes { @@ -1729,19 +1748,10 @@ namespace PR14203 { constexpr duration() {} constexpr operator int() const { return 0; } }; + // These are valid per P0859R0 (moved as DR). template<typename T> void f() { - // If we want to evaluate this at the point of the template definition, we - // need to trigger the implicit definition of the move constructor at that - // point. - // FIXME: C++ does not permit us to implicitly define it at the appropriate - // times, since it is only allowed to be implicitly defined when it is - // odr-used. constexpr duration d = duration(); } - // FIXME: It's unclear whether this is valid. On the one hand, we're not - // allowed to generate a move constructor. On the other hand, if we did, - // this would be a constant expression. For now, we generate a move - // constructor here. int n = sizeof(short{duration(duration())}); } @@ -1902,6 +1912,52 @@ namespace Lifetime { }; constexpr int k1 = S().t; // expected-error {{constant expression}} expected-note {{in call}} constexpr int k2 = S(0).t; // expected-error {{constant expression}} expected-note {{in call}} + + struct Q { + int n = 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 int k3 = out_of_lifetime({})->n; // expected-error {{constant expression}} expected-note {{read of variable whose lifetime has ended}} + constexpr int k4 = out_of_lifetime({})->f(); // expected-error {{constant expression}} expected-note {{member call on variable whose lifetime has ended}} + + constexpr int null = ((Q*)nullptr)->f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced null pointer}} + + Q q; + Q qa[3]; + constexpr int pte0 = (&q)[0].f(); // ok + constexpr int pte1 = (&q)[1].f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced one-past-the-end pointer}} + constexpr int pte2 = qa[2].f(); // ok + constexpr int pte3 = qa[3].f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced one-past-the-end pointer}} + + constexpr Q cq; + constexpr Q cqa[3]; + constexpr int cpte0 = (&cq)[0].f(); // ok + constexpr int cpte1 = (&cq)[1].f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced one-past-the-end pointer}} + constexpr int cpte2 = cqa[2].f(); // ok + constexpr int cpte3 = cqa[3].f(); // expected-error {{constant expression}} expected-note {{member call on dereferenced one-past-the-end pointer}} + + // FIXME: There's no way if we can tell if the first call here is valid; it + // depends on the active union member. Should we reject for that reason? + union U { + int n; + Q q; + }; + U u1 = {0}; + constexpr U u2 = {0}; + constexpr int union_member1 = u1.q.f(); + constexpr int union_member2 = u2.q.f(); // expected-error {{constant expression}} expected-note {{member call on member 'q' of union with active member 'n'}} + + struct R { // expected-note {{field init}} + struct Inner { constexpr int f() const { return 0; } }; + int a = b.f(); // expected-warning {{uninitialized}} expected-note {{member call on object outside its lifetime}} + Inner b; + }; + // FIXME: This should be rejected under DR2026. + constexpr R r; // expected-note {{default constructor}} + void rf() { + constexpr R r; // expected-error {{constant expression}} expected-note {{in call}} + } } namespace Bitfields { |