Skip to content

Commit 1704d3d

Browse files
committed
Fix missing symbols for template functions with enable_if
Root cause: In getFunctionDisambiguator(), when handling template function instantiations, the code only handled two cases: 1. Non-templated member functions of class templates 2. Member function templates inside class templates It missed the case of member function templates in non-template classes (like Flashing::bind<T>) and free function templates (like ToString<T>). When getInstantiatedFromMemberTemplate() returned null, definingDecl was left pointing at the instantiated declaration, causing unstable/missing symbols. Fix: Added an else branch to use instantiatedTemplateDecl->getTemplatedDecl() directly when getInstantiatedFromMemberTemplate() returns null.
1 parent c0c8142 commit 1704d3d

5 files changed

Lines changed: 274 additions & 26 deletions

File tree

indexer/SymbolFormatter.cc

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -510,34 +510,38 @@ SymbolFormatter::getEnumSymbol(const clang::EnumDecl &enumDecl) {
510510
std::string_view SymbolFormatter::getFunctionDisambiguator(
511511
const clang::FunctionDecl &functionDecl, char buf[16]) {
512512
const clang::FunctionDecl *definingDecl = &functionDecl;
513-
// clang-format off
514-
if (functionDecl.isTemplateInstantiation()) {
515-
// Handle non-templated member functions
516-
if (auto *memberFnDecl = functionDecl.getInstantiatedFromMemberFunction()) {
517-
definingDecl = memberFnDecl;
518-
} else if (auto *templateInfo = functionDecl.getTemplateSpecializationInfo()) {
519-
// Consider code like:
520-
// template <typename T> class C { template <typename U> void f() {} };
521-
// void g() { C<int>().f<int>(); }
522-
// ^ Emitting a reference
523-
//
524-
// The dance below gets to the original declaration in 3 steps:
525-
// C<int>.f<int> (FunctionDecl) → C<int>.f<$U> (FunctionTemplateDecl)
526-
//
527-
// C<$T>.f<$U> (FunctionDecl) ← C<$T>.f<$U> (FunctionTemplateDecl)
528-
auto *instantiatedTemplateDecl = templateInfo->getTemplate();
529-
// For some reason, we end up on this code path for overloaded
530-
// literal operators. In that case, uninstantiatedTemplateDecl
531-
// can be null.
532-
if (auto *uninstantiatedTemplateDecl = instantiatedTemplateDecl->getInstantiatedFromMemberTemplate()) {
533-
definingDecl = uninstantiatedTemplateDecl->getTemplatedDecl();
534-
}
513+
if (functionDecl.isTemplateInstantiation()) {
514+
// Handle non-templated member functions of class templates
515+
if (auto *memberFnDecl = functionDecl.getInstantiatedFromMemberFunction()) {
516+
definingDecl = memberFnDecl;
517+
} else if (auto *templateInfo =
518+
functionDecl.getTemplateSpecializationInfo()) {
519+
// Consider code like:
520+
// template <typename T> class C { template <typename U> void f() {} };
521+
// void g() { C<int>().f<int>(); }
522+
// ^ Emitting a reference
523+
//
524+
// The dance below gets to the original declaration in 3 steps:
525+
// C<int>.f<int> (FunctionDecl) → C<int>.f<$U> (FunctionTemplateDecl)
526+
//
527+
// C<$T>.f<$U> (FunctionDecl) ← C<$T>.f<$U> (FunctionTemplateDecl)
528+
auto *instantiatedTemplateDecl = templateInfo->getTemplate();
529+
// For some reason, we end up on this code path for overloaded
530+
// literal operators. In that case, uninstantiatedTemplateDecl
531+
// can be null.
532+
if (auto *uninstantiatedTemplateDecl =
533+
instantiatedTemplateDecl->getInstantiatedFromMemberTemplate()) {
534+
definingDecl = uninstantiatedTemplateDecl->getTemplatedDecl();
535+
} else {
536+
// For free function templates or member function templates that are
537+
// not themselves inside a class template, use the template's decl
538+
// directly to get the uninstantiated type signature.
539+
definingDecl = instantiatedTemplateDecl->getTemplatedDecl();
535540
}
536541
}
537-
// clang-format on
542+
}
538543
// 64-bit hash in hex should take 16 characters at most.
539544
auto typeString = definingDecl->getType().getCanonicalType().getAsString();
540-
// char buf[16] = {0};
541545
auto *end = fmt::format_to(buf, "{:x}", HashValue::forText(typeString));
542546
return std::string_view{buf, end};
543547
}

test/index/cuda/kernelcall.snapshot.cu

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@
151151
}
152152
void e() { d0(1); }
153153
// ^ definition [..] b#e(49f6e7a06ebc5aa8).
154-
// ^^ reference [..] b#d0(d4f767463ce0a6b3).
154+
// ^^ reference [..] b#d0(9b289cee16747614).
155155
};
156156

157157
namespace x {
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Test for member function templates with enable_if in non-template classes,
2+
// and free function templates with enable_if. These patterns were previously
3+
// broken because getFunctionDisambiguator didn't handle the case where
4+
// getInstantiatedFromMemberTemplate() returns null for such templates.
5+
6+
namespace std {
7+
template <bool B, class T = void>
8+
struct enable_if {};
9+
10+
template <class T>
11+
struct enable_if<true, T> {
12+
using type = T;
13+
};
14+
15+
template <class T>
16+
struct is_integral {
17+
static constexpr bool value = false;
18+
};
19+
20+
template <>
21+
struct is_integral<int> {
22+
static constexpr bool value = true;
23+
};
24+
25+
template <class T>
26+
struct is_enum {
27+
static constexpr bool value = false;
28+
};
29+
} // namespace std
30+
31+
// Issue 1: Member function template with enable_if in a non-template class
32+
class Widget {
33+
public:
34+
template <typename T,
35+
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
36+
void process(T value) {
37+
(void)value;
38+
}
39+
};
40+
41+
// Issue 2: Free function template with enable_if
42+
template <typename T,
43+
typename std::enable_if<!std::is_enum<T>::value, bool>::type = false>
44+
T convert(int x) {
45+
return static_cast<T>(x);
46+
}
47+
48+
// Issue 3: Member call through a pointer wrapper (simplified unique_ptr)
49+
template <typename T>
50+
class Ptr {
51+
T *p;
52+
53+
public:
54+
Ptr(T *ptr) : p(ptr) {}
55+
T *operator->() const { return p; }
56+
};
57+
58+
class ThreadLoop {
59+
public:
60+
bool isHealthy() const { return true; }
61+
};
62+
63+
class ThreadPool {
64+
Ptr<ThreadLoop> mThread;
65+
66+
public:
67+
ThreadPool() : mThread(new ThreadLoop()) {}
68+
69+
bool checkHealth() const {
70+
return mThread->isHealthy();
71+
}
72+
};
73+
74+
void test() {
75+
Widget w;
76+
w.process(42);
77+
w.process<int>(100);
78+
79+
auto val = convert<int>(5);
80+
(void)val;
81+
82+
ThreadPool pool;
83+
(void)pool.checkHealth();
84+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Test for member function templates with enable_if in non-template classes,
2+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition [..] `<file>/enable_if_templates.cc`/
3+
// and free function templates with enable_if. These patterns were previously
4+
// broken because getFunctionDisambiguator didn't handle the case where
5+
// getInstantiatedFromMemberTemplate() returns null for such templates.
6+
7+
namespace std {
8+
// ^^^ definition [..] std/
9+
template <bool B, class T = void>
10+
// ^ definition local 0
11+
// ^ definition local 1
12+
struct enable_if {};
13+
// ^^^^^^^^^ definition [..] std/enable_if#
14+
15+
template <class T>
16+
// ^ definition local 2
17+
struct enable_if<true, T> {
18+
// ^^^^^^^^^ definition [..] std/enable_if#
19+
// ^ reference local 2
20+
using type = T;
21+
// ^^^^ definition [..] std/enable_if#type#
22+
// ^ reference local 2
23+
};
24+
25+
template <class T>
26+
// ^ definition local 3
27+
struct is_integral {
28+
// ^^^^^^^^^^^ definition [..] std/is_integral#
29+
static constexpr bool value = false;
30+
// ^^^^^ definition [..] std/is_integral#value.
31+
};
32+
33+
template <>
34+
struct is_integral<int> {
35+
// ^^^^^^^^^^^ reference [..] std/is_integral#
36+
// ^^^^^^^^^^^ definition [..] std/is_integral#
37+
static constexpr bool value = true;
38+
// ^^^^^ definition [..] std/is_integral#value.
39+
};
40+
41+
template <class T>
42+
// ^ definition local 4
43+
struct is_enum {
44+
// ^^^^^^^ definition [..] std/is_enum#
45+
static constexpr bool value = false;
46+
// ^^^^^ definition [..] std/is_enum#value.
47+
};
48+
} // namespace std
49+
50+
// Issue 1: Member function template with enable_if in a non-template class
51+
class Widget {
52+
// ^^^^^^ definition [..] Widget#
53+
public:
54+
template <typename T,
55+
// ^ definition local 5
56+
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
57+
// ^^^ reference [..] std/
58+
// ^^^^^^^^^ reference [..] std/enable_if#
59+
void process(T value) {
60+
// ^^^^^^^ definition [..] Widget#process(9b289cee16747614).
61+
// ^ reference local 5
62+
// ^^^^^ definition local 6
63+
(void)value;
64+
// ^^^^^ reference local 6
65+
}
66+
};
67+
68+
// Issue 2: Free function template with enable_if
69+
template <typename T,
70+
// ^ definition local 7
71+
typename std::enable_if<!std::is_enum<T>::value, bool>::type = false>
72+
// ^^^ reference [..] std/
73+
// ^^^^^^^^^ reference [..] std/enable_if#
74+
T convert(int x) {
75+
//^ reference local 7
76+
// ^^^^^^^ definition [..] convert(767fea59dce4185d).
77+
// ^ definition local 8
78+
return static_cast<T>(x);
79+
// ^ reference local 7
80+
// ^ reference local 8
81+
}
82+
83+
// Issue 3: Member call through a pointer wrapper (simplified unique_ptr)
84+
template <typename T>
85+
// ^ definition local 9
86+
class Ptr {
87+
// ^^^ definition [..] Ptr#
88+
T *p;
89+
// ^ reference local 9
90+
// ^ definition [..] Ptr#p.
91+
92+
public:
93+
Ptr(T *ptr) : p(ptr) {}
94+
// ^^^ definition [..] Ptr#`Ptr<T>`(ebd0a1552f8ce24f).
95+
// ^ reference local 9
96+
// ^^^ definition local 10
97+
// ^ reference [..] Ptr#p.
98+
// ^^^ reference local 10
99+
T *operator->() const { return p; }
100+
// ^ reference local 9
101+
// ^^^^^^^^ definition [..] Ptr#`operator->`(5a2a78a048fb49a8).
102+
// ^ reference [..] Ptr#p.
103+
};
104+
105+
class ThreadLoop {
106+
// ^^^^^^^^^^ definition [..] ThreadLoop#
107+
public:
108+
bool isHealthy() const { return true; }
109+
// ^^^^^^^^^ definition [..] ThreadLoop#isHealthy(50ce9a9e25b4a850).
110+
};
111+
112+
class ThreadPool {
113+
// ^^^^^^^^^^ definition [..] ThreadPool#
114+
Ptr<ThreadLoop> mThread;
115+
// ^^^ reference [..] Ptr#
116+
// ^^^^^^^^^^ reference [..] ThreadLoop#
117+
// ^^^^^^^ definition [..] ThreadPool#mThread.
118+
119+
public:
120+
ThreadPool() : mThread(new ThreadLoop()) {}
121+
// ^^^^^^^^^^ definition [..] ThreadPool#ThreadPool(49f6e7a06ebc5aa8).
122+
// ^^^^^^^ reference [..] ThreadPool#mThread.
123+
// ^^^^^^^ reference [..] Ptr#Ptr(ebd0a1552f8ce24f).
124+
// ^^^^^^^^^^ reference [..] ThreadLoop#
125+
126+
bool checkHealth() const {
127+
// ^^^^^^^^^^^ definition [..] ThreadPool#checkHealth(50ce9a9e25b4a850).
128+
return mThread->isHealthy();
129+
// ^^^^^^^ reference [..] ThreadPool#mThread.
130+
// ^^ reference [..] Ptr#`operator->`(5a2a78a048fb49a8).
131+
// ^^^^^^^^^ reference [..] ThreadLoop#isHealthy(50ce9a9e25b4a850).
132+
}
133+
};
134+
135+
void test() {
136+
// ^^^^ definition [..] test(49f6e7a06ebc5aa8).
137+
Widget w;
138+
// ^^^^^^ reference [..] Widget#
139+
// ^ definition local 11
140+
w.process(42);
141+
// ^ reference local 11
142+
// ^^^^^^^ reference [..] Widget#process(9b289cee16747614).
143+
w.process<int>(100);
144+
// ^ reference local 11
145+
// ^^^^^^^ reference [..] Widget#process(9b289cee16747614).
146+
147+
auto val = convert<int>(5);
148+
// ^^^ definition local 12
149+
// ^^^^^^^ reference [..] convert(767fea59dce4185d).
150+
(void)val;
151+
// ^^^ reference local 12
152+
153+
ThreadPool pool;
154+
// ^^^^^^^^^^ reference [..] ThreadPool#
155+
// ^^^^ definition local 13
156+
// ^^^^ reference [..] ThreadPool#ThreadPool(49f6e7a06ebc5aa8).
157+
(void)pool.checkHealth();
158+
// ^^^^ reference local 13
159+
// ^^^^^^^^^^^ reference [..] ThreadPool#checkHealth(50ce9a9e25b4a850).
160+
}

test/index/types/types.snapshot.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@
343343
};
344344
return ignore_first("", L{});
345345
// ^^^^^^^^^^^^ reference local 3
346-
// ^ reference [..] trailing_return_type(693bfa61ed1914d5).$anonymous_type_4#`operator()`(dc97d1a1ce4cdab3).
346+
// ^ reference [..] trailing_return_type(693bfa61ed1914d5).$anonymous_type_4#`operator()`(b691caff6c7f530).
347347
// ^ reference [..] L#
348348
}
349349

0 commit comments

Comments
 (0)