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