From 265df39a80dae1cc0eb6080b7e6bbf863512b7f7 Mon Sep 17 00:00:00 2001 From: Frederic Riss Date: Wed, 24 Apr 2019 21:04:23 +0000 Subject: Fix infinite recursion when calling C++ template functions Summary: When we encounter a templated function in the debug information, we were creating an AST that looked like this: FunctionTemplateDecl 0x12980ab90 <> foo |-TemplateTypeParmDecl 0x12980aad0 <> class depth 0 index 0 T |-FunctionDecl 0x12980aa30 <> foo 'int (int)' extern | |-TemplateArgument type 'int' | `-ParmVarDecl 0x12980a998 <> t1 'int' `-FunctionDecl 0x12980aa30 <> foo 'int (int)' extern |-TemplateArgument type 'int' `-ParmVarDecl 0x12980a998 <> t1 'int' Note that the FunctionTemplateDecl has 2 children which are identical (as in have the same address). This is not what Clang is doing: FunctionTemplateDecl 0x7f89d206c6f8 line:2:5 foo |-TemplateTypeParmDecl 0x7f89d206c4a8 col:19 referenced typename depth 0 index 0 T |-FunctionDecl 0x7f89d206c660 line:2:5 foo 'int (T)' | `-ParmVarDecl 0x7f89d206c570 col:11 t1 'T' `-FunctionDecl 0x7f89d206cb60 line:2:5 used foo 'int (int)' |-TemplateArgument type 'int' `-ParmVarDecl 0x7f89d206ca68 col:11 t1 'int':'int' The 2 chidlren are different and actually repesent different things: the first one is the unspecialized version and the second one is specialized. (Just looking at the names shows another major difference which is that we create the parent with a name of "foo" when it should be just "foo".) The fact that we have those 2 identical children confuses the ClangImporter and generates an infinite recursion (reported in https://llvm.org/pr41473). We cannot create the unspecialized version as the debug information doesn't contain a mapping from the template parameters to their use in the prototype. This patch just creates 2 different FunctionDecls for those 2 children of the FunctionTemplateDecl. This avoids the infinite recursion and allows us to call functions. As the XFAILs in the added test show, we've still got issues in our handling of templates. I believe they are mostly centered on the fact that we create do not register "foo" as a template, but "foo". This is a bigger change that will need changes to the debug information generation. I believe this change makes sense on its own. Reviewers: shafik, clayborg, jingham Subscribers: aprantl, javed.absar, kristof.beyls, lldb-commits Differential Revision: https://reviews.llvm.org/D61044 llvm-svn: 359140 --- .../cpp/template-function/TestTemplateFunctions.py | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 lldb/packages/Python/lldbsuite/test/lang/cpp/template-function/TestTemplateFunctions.py (limited to 'lldb/packages/Python/lldbsuite/test/lang/cpp/template-function/TestTemplateFunctions.py') diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/template-function/TestTemplateFunctions.py b/lldb/packages/Python/lldbsuite/test/lang/cpp/template-function/TestTemplateFunctions.py new file mode 100644 index 00000000000..14ebe3b316f --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/lang/cpp/template-function/TestTemplateFunctions.py @@ -0,0 +1,30 @@ +""" +Test that we can call C++ template fucntions. +""" +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TemplateFunctionsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def do_test_template_function(self, add_cast): + self.build() + (_, _, thread, _) = lldbutil.run_to_name_breakpoint(self, "main") + frame = thread.GetSelectedFrame() + expr = "foo(42)" + if add_cast: + expr = "(int)" + expr + expr_result = frame.EvaluateExpression(expr) + self.assertTrue(expr_result.IsValid()) + self.assertEqual(expr_result.GetValue(), "42") + + def test_template_function_with_cast(self): + self.do_test_template_function(True) + + @expectedFailureAll(debug_info=["dwarf", "gmodules"]) + def test_template_function_without_cast(self): + self.do_test_template_function(False) -- cgit v1.2.3