
This is a major change on how we represent nested name qualifications in the AST. * The nested name specifier itself and how it's stored is changed. The prefixes for types are handled within the type hierarchy, which makes canonicalization for them super cheap, no memory allocation required. Also translating a type into nested name specifier form becomes a no-op. An identifier is stored as a DependentNameType. The nested name specifier gains a lightweight handle class, to be used instead of passing around pointers, which is similar to what is implemented for TemplateName. There is still one free bit available, and this handle can be used within a PointerUnion and PointerIntPair, which should keep bit-packing aficionados happy. * The ElaboratedType node is removed, all type nodes in which it could previously apply to can now store the elaborated keyword and name qualifier, tail allocating when present. * TagTypes can now point to the exact declaration found when producing these, as opposed to the previous situation of there only existing one TagType per entity. This increases the amount of type sugar retained, and can have several applications, for example in tracking module ownership, and other tools which care about source file origins, such as IWYU. These TagTypes are lazily allocated, in order to limit the increase in AST size. This patch offers a great performance benefit. It greatly improves compilation time for [stdexec](https://github.com/NVIDIA/stdexec). For one datapoint, for `test_on2.cpp` in that project, which is the slowest compiling test, this patch improves `-c` compilation time by about 7.2%, with the `-fsyntax-only` improvement being at ~12%. This has great results on compile-time-tracker as well:  This patch also further enables other optimziations in the future, and will reduce the performance impact of template specialization resugaring when that lands. It has some other miscelaneous drive-by fixes. About the review: Yes the patch is huge, sorry about that. Part of the reason is that I started by the nested name specifier part, before the ElaboratedType part, but that had a huge performance downside, as ElaboratedType is a big performance hog. I didn't have the steam to go back and change the patch after the fact. There is also a lot of internal API changes, and it made sense to remove ElaboratedType in one go, versus removing it from one type at a time, as that would present much more churn to the users. Also, the nested name specifier having a different API avoids missing changes related to how prefixes work now, which could make existing code compile but not work. How to review: The important changes are all in `clang/include/clang/AST` and `clang/lib/AST`, with also important changes in `clang/lib/Sema/TreeTransform.h`. The rest and bulk of the changes are mostly consequences of the changes in API. PS: TagType::getDecl is renamed to `getOriginalDecl` in this patch, just for easier to rebasing. I plan to rename it back after this lands. Fixes #136624 Fixes https://github.com/llvm/llvm-project/issues/43179 Fixes https://github.com/llvm/llvm-project/issues/68670 Fixes https://github.com/llvm/llvm-project/issues/92757
270 lines
12 KiB
C++
270 lines
12 KiB
C++
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -verify %s
|
|
|
|
template <typename T> struct A { // expected-note 38{{declared here}}
|
|
constexpr A() {}
|
|
constexpr A(int) {}
|
|
constexpr operator int() { return 0; }
|
|
};
|
|
A() -> A<int>;
|
|
A(int) -> A<int>;
|
|
|
|
// Make sure we still correctly parse cases where a template can appear without arguments.
|
|
namespace template_template_arg {
|
|
template<template<typename> typename> struct X {};
|
|
template<typename> struct Y {};
|
|
|
|
X<A> xa;
|
|
Y<A> ya; // expected-error {{requires template arguments}}
|
|
X<::A> xcca;
|
|
Y<::A> ycca; // expected-error {{requires template arguments}}
|
|
X<A*> xap; // expected-error {{requires template arguments}}
|
|
X<const A> xca; // expected-error {{requires template arguments}}
|
|
X<A const> xac; // expected-error {{requires template arguments}}
|
|
// FIXME: This should not parse as a template template argument due to the
|
|
// trailing attributes.
|
|
X<A [[]]> xa_attr;
|
|
|
|
template<template<typename> typename = A> struct XD {};
|
|
template<typename = A> struct YD {}; // expected-error {{requires template arguments}}
|
|
template<template<typename> typename = ::A> struct XCCD {};
|
|
template<typename = ::A> struct YCCD {}; // expected-error {{requires template arguments}}
|
|
|
|
// FIXME: replacing the invalid type with 'int' here is horrible
|
|
template <A a = A<int>()> class C { }; // expected-error {{requires template arguments}}
|
|
template<typename T = A> struct G { }; // expected-error {{requires template arguments}}
|
|
}
|
|
|
|
namespace template_template_arg_pack {
|
|
template<template<typename> typename...> struct XP {};
|
|
template<typename...> struct YP {};
|
|
|
|
struct Z { template<typename T> struct Q {}; }; // expected-note 2{{here}}
|
|
|
|
template<typename T> using ZId = Z;
|
|
|
|
template<typename ...Ts> struct A {
|
|
XP<ZId<Ts>::Q...> xe;
|
|
YP<ZId<Ts>::Q...> ye; // expected-error {{requires template arguments}}
|
|
|
|
XP<ZId<Ts>::Q> xp; // expected-error {{unexpanded parameter pack}}
|
|
YP<ZId<Ts>::Q> yp; // expected-error {{requires template arguments}}
|
|
};
|
|
}
|
|
|
|
namespace injected_class_name {
|
|
template<typename T> struct A {
|
|
A(T);
|
|
void f(int) { // expected-note {{previous}}
|
|
A a = 1;
|
|
injected_class_name::A b = 1; // expected-note {{in instantiation of template class 'injected_class_name::A<int>'}}
|
|
}
|
|
void f(T); // expected-error {{multiple overloads of 'f' instantiate to the same signature 'void (int)}}
|
|
};
|
|
A<short> ai = 1;
|
|
A<double>::A b(1); // expected-error {{constructor name}}
|
|
}
|
|
|
|
struct member {
|
|
A a; // expected-error {{requires template arguments}}
|
|
A *b; // expected-error {{requires template arguments}}
|
|
const A c; // expected-error {{requires template arguments}}
|
|
|
|
void f() throw (A); // expected-error {{requires template arguments}}
|
|
|
|
friend A; // expected-error {{requires template arguments; argument deduction not allowed in friend declaration}}
|
|
|
|
operator A(); // expected-error {{requires template arguments; argument deduction not allowed in conversion function type}}
|
|
|
|
static A x; // expected-error {{declaration of variable 'x' with deduced type 'A' requires an initializer}}
|
|
static constexpr A y = 0;
|
|
};
|
|
|
|
namespace in_typedef {
|
|
typedef A *AutoPtr; // expected-error {{requires template arguments; argument deduction not allowed in typedef}}
|
|
typedef A (*PFun)(int a); // expected-error{{requires template arguments; argument deduction not allowed in typedef}}
|
|
typedef A Fun(int a) -> decltype(a + a); // expected-error{{requires template arguments; argument deduction not allowed in function return type}}
|
|
}
|
|
|
|
namespace stmt {
|
|
void g(A a) { // expected-error{{requires template arguments; argument deduction not allowed in function prototype}}
|
|
try { }
|
|
catch (A &a) { } // expected-error{{requires template arguments; argument deduction not allowed in exception declaration}}
|
|
catch (const A a) { } // expected-error{{requires template arguments; argument deduction not allowed in exception declaration}}
|
|
try { } catch (A a) { } // expected-error{{requires template arguments; argument deduction not allowed in exception declaration}}
|
|
|
|
// FIXME: The standard only permits class template argument deduction in a
|
|
// simple-declaration or cast. We also permit it in conditions,
|
|
// for-range-declarations, member-declarations for static data members, and
|
|
// new-expressions, because not doing so would be bizarre.
|
|
A local = 0;
|
|
static A local_static = 0;
|
|
static thread_local A thread_local_static = 0;
|
|
if (A a = 0) {}
|
|
if (A a = 0; a) {}
|
|
switch (A a = 0) {} // expected-warning {{no case matching constant switch condition '0'}}
|
|
switch (A a = 0; a) {} // expected-warning {{no case matching constant switch condition '0'}}
|
|
for (A a = 0; a; /**/) {}
|
|
for (/**/; A a = 0; /**/) {}
|
|
while (A a = 0) {}
|
|
int arr[3];
|
|
for (A a : arr) {}
|
|
}
|
|
|
|
namespace std {
|
|
class type_info;
|
|
}
|
|
}
|
|
|
|
namespace expr {
|
|
template<typename T> struct U {};
|
|
void j() {
|
|
(void)typeid(A); // expected-error{{requires template arguments; argument deduction not allowed here}}
|
|
(void)sizeof(A); // expected-error{{requires template arguments; argument deduction not allowed here}}
|
|
(void)__alignof(A); // expected-error{{requires template arguments; argument deduction not allowed here}}
|
|
|
|
U<A> v; // expected-error {{requires template arguments}}
|
|
|
|
int n;
|
|
(void)dynamic_cast<A&>(n); // expected-error{{requires template arguments; argument deduction not allowed here}}
|
|
(void)static_cast<A*>(&n); // expected-error{{requires template arguments; argument deduction not allowed here}}
|
|
(void)reinterpret_cast<A*>(&n); // expected-error{{requires template arguments; argument deduction not allowed here}}
|
|
(void)const_cast<A>(n); // expected-error{{requires template arguments; argument deduction not allowed here}}
|
|
(void)*(A*)(&n); // expected-error{{requires template arguments; argument deduction not allowed here}}
|
|
(void)(A)(n); // expected-error{{requires template arguments; argument deduction not allowed here}}
|
|
(void)(A){n}; // expected-error{{requires template arguments; argument deduction not allowed here}}
|
|
|
|
(void)A(n);
|
|
(void)A{n};
|
|
(void)new A(n);
|
|
(void)new A{n};
|
|
(void)new A;
|
|
}
|
|
}
|
|
|
|
namespace decl {
|
|
enum E : A {}; // expected-error{{requires template arguments; argument deduction not allowed here}}
|
|
struct F : A {}; // expected-error{{expected class name}}
|
|
|
|
using B = A; // expected-error{{requires template arguments}}
|
|
|
|
auto k() -> A; // expected-error{{requires template arguments}}
|
|
|
|
A a;
|
|
A b = 0;
|
|
const A c = 0;
|
|
A (parens) = 0;
|
|
A *p = 0; // expected-error {{cannot form pointer to deduced class template specialization type}}
|
|
A &r = *p; // expected-error {{cannot form reference to deduced class template specialization type}}
|
|
A arr[3] = 0; // expected-error {{cannot form array of deduced class template specialization type}}
|
|
A F::*pm = 0; // expected-error {{cannot form pointer to deduced class template specialization type}}
|
|
A (*fp)() = 0; // expected-error {{cannot form function returning deduced class template specialization type}}
|
|
A [x, y] = 0; // expected-error {{cannot be declared with type 'A'}} expected-error {{type 'A<int>' decomposes into 0 elements, but 2 names were provided}}
|
|
}
|
|
|
|
namespace typename_specifier {
|
|
struct F {};
|
|
|
|
void e() {
|
|
(void) typename ::A(0);
|
|
(void) typename ::A{0};
|
|
new typename ::A(0);
|
|
new typename ::A{0};
|
|
typename ::A a = 0;
|
|
const typename ::A b = 0;
|
|
if (typename ::A a = 0) {}
|
|
for (typename ::A a = 0; typename ::A b = 0; /**/) {}
|
|
|
|
(void)(typename ::A)(0); // expected-error{{requires template arguments; argument deduction not allowed here}}
|
|
(void)(typename ::A){0}; // expected-error{{requires template arguments; argument deduction not allowed here}}
|
|
}
|
|
typename ::A a = 0;
|
|
const typename ::A b = 0;
|
|
typename ::A (parens) = 0;
|
|
typename ::A *p = 0; // expected-error {{cannot form pointer to deduced class template specialization type}}
|
|
typename ::A &r = *p; // expected-error {{cannot form reference to deduced class template specialization type}}
|
|
typename ::A arr[3] = 0; // expected-error {{cannot form array of deduced class template specialization type}}
|
|
typename ::A F::*pm = 0; // expected-error {{cannot form pointer to deduced class template specialization type}}
|
|
typename ::A (*fp)() = 0; // expected-error {{cannot form function returning deduced class template specialization type}}
|
|
typename ::A [x, y] = 0; // expected-error {{cannot be declared with type 'typename ::A'}} expected-error {{type 'typename ::A<int>' (aka 'A<int>') decomposes into 0}}
|
|
|
|
struct X { template<typename T> struct A { A(T); }; }; // expected-note 8{{declared here}}
|
|
|
|
template<typename T> void f() {
|
|
(void) typename T::A(0);
|
|
(void) typename T::A{0};
|
|
new typename T::A(0);
|
|
new typename T::A{0};
|
|
typename T::A a = 0;
|
|
const typename T::A b = 0;
|
|
if (typename T::A a = 0) {} // expected-error {{value of type 'typename typename_specifier::X::A<int>' (aka 'typename_specifier::X::A<int>') is not contextually convertible to 'bool'}}
|
|
for (typename T::A a = 0; typename T::A b = 0; /**/) {} // expected-error {{value of type 'typename typename_specifier::X::A<int>' (aka 'typename_specifier::X::A<int>') is not contextually convertible to 'bool'}}
|
|
|
|
{(void)(typename T::A)(0);} // expected-error{{refers to class template member}}
|
|
{(void)(typename T::A){0};} // expected-error{{refers to class template member}}
|
|
{typename T::A (parens) = 0;} // expected-error {{refers to class template member in 'typename_specifier::X'; argument deduction not allowed here}}
|
|
// expected-warning@-1 {{disambiguated as redundant parentheses around declaration of variable named 'parens'}} expected-note@-1 {{add a variable name}} expected-note@-1{{remove parentheses}} expected-note@-1 {{add enclosing parentheses}}
|
|
{typename T::A *p = 0;} // expected-error {{refers to class template member}}
|
|
{typename T::A &r = *p;} // expected-error {{refers to class template member}}
|
|
{typename T::A arr[3] = 0;} // expected-error {{refers to class template member}}
|
|
{typename T::A F::*pm = 0;} // expected-error {{refers to class template member}}
|
|
{typename T::A (*fp)() = 0;} // expected-error {{refers to class template member}}
|
|
{typename T::A [x, y] = 0;} // expected-error {{cannot be declared with type 'typename T::A'}} expected-error {{type 'typename typename_specifier::X::A<int>' (aka 'typename_specifier::X::A<int>') decomposes into 0}}
|
|
}
|
|
template void f<X>(); // expected-note {{instantiation of}}
|
|
|
|
template<typename T> void g(typename T::A = 0); // expected-note {{refers to class template member}}
|
|
void h() { g<X>(); } // expected-error {{no matching function}}
|
|
}
|
|
|
|
namespace parenthesized {
|
|
template<typename T> struct X { X(T); };
|
|
auto n = (X([]{}));
|
|
}
|
|
|
|
namespace within_template_arg_list {
|
|
template<typename T> struct X { constexpr X(T v) : v(v) {} T v; };
|
|
template<int N = X(1).v> struct Y {};
|
|
using T = Y<>;
|
|
using T = Y<X(1).v>;
|
|
using T = Y<within_template_arg_list::X(1).v>;
|
|
|
|
template<int ...N> struct Z { Y<X(N)...> y; };
|
|
}
|
|
|
|
namespace PR49735 {
|
|
// Ensure that we do not crash when parsing code which looks like an invalid
|
|
// deduction guide declaration.
|
|
template<class> struct B; // expected-note 2{{template is declared here}}
|
|
struct A1 {
|
|
B() noexcept(false); // expected-error {{deduction guide must be declared in the same scope as template 'PR49735::B'}} \
|
|
// expected-error {{deduction guide declaration without trailing return type}}
|
|
};
|
|
|
|
struct A2 {
|
|
template <typename Ty>
|
|
B() noexcept(false); // expected-error {{deduction guide must be declared in the same scope as template 'PR49735::B'}} \
|
|
// expected-error {{deduction guide declaration without trailing return type}}
|
|
};
|
|
|
|
}
|
|
|
|
namespace GH57495 {
|
|
template <typename T> struct vector{};
|
|
|
|
void f() {
|
|
GH57495::vector.d; // expected-error {{cannot use dot operator on a type}}
|
|
}
|
|
}
|
|
|
|
namespace GH107887 {
|
|
|
|
namespace a {
|
|
template <class> struct pair; // expected-note 3{{declared here}}
|
|
}
|
|
template <class T2> pair() -> pair<T2>; // expected-error 2{{no template named 'pair'}} \
|
|
// expected-error {{deduction guide must be declared in the same scope}} \
|
|
// expected-error {{cannot be deduced}} \
|
|
// expected-note {{non-deducible template parameter 'T2'}}
|
|
|
|
}
|