Skip to content

Commit 9e2715a

Browse files
committed
[lldb] Remove template parameters from FunctionTemplateDecl names
Fix to get the AST we generate for function templates closer to what clang generates and expects. We fix which FuntionDecl we are passing to CreateFunctionTemplateSpecializationInfo and we strip template parameters from the name when creating the FunctionDecl and FunctionTemplateDecl. These two fixes together fix asserts and ambiguous lookup issues for several cases which are added to the already existing small function template test. This fixes issues with overloads, overloads and ADL, variadic function templates and templated operator overloads. Differential Revision: https://reviews.llvm.org/D75761
1 parent 585a3cc commit 9e2715a

File tree

3 files changed

+114
-16
lines changed

3 files changed

+114
-16
lines changed

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
#include "lldb/Utility/Log.h"
3636
#include "lldb/Utility/StreamString.h"
3737

38+
#include "llvm/Demangle/Demangle.h"
39+
3840
#include "clang/AST/CXXInheritance.h"
3941
#include "clang/AST/DeclCXX.h"
4042
#include "clang/AST/DeclObjC.h"
@@ -1167,27 +1169,37 @@ TypeSP DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die,
11671169
}
11681170

11691171
if (!function_decl) {
1172+
const char *name = attrs.name.GetCString();
1173+
1174+
// We currently generate function templates with template parameters in
1175+
// their name. In order to get closer to the AST that clang generates
1176+
// we want to strip these from the name when creating the AST.
1177+
if (attrs.mangled_name) {
1178+
llvm::ItaniumPartialDemangler D;
1179+
if (!D.partialDemangle(attrs.mangled_name))
1180+
name = D.getFunctionBaseName(nullptr, nullptr);
1181+
}
1182+
11701183
// We just have a function that isn't part of a class
11711184
function_decl = m_ast.CreateFunctionDeclaration(
11721185
ignore_containing_context ? m_ast.GetTranslationUnitDecl()
11731186
: containing_decl_ctx,
1174-
attrs.name.GetCString(), clang_type, attrs.storage,
1175-
attrs.is_inline);
1187+
name, clang_type, attrs.storage, attrs.is_inline);
11761188

11771189
if (has_template_params) {
11781190
TypeSystemClang::TemplateParameterInfos template_param_infos;
11791191
ParseTemplateParameterInfos(die, template_param_infos);
11801192
template_function_decl = m_ast.CreateFunctionDeclaration(
11811193
ignore_containing_context ? m_ast.GetTranslationUnitDecl()
11821194
: containing_decl_ctx,
1183-
attrs.name.GetCString(), clang_type, attrs.storage,
1184-
attrs.is_inline);
1195+
name, clang_type, attrs.storage, attrs.is_inline);
1196+
11851197
clang::FunctionTemplateDecl *func_template_decl =
1186-
m_ast.CreateFunctionTemplateDecl(
1187-
containing_decl_ctx, template_function_decl,
1188-
attrs.name.GetCString(), template_param_infos);
1198+
m_ast.CreateFunctionTemplateDecl(containing_decl_ctx,
1199+
template_function_decl, name,
1200+
template_param_infos);
11891201
m_ast.CreateFunctionTemplateSpecializationInfo(
1190-
function_decl, func_template_decl, template_param_infos);
1202+
template_function_decl, func_template_decl, template_param_infos);
11911203
}
11921204

11931205
lldbassert(function_decl);

lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,40 @@ class TemplateFunctionsTestCase(TestBase):
1313

1414
def do_test_template_function(self, add_cast):
1515
self.build()
16-
(_, _, thread, _) = lldbutil.run_to_name_breakpoint(self, "main")
17-
frame = thread.GetSelectedFrame()
18-
expr = "foo(42)"
16+
lldbutil.run_to_source_breakpoint(self, '// break here',
17+
lldb.SBFileSpec("main.cpp", False))
18+
1919
if add_cast:
20-
expr = "(int)" + expr
21-
expr_result = frame.EvaluateExpression(expr)
22-
self.assertTrue(expr_result.IsValid())
23-
self.assertEqual(expr_result.GetValue(), "42")
20+
self.expect_expr("(int) foo(42)", result_type="int", result_value="42")
21+
else:
22+
self.expect("expr b1 <=> b2", error=True, substrs=["warning: <user expression 0>:1:4: '<=>' is a single token in C++20; add a space to avoid a change in behavior"])
23+
24+
self.expect_expr("foo(42)", result_type="int", result_value="42")
25+
26+
# overload with template case
27+
self.expect_expr("h(10)", result_type="int", result_value="10")
28+
29+
# ADL lookup case
30+
self.expect_expr("f(A::C{})", result_type="int", result_value="4")
31+
32+
# ADL lookup but no overload
33+
self.expect_expr("g(A::C{})", result_type="int", result_value="4")
34+
35+
# variadic function cases
36+
self.expect_expr("var(1)", result_type="int", result_value="10")
37+
self.expect_expr("var(1, 2)", result_type="int", result_value="10")
38+
39+
# Overloaded templated operator case
40+
self.expect_expr("b1 > b2", result_type="bool", result_value="true")
41+
self.expect_expr("b1 >> b2", result_type="bool", result_value="true")
42+
self.expect_expr("b1 << b2", result_type="bool", result_value="true")
43+
self.expect_expr("b1 == b2", result_type="bool", result_value="true")
44+
45+
# Overloaded operator case
46+
self.expect_expr("d1 > d2", result_type="bool", result_value="true")
47+
self.expect_expr("d1 >> d2", result_type="bool", result_value="true")
48+
self.expect_expr("d1 << d2", result_type="bool", result_value="true")
49+
self.expect_expr("d1 == d2", result_type="bool", result_value="true")
2450

2551
@skipIfWindows
2652
def test_template_function_with_cast(self):

lldb/test/API/lang/cpp/template-function/main.cpp

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,66 @@ int foo(T t1) {
33
return int(t1);
44
}
55

6+
// Some cases to cover ADL, we have two cases:
7+
//
8+
// - f which will have a overload in the global namespace if unqualified lookup
9+
// find f(int) and f(T) is found via ADL.
10+
//
11+
// - g which does not have an overload in the global namespace.
12+
namespace A {
13+
struct C {};
14+
15+
template <typename T> int f(T) { return 4; }
16+
17+
template <typename T> int g(T) { return 4; }
18+
} // namespace A
19+
20+
// Meant to overload A::f(T) which may be found via ADL
21+
int f(int) { return 1; }
22+
23+
// Regular overloaded functions case h(T) and h(double).
24+
template <class T> int h(T x) { return x; }
25+
int h(double d) { return 5; }
26+
27+
template <class... Us> int var(Us... pargs) { return 10; }
28+
29+
// Having the templated overloaded operators in a namespace effects the
30+
// mangled name generated in the IR e.g. _ZltRK1BS1_ Vs _ZN1AltERKNS_1BES2_
31+
// One will be in the symbol table but the other won't. This results in a
32+
// different code path that will result in CPlusPlusNameParser being used.
33+
// This allows us to cover that code as well.
34+
namespace A {
35+
template <typename T> bool operator<(const T &, const T &) { return true; }
36+
37+
template <typename T> bool operator>(const T &, const T &) { return true; }
38+
39+
template <typename T> bool operator<<(const T &, const T &) { return true; }
40+
41+
template <typename T> bool operator>>(const T &, const T &) { return true; }
42+
43+
template <typename T> bool operator==(const T &, const T &) { return true; }
44+
45+
struct B {};
46+
} // namespace A
47+
48+
struct D {};
49+
50+
// Make sure we cover more straight forward cases as well.
51+
bool operator<(const D &, const D &) { return true; }
52+
bool operator>(const D &, const D &) { return true; }
53+
bool operator>>(const D &, const D &) { return true; }
54+
bool operator<<(const D &, const D &) { return true; }
55+
bool operator==(const D &, const D &) { return true; }
56+
657
int main() {
7-
return foo(42);
58+
A::B b1;
59+
A::B b2;
60+
D d1;
61+
D d2;
62+
63+
bool result_b = b1 < b2 && b1 << b2 && b1 == b2 && b1 > b2 && b1 >> b2;
64+
bool result_c = d1 < d2 && d1 << d2 && d1 == d2 && d1 > d2 && d1 >> d2;
65+
66+
return foo(42) + result_b + result_c + f(A::C{}) + g(A::C{}) + h(10) + h(1.) +
67+
var(1) + var(1, 2); // break here
868
}

0 commit comments

Comments
 (0)