reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   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}}