diff options
| author | Richard Smith <richard-llvm@metafoo.co.uk> | 2016-12-15 02:28:18 +0000 |
|---|---|---|
| committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2016-12-15 02:28:18 +0000 |
| commit | 81f5ade22790685efa5542bd8b7e7fb2c6b6c56c (patch) | |
| tree | 23c92251419f2a497e8abcd378c2fa5b8b7849b6 /clang/test | |
| parent | 1a328f508f99473907bbca0e5a40e9f57209a5da (diff) | |
| download | bcm5719-llvm-81f5ade22790685efa5542bd8b7e7fb2c6b6c56c.tar.gz bcm5719-llvm-81f5ade22790685efa5542bd8b7e7fb2c6b6c56c.zip | |
Move checks for creation of objects of abstract class type from the various
constructs that can do so into the initialization code. This fixes a number
of different cases in which we used to fail to check for abstract types.
Thanks to Tim Shen for inspiring the weird code that uncovered this!
llvm-svn: 289753
Diffstat (limited to 'clang/test')
| -rw-r--r-- | clang/test/CXX/class.derived/class.abstract/p2.cpp | 23 | ||||
| -rw-r--r-- | clang/test/CXX/class.derived/class.abstract/p3.cpp | 97 |
2 files changed, 120 insertions, 0 deletions
diff --git a/clang/test/CXX/class.derived/class.abstract/p2.cpp b/clang/test/CXX/class.derived/class.abstract/p2.cpp new file mode 100644 index 00000000000..713a5269f14 --- /dev/null +++ b/clang/test/CXX/class.derived/class.abstract/p2.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +// no objects of an abstract class can be created except as subobjects of a +// class derived from it + +struct A { + A() {} + A(int) : A() {} // ok + + virtual void f() = 0; // expected-note 1+{{unimplemented}} +}; + +void f(A &&a); + +void g() { + f({}); // expected-error {{abstract class}} + f({0}); // expected-error {{abstract class}} + f(0); // expected-error {{abstract class}} +} + +struct B : A { + B() : A() {} // ok +}; diff --git a/clang/test/CXX/class.derived/class.abstract/p3.cpp b/clang/test/CXX/class.derived/class.abstract/p3.cpp new file mode 100644 index 00000000000..ad5b8747883 --- /dev/null +++ b/clang/test/CXX/class.derived/class.abstract/p3.cpp @@ -0,0 +1,97 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +struct A { + A() {} + A(int) : A() {} // ok + + virtual void f() = 0; // expected-note 1+{{unimplemented}} +}; + +template<typename> struct SecretlyAbstract { + SecretlyAbstract(); + SecretlyAbstract(int); + virtual void f() = 0; // expected-note 1+{{unimplemented}} +}; +using B = SecretlyAbstract<int>; +using C = SecretlyAbstract<float>; +using D = SecretlyAbstract<char>[1]; + +B b; // expected-error {{abstract class}} +D d; // expected-error {{abstract class}} + +template<int> struct N; + +// Note: C is not instantiated anywhere in this file, so we never discover that +// it is in fact abstract. The C++ standard suggests that we need to +// instantiate in all cases where abstractness could affect the validity of a +// program, but that breaks a *lot* of code, so we don't do that. +// +// FIXME: Once DR1640 is resolved, remove the check on forming an abstract +// array type entirely. The only restriction we need is that you can't create +// an object of abstract (most-derived) type. + + +// An abstract class shall not be used + +// - as a parameter type +void f(A&); +void f(A); // expected-error {{abstract class}} +void f(A[1]); // expected-error {{abstract class}} +void f(B); // expected-error {{abstract class}} +void f(B[1]); // expected-error {{abstract class}} +void f(C); +void f(C[1]); +void f(D); // expected-error {{abstract class}} +void f(D[1]); // expected-error {{abstract class}} + +// - as a function return type +A &f(N<0>); +A *f(N<1>); +A f(N<2>); // expected-error {{abstract class}} +A (&f(N<3>))[2]; // expected-error {{abstract class}} +B f(N<4>); // expected-error {{abstract class}} +B (&f(N<5>))[2]; // expected-error {{abstract class}} +C f(N<6>); +C (&f(N<7>))[2]; + +// - as the type of an explicit conversion +void g(A&&); +void h() { + A(); // expected-error {{abstract class}} + A(0); // expected-error {{abstract class}} + A{}; // expected-error {{abstract class}} + A{0}; // expected-error {{abstract class}} + (A)(0); // expected-error {{abstract class}} + (A){}; // expected-error {{abstract class}} + (A){0}; // expected-error {{abstract class}} + + D(); // expected-error {{array type}} + D{}; // expected-error {{abstract class}} + D{0}; // expected-error {{abstract class}} + (D){}; // expected-error {{abstract class}} + (D){0}; // expected-error {{abstract class}} +} + +template<typename T> void t(T); // expected-note 2{{abstract class}} +void i(A &a, B &b, C &c, D &d) { + // FIXME: These should be handled consistently. We currently reject the first + // two early because we (probably incorrectly, depending on dr1640) take + // abstractness into account in forming implicit conversion sequences. + t(a); // expected-error {{no matching function}} + t(b); // expected-error {{no matching function}} + t(c); // expected-error {{allocating an object of abstract class type}} + t(d); // ok, decays to pointer +} + +struct E : A { + E() : A() {} // ok + E(int n) : A( A(n) ) {} // expected-error {{abstract class}} +}; + +namespace std { + template<typename T> struct initializer_list { + const T *begin, *end; + initializer_list(); + }; +} +std::initializer_list<A> ila = {1, 2, 3, 4}; // expected-error {{abstract class}} |

