shafik created this revision.
shafik added reviewers: teemperor, aprantl.

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.

The recent fix D75545 <https://reviews.llvm.org/D75545> came out of this patch 
and there will be at least one more patch to deal with and edge case with 
templated `operator<` when it is in a namespace. We do C++ name parsing in 
`CPlusPlusNameParser` and we don’t deal with cases like `A::operator<<A::B>` 
properly.

Currently the fix applies mostly to dsym cases and I will be working on a fix 
that will apply to all cases but it may be a separate PR.


https://reviews.llvm.org/D75761

Files:
  lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
  lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py
  lldb/test/API/lang/cpp/template-function/main.cpp

Index: lldb/test/API/lang/cpp/template-function/main.cpp
===================================================================
--- lldb/test/API/lang/cpp/template-function/main.cpp
+++ lldb/test/API/lang/cpp/template-function/main.cpp
@@ -3,6 +3,67 @@
         return int(t1);
 }
 
+// Some cases to cover ADL
+namespace A {
+struct C {};
+
+template <typename T> int f(T) { return 4; }
+
+template <typename T> int g(T) { return 4; }
+} // namespace A
+
+int f(int) { return 1; }
+
+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 C {};
+
+// Make sure we cover more straight forward cases as well.
+bool operator<(const C &, const C &) { return true; }
+bool operator>(const C &, const C &) { return true; }
+bool operator>>(const C &, const C &) { return true; }
+bool operator<<(const C &, const C &) { return true; }
+bool operator==(const C &, const C &) { return true; }
+
 int main() {
-        return foo(42);
+  A::B b1;
+  A::B b2;
+  C c1;
+  C c2;
+
+  bool result_b = b1 < b2 && b1 << b2 && b1 == b2 && b1 > b2 && b1 >> b2;
+  bool result_c = c1 < c2 && c1 << c2 && c1 == c2 && c1 > c2 && c1 >> c2;
+
+  return foo(42) + result_b + result_c +
+         // ADL lookup case,
+         f(A::C{}) +
+         // ADL lookup but no overload
+         g(A::C{}) +
+         // overload with template
+         h(10) + h(1.) +
+         // variadic function
+         var(1) + var(1, 2); // break here
 }
Index: lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py
===================================================================
--- lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py
+++ lldb/test/API/lang/cpp/template-function/TestTemplateFunctions.py
@@ -13,14 +13,54 @@
 
     def do_test_template_function(self, add_cast):
         self.build()
-        (_, _, thread, _) = lldbutil.run_to_name_breakpoint(self, "main")
-        frame = thread.GetSelectedFrame()
-        expr = "foo(42)"
+        (self.target, self.process, _, bkpt) = 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)",
+                substrs=['(int)', '= 42'])
+        else:
+          self.expect("expr foo(42)",
+                substrs=['(int)', '= 42'])
+
+          self.expect("expr h(10)",
+                substrs=['(int)', '= 10'])
+
+          self.expect("expr f(A::C{})",
+           substrs=['(int)', '= 4'])
+
+          self.expect("expr g(A::C{})",
+           substrs=['(int)', '= 4'])
+
+          self.expect("expr var(1)",
+           substrs=['(int)', '= 10'])
+
+          self.expect("expr var(1,2)",
+           substrs=['(int)', '= 10'])
+
+          self.expect("expr b1 > b2",
+           substrs=['(bool)', '= true'])
+
+          self.expect("expr b1 >> b2",
+           substrs=['(bool)', '= true'])
+
+          self.expect("expr b1 << b2",
+           substrs=['(bool)', '= true'])
+
+          self.expect("expr b1 == b2",
+           substrs=['(bool)', '= true'])
+
+          self.expect("expr c1 > c2",
+           substrs=['(bool)', '= true'])
+
+          self.expect("expr c1 >> c2",
+           substrs=['(bool)', '= true'])
+
+          self.expect("expr c1 << c2",
+           substrs=['(bool)', '= true'])
+
+          self.expect("expr c1 == c2",
+           substrs=['(bool)', '= true'])
 
     @skipIfWindows
     def test_template_function_with_cast(self):
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -818,6 +818,38 @@
   return type_sp;
 }
 
+static clang::Optional<llvm::StringRef>
+StripTemplateParameters(llvm::StringRef Name) {
+  // We are looking for template parameters to strip from Name. e.g.
+  //
+  //  operator<<B>
+  //
+  // We look for > at the end but if it does not contain any < then we
+  // have something like operator>>. We check for the operator<=> case.
+  if (!Name.endswith(">") || Name.count("<") == 0 || Name.endswith("<=>"))
+    return {};
+
+  // How many < until we have the start of the template parameters.
+  size_t NumLeftAnglesToSkip = 1;
+
+  // If we have operator<=> then we need to skip its < as well.
+  NumLeftAnglesToSkip += Name.count("<=>");
+
+  size_t RightAngleCount = Name.count('>');
+  size_t LeftAngleCount = Name.count('<');
+
+  // If we have more < than > we have operator< or operator<<
+  // we to account for their < as well.
+  if (LeftAngleCount > RightAngleCount)
+    NumLeftAnglesToSkip += LeftAngleCount - RightAngleCount;
+
+  size_t StartOfTemplate = 0;
+  while (NumLeftAnglesToSkip--)
+    StartOfTemplate = Name.find('<', StartOfTemplate) + 1;
+
+  return Name.substr(0, StartOfTemplate - 1);
+}
+
 TypeSP DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die,
                            ParsedDWARFTypeAttributes &attrs) {
   Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION |
@@ -1167,11 +1199,20 @@
       }
 
       if (!function_decl) {
+        llvm::StringRef name_without_template_params_ref =
+            attrs.name.GetStringRef();
+        if (clang::Optional<llvm::StringRef> stripped_name =
+                StripTemplateParameters(attrs.name.GetStringRef()))
+          name_without_template_params_ref = *stripped_name;
+
+        std::string name_without_template_params =
+            name_without_template_params_ref.str();
+
         // 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,
+            name_without_template_params.c_str(), clang_type, attrs.storage,
             attrs.is_inline);
 
         if (has_template_params) {
@@ -1180,14 +1221,15 @@
           template_function_decl = m_ast.CreateFunctionDeclaration(
               ignore_containing_context ? m_ast.GetTranslationUnitDecl()
                                         : containing_decl_ctx,
-              attrs.name.GetCString(), clang_type, attrs.storage,
+              name_without_template_params.c_str(), 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);
+                  name_without_template_params.c_str(), 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);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to