Skip to content

Commit

Permalink
[lldb] Remove template parameters from FunctionTemplateDecl names
Browse files Browse the repository at this point in the history
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 9e2715a)
  • Loading branch information
shafik committed Mar 18, 2020
1 parent 9fdd051 commit 6cdbe04
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 16 deletions.
28 changes: 20 additions & 8 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -1169,27 +1171,37 @@ 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;
ParseTemplateParameterInfos(die, template_param_infos);
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);
Expand Down
40 changes: 33 additions & 7 deletions lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py
Expand Up @@ -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: <user expression 0>: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):
Expand Down
62 changes: 61 additions & 1 deletion lldb/test/API/lang/cpp/template-function/main.cpp
Expand Up @@ -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 <typename T> int f(T) { return 4; }

template <typename T> 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 <class T> int h(T x) { return x; }
int h(double d) { return 5; }

template <class... Us> 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 <typename T> bool operator<(const T &, const T &) { return true; }

template <typename T> bool operator>(const T &, const T &) { return true; }

template <typename T> bool operator<<(const T &, const T &) { return true; }

template <typename T> bool operator>>(const T &, const T &) { return true; }

template <typename T> 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
}

0 comments on commit 6cdbe04

Please sign in to comment.