diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2019-06-13 19:00:16 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2019-06-13 19:00:16 +0000 |
commit | d54e3434d4878e8fd87a7dcdfb41aaf62f0e73f8 (patch) | |
tree | 2ed15025d74cd7fed1ebd0cb86c38fbea8e0eeac /test/CXX | |
parent | 552d41903d2003e01030581ecc51222be9707e8b (diff) | |
download | clang-d54e3434d4878e8fd87a7dcdfb41aaf62f0e73f8.tar.gz |
C++ DR712 and others: handle non-odr-use resulting from an lvalue-to-rvalue conversion applied to a member access or similar not-quite-trivial lvalue expression.
Summary:
When a variable is named in a context where we can't directly emit a
reference to it (because we don't know for sure that it's going to be
defined, or it's from an enclosing function and not captured, or the
reference might not "work" for some reason), we emit a copy of the
variable as a global and use that for the known-to-be-read-only access.
Reviewers: rjmccall
Subscribers: jdoerfert, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D63157
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@363295 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test/CXX')
-rw-r--r-- | test/CXX/basic/basic.def.odr/p2.cpp | 80 | ||||
-rw-r--r-- | test/CXX/drs/dr20xx.cpp | 197 | ||||
-rw-r--r-- | test/CXX/drs/dr21xx.cpp | 26 | ||||
-rw-r--r-- | test/CXX/drs/dr23xx.cpp | 42 | ||||
-rw-r--r-- | test/CXX/drs/dr6xx.cpp | 17 | ||||
-rw-r--r-- | test/CXX/drs/dr7xx.cpp | 36 |
6 files changed, 391 insertions, 7 deletions
diff --git a/test/CXX/basic/basic.def.odr/p2.cpp b/test/CXX/basic/basic.def.odr/p2.cpp new file mode 100644 index 0000000000..0ffd08c924 --- /dev/null +++ b/test/CXX/basic/basic.def.odr/p2.cpp @@ -0,0 +1,80 @@ +// RUN: %clang_cc1 -std=c++98 %s -Wno-unused -verify +// RUN: %clang_cc1 -std=c++11 %s -Wno-unused -verify +// RUN: %clang_cc1 -std=c++2a %s -Wno-unused -verify + +void use(int); + +void f() { + const int a = 1; // expected-note {{here}} + +#if __cplusplus >= 201103L + constexpr int arr[3] = {1, 2, 3}; // expected-note 2{{here}} + + struct S { int x; int f() const; }; + constexpr S s = {0}; // expected-note 3{{here}} + constexpr S *ps = nullptr; + S *const &psr = ps; // expected-note 2{{here}} +#endif + + struct Inner { + void test(int i) { + // id-expression + use(a); + +#if __cplusplus >= 201103L + // subscripting operation with an array operand + use(arr[i]); + use(i[arr]); + use((+arr)[i]); // expected-error {{reference to local variable}} + use(i[+arr]); // expected-error {{reference to local variable}} + + // class member access naming non-static data member + use(s.x); + use(s.f()); // expected-error {{reference to local variable}} + use((&s)->x); // expected-error {{reference to local variable}} + use(ps->x); // ok (lvalue-to-rvalue conversion applied to id-expression) + use(psr->x); // expected-error {{reference to local variable}} + + // class member access naming a static data member + // FIXME: How to test this? + + // pointer-to-member expression + use(s.*&S::x); + use((s.*&S::f)()); // expected-error {{reference to local variable}} + use(ps->*&S::x); // ok (lvalue-to-rvalue conversion applied to id-expression) + use(psr->*&S::x); // expected-error {{reference to local variable}} +#endif + + // parentheses + use((a)); +#if __cplusplus >= 201103L + use((s.x)); +#endif + + // glvalue conditional expression + use(i ? a : a); + use(i ? i : a); + + // comma expression + use((i, a)); + // FIXME: This is not an odr-use because it is a discarded-value + // expression applied to an expression whose potential result is 'a'. + use((a, a)); // expected-error {{reference to local variable}} + + // (and combinations thereof) + use(a ? (i, a) : a); +#if __cplusplus >= 201103L + use(a ? (i, a) : arr[a ? s.x : arr[a]]); +#endif + } + }; +} + +// FIXME: Test that this behaves properly. +namespace std_example { + struct S { static const int x = 0, y = 0; }; + const int &f(const int &r); + bool b; + int n = b ? (1, S::x) + : f(S::y); +} diff --git a/test/CXX/drs/dr20xx.cpp b/test/CXX/drs/dr20xx.cpp index 5819c319fd..90ccd7c055 100644 --- a/test/CXX/drs/dr20xx.cpp +++ b/test/CXX/drs/dr20xx.cpp @@ -4,12 +4,205 @@ // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// expected-no-diagnostics - #if __cplusplus < 201103L #define static_assert(...) _Static_assert(__VA_ARGS__) #endif +namespace dr2083 { // dr2083: partial +#if __cplusplus >= 201103L + void non_const_mem_ptr() { + struct A { + int x; + int y; + }; + constexpr A a = {1, 2}; + struct B { + int A::*p; + constexpr int g() const { + // OK, not an odr-use of 'a'. + return a.*p; + }; + }; + static_assert(B{&A::x}.g() == 1, ""); + static_assert(B{&A::y}.g() == 2, ""); + } +#endif + + const int a = 1; + int b; + // Note, references only get special odr-use / constant initializxer + // treatment in C++11 onwards. We continue to apply that even after DR2083. + void ref_to_non_const() { + int c; + const int &ra = a; // expected-note 0-1{{here}} + int &rb = b; // expected-note 0-1{{here}} + int &rc = c; // expected-note {{here}} + struct A { + int f() { + int a = ra; + int b = rb; +#if __cplusplus < 201103L + // expected-error@-3 {{in enclosing function}} + // expected-error@-3 {{in enclosing function}} +#endif + int c = rc; // expected-error {{in enclosing function}} + return a + b + c; + } + }; + } + +#if __cplusplus >= 201103L + struct NoMut1 { int a, b; }; + struct NoMut2 { NoMut1 m; }; + struct NoMut3 : NoMut1 { + constexpr NoMut3(int a, int b) : NoMut1{a, b} {} + }; + struct Mut1 { + int a; + mutable int b; + }; + struct Mut2 { Mut1 m; }; + struct Mut3 : Mut1 { + constexpr Mut3(int a, int b) : Mut1{a, b} {} + }; + void mutable_subobjects() { + constexpr NoMut1 nm1 = {1, 2}; + constexpr NoMut2 nm2 = {1, 2}; + constexpr NoMut3 nm3 = {1, 2}; + constexpr Mut1 m1 = {1, 2}; // expected-note {{declared here}} + constexpr Mut2 m2 = {1, 2}; // expected-note {{declared here}} + constexpr Mut3 m3 = {1, 2}; // expected-note {{declared here}} + struct A { + void f() { + static_assert(nm1.a == 1, ""); + static_assert(nm2.m.a == 1, ""); + static_assert(nm3.a == 1, ""); + // Can't even access a non-mutable member of a variable containing mutable fields. + static_assert(m1.a == 1, ""); // expected-error {{enclosing function}} + static_assert(m2.m.a == 1, ""); // expected-error {{enclosing function}} + static_assert(m3.a == 1, ""); // expected-error {{enclosing function}} + } + }; + } +#endif + + void ellipsis() { + void ellipsis(...); + struct A {}; + const int n = 0; +#if __cplusplus >= 201103L + constexpr +#endif + A a = {}; // expected-note {{here}} + struct B { + void f() { + ellipsis(n); + // Even though this is technically modelled as an lvalue-to-rvalue + // conversion, it calls a constructor and binds 'a' to a reference, so + // it results in an odr-use. + ellipsis(a); // expected-error {{enclosing function}} + } + }; + } + +#if __cplusplus >= 201103L + void volatile_lval() { + struct A { int n; }; + constexpr A a = {0}; // expected-note {{here}} + struct B { + void f() { + // An lvalue-to-rvalue conversion of a volatile lvalue always results + // in odr-use. + int A::*p = &A::n; + int x = a.*p; + volatile int A::*q = p; + int y = a.*q; // expected-error {{enclosing function}} + } + }; + } +#endif + + void discarded_lval() { + struct A { int x; mutable int y; volatile int z; }; + A a; // expected-note 1+{{here}} + int &r = a.x; // expected-note {{here}} + struct B { + void f() { + a.x; // expected-warning {{unused}} + a.*&A::x; // expected-warning {{unused}} + true ? a.x : a.y; // expected-warning {{unused}} + (void)a.x; + a.x, discarded_lval(); // expected-warning {{unused}} +#if 1 // FIXME: These errors are all incorrect; the above code is valid. + // expected-error@-6 {{enclosing function}} + // expected-error@-6 {{enclosing function}} + // expected-error@-6 2{{enclosing function}} + // expected-error@-6 {{enclosing function}} + // expected-error@-6 {{enclosing function}} +#endif + + // 'volatile' qualifier triggers an lvalue-to-rvalue conversion. + a.z; // expected-error {{enclosing function}} +#if __cplusplus < 201103L + // expected-warning@-2 {{assign into a variable}} +#endif + + // References always get "loaded" to determine what they reference, + // even if the result is discarded. + r; // expected-error {{enclosing function}} expected-warning {{unused}} + } + }; + } + + namespace dr_example_1 { + extern int globx; + int main() { + const int &x = globx; + struct A { +#if __cplusplus < 201103L + // expected-error@+2 {{enclosing function}} expected-note@-3 {{here}} +#endif + const int *foo() { return &x; } + } a; + return *a.foo(); + } + } + +#if __cplusplus >= 201103L + namespace dr_example_2 { + struct A { + int q; + constexpr A(int q) : q(q) {} + constexpr A(const A &a) : q(a.q * 2) {} // (note, not called) + }; + + int main(void) { + constexpr A a(42); + constexpr int aq = a.q; + struct Q { + int foo() { return a.q; } + } q; + return q.foo(); + } + + // Checking odr-use does not invent an lvalue-to-rvalue conversion (and + // hence copy construction) on the potential result variable. + struct B { + int b = 42; + constexpr B() {} + constexpr B(const B&) = delete; + }; + void f() { + constexpr B b; + struct Q { + constexpr int foo() const { return b.b; } + }; + static_assert(Q().foo() == 42, ""); + } + } +#endif +} + namespace dr2094 { // dr2094: 5 struct A { int n; }; struct B { volatile int n; }; diff --git a/test/CXX/drs/dr21xx.cpp b/test/CXX/drs/dr21xx.cpp index 2522ff7dbd..83c59d0120 100644 --- a/test/CXX/drs/dr21xx.cpp +++ b/test/CXX/drs/dr21xx.cpp @@ -8,6 +8,19 @@ #define static_assert(...) __extension__ _Static_assert(__VA_ARGS__) #endif +namespace dr2103 { // dr2103: yes + void f() { + int a; + int &r = a; // expected-note {{here}} + struct Inner { + void f() { + int &s = r; // expected-error {{enclosing function}} + (void)s; + } + }; + } +} + namespace dr2120 { // dr2120: 7 struct A {}; struct B : A {}; @@ -19,6 +32,19 @@ namespace dr2120 { // dr2120: 7 static_assert(!__is_standard_layout(E), ""); } +namespace dr2170 { // dr2170: 9 +#if __cplusplus >= 201103L + void f() { + constexpr int arr[3] = {1, 2, 3}; // expected-note {{here}} + struct S { + int get(int n) { return arr[n]; } + const int &get_ref(int n) { return arr[n]; } // expected-error {{enclosing function}} + // FIXME: expected-warning@-1 {{reference to stack}} + }; + } +#endif +} + namespace dr2180 { // dr2180: yes class A { A &operator=(const A &); // expected-note 0-2{{here}} diff --git a/test/CXX/drs/dr23xx.cpp b/test/CXX/drs/dr23xx.cpp index 87db0d4c9b..8e7a9a880b 100644 --- a/test/CXX/drs/dr23xx.cpp +++ b/test/CXX/drs/dr23xx.cpp @@ -1,13 +1,45 @@ -// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -// RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s #if __cplusplus <= 201103L // expected-no-diagnostics #endif +namespace dr2353 { // dr2353: 9 + struct X { + static const int n = 0; + }; + + // CHECK: FunctionDecl {{.*}} use + int use(X x) { + // CHECK: MemberExpr {{.*}} .n + // CHECK-NOT: non_odr_use + // CHECK: DeclRefExpr {{.*}} 'x' + // CHECK-NOT: non_odr_use + return *&x.n; + } +#pragma clang __debug dump use + + // CHECK: FunctionDecl {{.*}} not_use + int not_use(X x) { + // CHECK: MemberExpr {{.*}} .n {{.*}} non_odr_use_constant + // CHECK: DeclRefExpr {{.*}} 'x' + return x.n; + } +#pragma clang __debug dump not_use + + // CHECK: FunctionDecl {{.*}} not_use_2 + int not_use_2(X *x) { + // CHECK: MemberExpr {{.*}} ->n {{.*}} non_odr_use_constant + // CHECK: DeclRefExpr {{.*}} 'x' + return x->n; + } +#pragma clang __debug dump not_use_2 +} + namespace dr2387 { // dr2387: 9 #if __cplusplus >= 201402L template<int> int a = 0; diff --git a/test/CXX/drs/dr6xx.cpp b/test/CXX/drs/dr6xx.cpp index 31642dfbb0..530c88f86f 100644 --- a/test/CXX/drs/dr6xx.cpp +++ b/test/CXX/drs/dr6xx.cpp @@ -1132,3 +1132,20 @@ namespace dr692 { // dr692: no template void f(int*); // expected-error {{ambiguous}} } } + +namespace dr696 { // dr696: yes + void f(const int*); + void g() { + const int N = 10; // expected-note 1+{{here}} + struct A { + void h() { + int arr[N]; (void)arr; + f(&N); // expected-error {{declared in enclosing}} + } + }; +#if __cplusplus >= 201103L + (void) [] { int arr[N]; (void)arr; }; + (void) [] { f(&N); }; // expected-error {{cannot be implicitly captured}} expected-note {{here}} +#endif + } +} diff --git a/test/CXX/drs/dr7xx.cpp b/test/CXX/drs/dr7xx.cpp index 2d9d396018..30cc4b9abe 100644 --- a/test/CXX/drs/dr7xx.cpp +++ b/test/CXX/drs/dr7xx.cpp @@ -17,6 +17,42 @@ namespace dr705 { // dr705: yes } } +namespace dr712 { // dr712: partial + void use(int); + void f() { + const int a = 0; // expected-note 5{{here}} + struct X { + void g(bool cond) { + use(a); + use((a)); + use(cond ? a : a); + use((cond, a)); // expected-warning 2{{unused}} FIXME: should only warn once + + (void)a; // FIXME: expected-error {{declared in enclosing}} + (void)(a); // FIXME: expected-error {{declared in enclosing}} + (void)(cond ? a : a); // FIXME: expected-error 2{{declared in enclosing}} + (void)(cond, a); // FIXME: expected-error {{declared in enclosing}} expected-warning {{unused}} + } + }; + } + +#if __cplusplus >= 201103L + void g() { + struct A { int n; }; + constexpr A a = {0}; // expected-note 2{{here}} + struct X { + void g(bool cond) { + use(a.n); + use(a.*&A::n); + + (void)a.n; // FIXME: expected-error {{declared in enclosing}} + (void)(a.*&A::n); // FIXME: expected-error {{declared in enclosing}} + } + }; + } +#endif +} + namespace dr727 { // dr727: partial struct A { template<typename T> struct C; // expected-note 6{{here}} |