Consider the following:
```
template<typename T>
struct A
{
struct B : A { };
};
```
According to [class.derived.general] p2:
> [...] A _class-or-decltype_ shall denote a (possibly cv-qualified)
class type that is not an incompletely defined class; any cv-qualifiers
are ignored. [...]
Although GCC and EDG rejects this, Clang accepts it. This is incorrect,
as `A` is incomplete within its own definition (outside of a
complete-class context). This patch correctly diagnoses instances where
the current instantiation is used as a base class before it is complete.
Conversely, Clang erroneously rejects the following:
```
template<typename T>
struct A
{
struct B;
struct C : B { };
struct B : C { }; // error: circular inheritance between 'C' and 'A::B'
};
```
Though it may seem like no valid specialization of this template can be
instantiated, an explicit specialization of either member classes for an
implicit instantiated specialization of `A` would permit the definition
of the other member class to be instantiated, e.g.:
```
template<>
struct A<int>::B { };
A<int>::C c; // ok
```
So this patch also does away with this error. This means that circular
inheritance is diagnosed during instantiation of the definition as a
consequence of requiring the base class type to be complete (matching
the behavior of GCC and EDG).
49 lines
1.1 KiB
C++
49 lines
1.1 KiB
C++
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s
|
|
|
|
using nullptr_t = decltype(nullptr);
|
|
|
|
template<typename T>
|
|
struct Base {
|
|
T inner;
|
|
};
|
|
|
|
int z;
|
|
|
|
template<typename T>
|
|
struct X : Base<T> {
|
|
static int z;
|
|
|
|
template<int U>
|
|
struct Inner {
|
|
};
|
|
|
|
bool f(T other) {
|
|
// A pair of comparisons; 'inner' is a dependent name so can't be assumed
|
|
// to be a template.
|
|
return this->inner < other > ::z; // expected-warning {{comparisons like 'X<=Y<=Z' don't have their mathematical meaning}}
|
|
}
|
|
};
|
|
|
|
void use_x(X<int> x) { x.f(0); } // expected-note {{requested here}}
|
|
|
|
template<typename T>
|
|
struct Y {
|
|
static int z;
|
|
|
|
template<int U>
|
|
struct Inner; // expected-note {{declared here}}
|
|
|
|
bool f(T other) {
|
|
// We can determine that 'inner' does not exist at parse time, so can
|
|
// perform typo correction in this case.
|
|
return this->inner<other>::z; // expected-error {{no template named 'inner' in 'Y<T>'; did you mean 'Inner'?}}
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
template<int U>
|
|
struct Y<T>::Inner : Y { };
|
|
|
|
struct Q { constexpr operator int() { return 0; } };
|
|
void use_y(Y<Q> x) { x.f(Q()); }
|