llvm-project/clang/test/SemaTemplate/instantiate-local-class.cpp
Younan Zhang d1a80deae6
Reapply "[Clang] Fix dependent local class instantiation bugs" (#135914)
This reapplies #134038

Since the last patch, this fixes a null pointer dereference where the
TSI of the destructor wasn't properly propagated into the
DeclarationNameInfo. We now construct a LocInfoType for dependent cases,
as done elsewhere in getDestructorName, such that GetTypeFromParser can
correctly obtain the TSI.

---

This patch fixes two long-standing bugs that prevent Clang from
instantiating local class members inside a dependent context. These bugs
were introduced in commits
21eb1af469
and
919df9d75a.


21eb1af469
introduced a concept called eligible methods such that it did an attempt
to skip past ineligible method instantiation when instantiating class
members. Unfortunately, this broke the instantiation chain for local
classes - getTemplateInstantiationPattern() would fail to find the
correct definition pattern if the class was defined within a partially
transformed dependent context.


919df9d75a
introduced a separate issue by incorrectly copying the
DeclarationNameInfo during function definition instantiation from the
template pattern, even though that DNI might contain a transformed
TypeSourceInfo. Since that TSI was already updated when the declaration
was instantiated, this led to inconsistencies. As a result, the final
instantiated function could lose track of the transformed declarations,
hence we crash: https://compiler-explorer.com/z/vjvoG76Tf.

This PR corrects them by

1. Removing the bypass logic for method instantiation. The eligible flag
is independent of instantiation and can be updated properly afterward,
so skipping instantiation is unnecessary.

2. Carefully handling TypeSourceInfo by creating a new instance that
preserves the pattern's source location while using the already
transformed type.
2025-04-17 14:34:32 +08:00

572 lines
13 KiB
C++

// RUN: %clang_cc1 -verify -std=c++11 %s
// RUN: %clang_cc1 -verify -std=c++11 -fdelayed-template-parsing %s
// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s
template<typename T>
void f0() {
struct X;
typedef struct Y {
T (X::* f1())(int) { return 0; }
} Y2;
Y2 y = Y();
}
template void f0<int>();
// PR5764
namespace PR5764 {
struct X {
template <typename T>
void Bar() {
typedef T ValueType;
struct Y {
Y() { V = ValueType(); }
ValueType V;
};
Y y;
}
};
void test(X x) {
x.Bar<int>();
}
}
// Instantiation of local classes with virtual functions.
namespace local_class_with_virtual_functions {
template <typename T> struct X { };
template <typename T> struct Y { };
template <typename T>
void f() {
struct Z : public X<Y<T>*> {
virtual void g(Y<T>* y) { }
void g2(int x) {(void)x;}
};
Z z;
(void)z;
}
struct S { };
void test() { f<S>(); }
}
namespace PR8801 {
template<typename T>
void foo() {
class X;
typedef int (X::*pmf_type)();
class X : public T { };
pmf_type pmf = &T::foo;
}
struct Y { int foo(); };
template void foo<Y>();
}
namespace TemplatePacksAndLambdas {
template <typename ...T> int g(T...);
struct S {
template <typename ...T> static void f(int f = g([]{ static T t; return ++t; }()...)) {}
};
void h() { S::f<int, int, int>(); }
}
namespace PR9685 {
template <class Thing> void forEach(Thing t) { t.func(); }
template <typename T> void doIt() {
struct Functor {
void func() { (void)i; }
int i;
};
forEach(Functor());
}
void call() {
doIt<int>();
}
}
namespace PR12702 {
struct S {
template <typename F> bool apply(F f) { return f(); }
};
template <typename> struct T {
void foo() {
struct F {
int x;
bool operator()() { return x == 0; }
};
S().apply(F());
}
};
void call() { T<int>().foo(); }
}
namespace PR17139 {
template <class T> void foo(const T &t) { t.foo(); }
template <class F> void bar(F *f) {
struct B {
F *fn;
void foo() const { fn(); }
} b = { f };
foo(b);
}
void go() {}
void test() { bar(go); }
}
namespace PR17740 {
class C {
public:
template <typename T> static void foo(T function);
template <typename T> static void bar(T function);
template <typename T> static void func(T function);
};
template <typename T> void C::foo(T function) { function(); }
template <typename T> void C::bar(T function) {
foo([&function]() { function(); });
}
template <typename T> void C::func(T function) {
struct Struct {
T mFunction;
Struct(T function) : mFunction(function) {};
void operator()() {
mFunction();
};
};
bar(Struct(function));
}
void call() {
C::func([]() {});
}
}
namespace PR14373 {
struct function {
template <typename _Functor> function(_Functor __f) { __f(); }
};
template <typename Func> function exec_func(Func f) {
struct functor {
functor(Func f) : func(f) {}
void operator()() const { func(); }
Func func;
};
return functor(f);
}
struct Type {
void operator()() const {}
};
int call() {
exec_func(Type());
return 0;
}
}
namespace PR18907 {
template <typename>
class C : public C<int> {}; // expected-error{{within its own definition}}
template <typename X>
void F() {
struct A : C<X> {};
}
struct B {
void f() { F<int>(); }
};
}
namespace PR23194 {
struct X {
int operator()() const { return 0; }
};
struct Y {
Y(int) {}
};
template <bool = true> int make_seed_pair() noexcept {
struct state_t {
X x;
Y y{x()};
};
return 0;
}
int func() {
return make_seed_pair();
}
}
namespace PR18653 {
// Forward declarations
template<typename T> void f1() {
void g1(struct x1);
struct x1 {};
}
template void f1<int>();
template<typename T> void f1a() {
void g1(union x1);
union x1 {};
}
template void f1a<int>();
template<typename T> void f2() {
void g2(enum x2); // expected-error{{ISO C++ forbids forward references to 'enum' types}}
enum x2 { nothing };
}
template void f2<int>();
template<typename T> void f3() {
enum class x3;
void g3(enum x3);
enum class x3 { nothing };
}
template void f3<int>();
template<typename T> void f4() {
void g4(struct x4 {} x); // expected-error{{'x4' cannot be defined in a parameter type}}
}
template void f4<int>();
template<typename T> void f4a() {
void g4(union x4 {} x); // expected-error{{'x4' cannot be defined in a parameter type}}
}
template void f4a<int>();
template <class T> void f();
template <class T> struct S1 {
void m() {
f<class newclass>();
f<union newunion>();
}
};
template struct S1<int>;
template <class T> struct S2 {
void m() {
f<enum new_enum>(); // expected-error{{ISO C++ forbids forward references to 'enum' types}}
}
};
template struct S2<int>;
template <class T> struct S3 {
void m() {
enum class new_enum;
f<enum new_enum>();
}
};
template struct S3<int>;
template <class T> struct S4 {
struct local {};
void m() {
f<local>();
}
};
template struct S4<int>;
template <class T> struct S4a {
union local {};
void m() {
f<local>();
}
};
template struct S4a<int>;
template <class T> struct S5 {
enum local { nothing };
void m() {
f<local>();
}
};
template struct S5<int>;
template <class T> struct S7 {
enum class local { nothing };
void m() {
f<local>();
}
};
template struct S7<int>;
template <class T> void fff(T *x);
template <class T> struct S01 {
struct local { };
void m() {
local x;
fff(&x);
}
};
template struct S01<int>;
template <class T> struct S01a {
union local { };
void m() {
local x;
fff(&x);
}
};
template struct S01a<int>;
template <class T> struct S02 {
enum local { nothing };
void m() {
local x;
fff(&x);
}
};
template struct S02<int>;
template <class T> struct S03 {
enum class local { nothing };
void m() {
local x;
fff(&x);
}
};
template struct S03<int>;
template <class T> struct S04 {
void m() {
struct { } x;
fff(&x);
}
};
template struct S04<int>;
template <class T> struct S04a {
void m() {
union { } x;
fff(&x);
}
};
template struct S04a<int>;
template <class T> struct S05 {
void m() {
enum { nothing } x;
fff(&x);
}
};
template struct S05<int>;
template <class T> struct S06 {
void m() {
class { virtual void mmm() {} } x;
fff(&x);
}
};
template struct S06<int>;
}
namespace PR20625 {
template <typename T>
void f() {
struct N {
static constexpr int get() { return 42; }
};
constexpr int n = N::get();
static_assert(n == 42, "n == 42");
}
void g() { f<void>(); }
}
namespace PR21332 {
template<typename T> void f1() {
struct S { // expected-note{{in instantiation of member class 'S' requested here}}
void g1(int n = T::error); // expected-error{{type 'int' cannot be used prior to '::' because it has no members}} \
// expected-note {{in instantiation of default function argument expression for 'g1<int>' required here}}
};
}
template void f1<int>(); // expected-note{{in instantiation of function template specialization 'PR21332::f1<int>' requested here}}
template<typename T> void f2() {
struct S { // expected-note{{in instantiation of member class 'S' requested here}}
void g2() noexcept(T::error); // expected-error{{type 'int' cannot be used prior to '::' because it has no members}}
};
}
template void f2<int>(); // expected-note{{in instantiation of function template specialization 'PR21332::f2<int>' requested here}}
template<typename T> void f3() {
enum S {
val = T::error; // expected-error{{expected '}' or ','}} expected-error{{type 'int' cannot be used prior to '::' because it has no members}}
};
}
template void f3<int>(); //expected-note{{in instantiation of function template specialization 'PR21332::f3<int>' requested here}}
template<typename T> void f4() {
enum class S {
val = T::error; // expected-error{{expected '}' or ','}} expected-error{{type 'int' cannot be used prior to '::' because it has no members}}
};
}
template void f4<int>(); // expected-note{{in instantiation of function template specialization 'PR21332::f4<int>' requested here}}
template<typename T> void f5() {
class S { // expected-note {{in instantiation of default member initializer 'PR21332::f5()::S::val' requested here}}
int val = T::error; // expected-error {{type 'int' cannot be used prior to '::' because it has no members}}
};
}
template void f5<int>(); // expected-note {{in instantiation of function template specialization 'PR21332::f5<int>' requested here}}
template<typename T> void f6() {
class S { // expected-note {{in instantiation of member function 'PR21332::f6()::S::get' requested here}}
void get() {
class S2 { // expected-note {{in instantiation of member class 'S2' requested here}}
void g1(int n = T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}} \
// expected-note {{in instantiation of default function argument expression for 'g1<int>' required here}}
};
}
};
}
template void f6<int>(); // expected-note{{in instantiation of function template specialization 'PR21332::f6<int>' requested here}}
template<typename T> void f7() {
struct S { void g() noexcept(undefined_val); }; // expected-error{{use of undeclared identifier 'undefined_val'}}
}
template void f7<int>();
}
// Ensure that we correctly perform implicit conversions when instantiating the
// default arguments of local functions.
namespace rdar23721638 {
struct A {
A(const char *) = delete; // expected-note 2 {{explicitly marked deleted here}}
};
template <typename T> void foo() {
struct Inner { // expected-note {{in instantiation}}
void operator()(T a = "") {} // expected-error {{conversion function from 'const char[1]' to 'rdar23721638::A' invokes a deleted function}} \
// expected-note {{in instantiation of default function argument expression for 'operator()<rdar23721638::A>' required here}} \
// expected-note {{passing argument to parameter 'a' here}}
};
Inner()();
}
template void foo<A>(); // expected-note {{in instantiation}}
template <typename T> void bar() {
auto lambda = [](T a = "") {}; // expected-error {{conversion function from 'const char[1]' to 'rdar23721638::A' invokes a deleted function}} \
// expected-note {{in instantiation of default function argument expression for 'operator()<rdar23721638::A>' required here}} \
// expected-note {{passing argument to parameter 'a' here}}
lambda();
}
template void bar<A>(); // expected-note {{in instantiation}}
}
namespace anon_union_default_member_init {
template<typename T> void f() {
struct S {
union {
int i = 0;
};
};
}
void g() { f<int>(); }
}
namespace PR45000 {
template <typename T>
void f(int x = [](T x = nullptr) -> int { return x; }());
// expected-error@-1 {{cannot initialize a parameter of type 'int' with an rvalue of type 'std::nullptr_t'}}
// expected-note@-2 {{in instantiation of default function argument expression for 'operator()<int>' required here}}
// expected-note@-3 {{passing argument to parameter 'x' here}}
void g() { f<int>(); }
// expected-note@-1 {{in instantiation of default function argument expression for 'f<int>' required here}}
}
namespace LambdaInDefaultMemberInitializer {
template<typename T> void f() {
struct S {
void *p = [this] { return &p; }();
};
}
template void f<int>();
}
#if __cplusplus >= 201703L
// Reduced from https://github.com/llvm/llvm-project/issues/98526
// This relies on the deferral instantiation of the local lambda, otherwise we would fail in DeduceReturnType().
namespace local_recursive_lambda {
template <typename F> struct recursive_lambda {
template <typename... Args> auto operator()(Args &&...args) const {
return fn(*this, args...);
}
F fn;
};
template <typename F> recursive_lambda(F) -> recursive_lambda<F>;
void foo() {
recursive_lambda{[&](auto &self_fn, int) -> int {
return self_fn(0);
}}(0);
}
} // namespace local_recursive_lambda
#endif
namespace PR134038_Regression {
template <class T> class G {
public:
template <class> class Iter {
public:
Iter();
~Iter();
operator G<T>();
};
};
template <class ObserverType>
template <class ContainerType>
G<ObserverType>::Iter<ContainerType>::Iter() {}
template <class ObserverType>
template <class ContainerType>
G<ObserverType>::Iter<ContainerType>::~Iter() {}
template <class ObserverType>
template <class ContainerType>
G<ObserverType>::Iter<ContainerType>::operator G<ObserverType>() {
return G<ObserverType>{};
}
void NotifySettingChanged() {
G<int>::Iter<int> Iter;
G<int> g = Iter;
}
}