From 6cdbe04d7d6849343e682c7bc8612c7a3d8f9699 Mon Sep 17 00:00:00 2001 From: shafik Date: Tue, 17 Mar 2020 10:55:53 -0700 Subject: [PATCH] [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 (cherry picked from commit 9e2715aaacaa3087933b5575d23563751b54843b) --- .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 28 ++++++--- .../TestTemplateFunctions.py | 40 +++++++++--- .../API/lang/cpp/template-function/main.cpp | 62 ++++++++++++++++++- 3 files changed, 114 insertions(+), 16 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index c07ac9bc252ba..e844f331acdc5 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -35,6 +35,8 @@ #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" +#include "llvm/Demangle/Demangle.h" + #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -1169,12 +1171,22 @@ TypeSP DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, } if (!function_decl) { + const char *name = attrs.name.GetCString(); + + // We currently generate function templates with template parameters in + // their name. In order to get closer to the AST that clang generates + // we want to strip these from the name when creating the AST. + if (attrs.mangled_name) { + llvm::ItaniumPartialDemangler D; + if (!D.partialDemangle(attrs.mangled_name)) + name = D.getFunctionBaseName(nullptr, nullptr); + } + // We just have a function that isn't part of a class function_decl = m_ast.CreateFunctionDeclaration( ignore_containing_context ? m_ast.GetTranslationUnitDecl() : containing_decl_ctx, - attrs.name.GetCString(), clang_type, attrs.storage, - attrs.is_inline); + name, clang_type, attrs.storage, attrs.is_inline); if (has_template_params) { TypeSystemClang::TemplateParameterInfos template_param_infos; @@ -1182,14 +1194,14 @@ TypeSP DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, template_function_decl = m_ast.CreateFunctionDeclaration( ignore_containing_context ? m_ast.GetTranslationUnitDecl() : containing_decl_ctx, - attrs.name.GetCString(), clang_type, attrs.storage, - attrs.is_inline); + name, clang_type, attrs.storage, attrs.is_inline); + clang::FunctionTemplateDecl *func_template_decl = - m_ast.CreateFunctionTemplateDecl( - containing_decl_ctx, template_function_decl, - attrs.name.GetCString(), template_param_infos); + m_ast.CreateFunctionTemplateDecl(containing_decl_ctx, + template_function_decl, name, + template_param_infos); m_ast.CreateFunctionTemplateSpecializationInfo( - function_decl, func_template_decl, template_param_infos); + template_function_decl, func_template_decl, template_param_infos); } lldbassert(function_decl); diff --git a/lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py b/lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py index 6c9a0ac35f4fd..0e74185546e84 100644 --- a/lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py +++ b/lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py @@ -13,14 +13,40 @@ class TemplateFunctionsTestCase(TestBase): def do_test_template_function(self, add_cast): self.build() - (_, _, thread, _) = lldbutil.run_to_name_breakpoint(self, "main") - frame = thread.GetSelectedFrame() - expr = "foo(42)" + lldbutil.run_to_source_breakpoint(self, '// break here', + lldb.SBFileSpec("main.cpp", False)) + if add_cast: - expr = "(int)" + expr - expr_result = frame.EvaluateExpression(expr) - self.assertTrue(expr_result.IsValid()) - self.assertEqual(expr_result.GetValue(), "42") + self.expect_expr("(int) foo(42)", result_type="int", result_value="42") + else: + self.expect("expr b1 <=> b2", error=True, substrs=["warning: :1:4: '<=>' is a single token in C++20; add a space to avoid a change in behavior"]) + + self.expect_expr("foo(42)", result_type="int", result_value="42") + + # overload with template case + self.expect_expr("h(10)", result_type="int", result_value="10") + + # ADL lookup case + self.expect_expr("f(A::C{})", result_type="int", result_value="4") + + # ADL lookup but no overload + self.expect_expr("g(A::C{})", result_type="int", result_value="4") + + # variadic function cases + self.expect_expr("var(1)", result_type="int", result_value="10") + self.expect_expr("var(1, 2)", result_type="int", result_value="10") + + # Overloaded templated operator case + self.expect_expr("b1 > b2", result_type="bool", result_value="true") + self.expect_expr("b1 >> b2", result_type="bool", result_value="true") + self.expect_expr("b1 << b2", result_type="bool", result_value="true") + self.expect_expr("b1 == b2", result_type="bool", result_value="true") + + # Overloaded operator case + self.expect_expr("d1 > d2", result_type="bool", result_value="true") + self.expect_expr("d1 >> d2", result_type="bool", result_value="true") + self.expect_expr("d1 << d2", result_type="bool", result_value="true") + self.expect_expr("d1 == d2", result_type="bool", result_value="true") @skipIfWindows def test_template_function_with_cast(self): diff --git a/lldb/test/API/lang/cpp/template-function/main.cpp b/lldb/test/API/lang/cpp/template-function/main.cpp index 035e6450cd3e5..47b2c5c1f6e4b 100644 --- a/lldb/test/API/lang/cpp/template-function/main.cpp +++ b/lldb/test/API/lang/cpp/template-function/main.cpp @@ -11,6 +11,66 @@ int foo(T t1) { return int(t1); } +// Some cases to cover ADL, we have two cases: +// +// - f which will have a overload in the global namespace if unqualified lookup +// find f(int) and f(T) is found via ADL. +// +// - g which does not have an overload in the global namespace. +namespace A { +struct C {}; + +template int f(T) { return 4; } + +template int g(T) { return 4; } +} // namespace A + +// Meant to overload A::f(T) which may be found via ADL +int f(int) { return 1; } + +// Regular overloaded functions case h(T) and h(double). +template int h(T x) { return x; } +int h(double d) { return 5; } + +template int var(Us... pargs) { return 10; } + +// Having the templated overloaded operators in a namespace effects the +// mangled name generated in the IR e.g. _ZltRK1BS1_ Vs _ZN1AltERKNS_1BES2_ +// One will be in the symbol table but the other won't. This results in a +// different code path that will result in CPlusPlusNameParser being used. +// This allows us to cover that code as well. +namespace A { +template bool operator<(const T &, const T &) { return true; } + +template bool operator>(const T &, const T &) { return true; } + +template bool operator<<(const T &, const T &) { return true; } + +template bool operator>>(const T &, const T &) { return true; } + +template bool operator==(const T &, const T &) { return true; } + +struct B {}; +} // namespace A + +struct D {}; + +// Make sure we cover more straight forward cases as well. +bool operator<(const D &, const D &) { return true; } +bool operator>(const D &, const D &) { return true; } +bool operator>>(const D &, const D &) { return true; } +bool operator<<(const D &, const D &) { return true; } +bool operator==(const D &, const D &) { return true; } + int main() { - return foo(42); + A::B b1; + A::B b2; + D d1; + D d2; + + bool result_b = b1 < b2 && b1 << b2 && b1 == b2 && b1 > b2 && b1 >> b2; + bool result_c = d1 < d2 && d1 << d2 && d1 == d2 && d1 > d2 && d1 >> d2; + + return foo(42) + result_b + result_c + f(A::C{}) + g(A::C{}) + h(10) + h(1.) + + var(1) + var(1, 2); // break here }