Index: packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/Makefile =================================================================== --- packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/Makefile +++ packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules Index: packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/TestCallOverloadedCFunction.py =================================================================== --- packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/TestCallOverloadedCFunction.py +++ packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/TestCallOverloadedCFunction.py @@ -0,0 +1,50 @@ +""" +Test calling user defined C functions using expression evaluation. +This test checks that expression evaluation works correctly for +functions defined with __attribute__((overloadable)). + +Ticket: https://llvm.org/bugs/show_bug.cgi?id=26694 +""" + +from __future__ import print_function + +import lldb + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestExprCallOverloadedCFunction(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + # Find the line number to break for main.c. + self.line = line_number('main.c', + '// break here') + + @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24489: Name lookup not working correctly on Windows") + @expectedFailureAll(compiler='gcc') # attribute overloadable only supported by clang + def test(self): + """Test calling overloaded C functions.""" + self.build() + + # Set breakpoint in main and run exe + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + lldbutil.run_break_set_by_file_and_line( + self, + "main.c", + self.line, + num_expected_locations=-1, + loc_exact=True + ) + + self.runCmd("run", RUN_SUCCEEDED) + + # Test floating point type overload. + self.expect("expr get_arg_type(0.5f)", substrs=["$0 = 'F'"]) + + # Test integer type overload + self.expect("expr get_arg_type(8)", substrs=["$1 = 'I'"]) Index: packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/main.c =================================================================== --- packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/main.c +++ packages/Python/lldbsuite/test/expression_command/call-overloaded-c-fuction/main.c @@ -0,0 +1,17 @@ +char __attribute__((overloadable)) get_arg_type(float x) +{ + return 'F'; // 'F' for float +} + +char __attribute__((overloadable)) get_arg_type(int x) +{ + return 'I'; // 'I' for int +} + +int main() +{ + char float_result = get_arg_type(0.1f); + char int_result = get_arg_type(2); + + return 0; // break here +} Index: source/Expression/IRExecutionUnit.cpp =================================================================== --- source/Expression/IRExecutionUnit.cpp +++ source/Expression/IRExecutionUnit.cpp @@ -743,24 +743,10 @@ CPP_specs.push_back(SearchSpec(demangled, lldb::eFunctionNameTypeFull)); } - } - // Maybe we're looking for a const symbol but the debug info told us it was const... - if (!strncmp(name.GetCString(), "_ZN", 3) && - strncmp(name.GetCString(), "_ZNK", 4)) - { - std::string fixed_scratch("_ZNK"); - fixed_scratch.append(name.GetCString() + 3); - CPP_specs.push_back(ConstString(fixed_scratch.c_str())); - } - - // Maybe we're looking for a static symbol but we thought it was global... - if (!strncmp(name.GetCString(), "_Z", 2) && - strncmp(name.GetCString(), "_ZL", 3)) - { - std::string fixed_scratch("_ZL"); - fixed_scratch.append(name.GetCString() + 2); - CPP_specs.push_back(ConstString(fixed_scratch.c_str())); + std::vector alternates; + CPlusPlusLanguage::FindAlternateFunctionMangling(name, alternates); + CPP_specs.insert(CPP_specs.end(), alternates.begin(), alternates.end()); } } Index: source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp =================================================================== --- source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -2045,7 +2045,8 @@ Type *function_type = function->GetType(); const lldb::LanguageType comp_unit_language = function->GetCompileUnit()->GetLanguage(); - const bool extern_c = Language::LanguageIsC(comp_unit_language) || + const bool extern_c = (Language::LanguageIsC(comp_unit_language) && + !CPlusPlusLanguage::IsCPPMangledName(function->GetMangled().GetMangledName().AsCString())) || (Language::LanguageIsObjC(comp_unit_language) && !Language::LanguageIsCPlusPlus(comp_unit_language)); Index: source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h =================================================================== --- source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -174,6 +174,11 @@ static uint32_t FindEquivalentNames(ConstString type_name, std::vector& equivalents); + // Given a mangled function name, calculates some alternative manglings + // since the compiler mangling may not line up with the symbol we are expecting + static uint32_t + FindAlternateFunctionMangling(ConstString mangled_name, std::vector& alternates); + //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ Index: source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp =================================================================== --- source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -482,6 +482,85 @@ return count; } +// Given a mangled function, replaces all the function parameters of mangled type +// 'search', with parameters of type 'replace'. +static ConstString +SubstituteMangledParameters(const llvm::StringRef& mangled, const llvm::StringRef& search, const llvm::StringRef& replace) +{ + // Find length of function name, already encoded in mangled symbol + static const llvm::StringRef numeric_digits("0123456789"); + const size_t encoded_length_start = mangled.find_first_of(numeric_digits); + if (encoded_length_start == llvm::StringRef::npos) + return ConstString(); + + const size_t encoded_length_end = mangled.find_first_not_of(numeric_digits, encoded_length_start); + if (encoded_length_end == llvm::StringRef::npos || encoded_length_end <= encoded_length_start) + return ConstString(); + + // Parse encoded length into name_length + int name_length; + const llvm::StringRef encoded_length_str = mangled.substr(encoded_length_start, encoded_length_end - encoded_length_start); + if (encoded_length_str.getAsInteger(10, name_length) || name_length == 0) + return ConstString(); + + // Position in 'mangled' string of first function parameter + size_t param_pos = encoded_length_end + name_length; + + // Iterate over all matching parameters, replacing them with string 'replace' + std::string modified_str = mangled.str(); + while ((param_pos = modified_str.find(search, param_pos)) != std::string::npos) + { + modified_str.replace(param_pos, search.size(), replace); + param_pos += replace.size(); + } + + return ConstString(modified_str); +} + +uint32_t +CPlusPlusLanguage::FindAlternateFunctionMangling(ConstString mangled_name, std::vector& alternates) +{ + const uint32_t start_size = alternates.size(); + + // Maybe we're looking for a const symbol but the debug info told us it was const... + if (!strncmp(mangled_name.GetCString(), "_ZN", 3) && + strncmp(mangled_name.GetCString(), "_ZNK", 4)) + { + std::string fixed_scratch("_ZNK"); + fixed_scratch.append(mangled_name.GetCString() + 3); + alternates.push_back(ConstString(fixed_scratch.c_str())); + } + + // Maybe we're looking for a static symbol but we thought it was global... + if (!strncmp(mangled_name.GetCString(), "_Z", 2) && + strncmp(mangled_name.GetCString(), "_ZL", 3)) + { + std::string fixed_scratch("_ZL"); + fixed_scratch.append(mangled_name.GetCString() + 2); + alternates.push_back(ConstString(fixed_scratch.c_str())); + } + + // Char is implementation defined as either signed or unsigned. + // As a result a char parameter has 3 possible manglings: 'c'-char, 'a'-signed char, 'h'-unsigned char. + // If we're looking for symbols with a signed char parameter, + // try finding matches which have the general case 'c'. + ConstString char_fixup = SubstituteMangledParameters(mangled_name.GetStringRef(), llvm::StringRef("a"), llvm::StringRef("c")); + if (!char_fixup.IsEmpty()) + alternates.push_back(char_fixup); + + // long long parameter mangling 'x', may actually just be a long 'l' argument + ConstString long_fixup = SubstituteMangledParameters(mangled_name.GetStringRef(), llvm::StringRef("x"), llvm::StringRef("l")); + if (!long_fixup.IsEmpty()) + alternates.push_back(long_fixup); + + // unsigned long long parameter mangling 'y', may actually just be unsigned long 'm' argument + ConstString ulong_fixup = SubstituteMangledParameters(mangled_name.AsCString(), llvm::StringRef("y"), llvm::StringRef("m")); + if (!ulong_fixup.IsEmpty()) + alternates.push_back(ulong_fixup); + + return alternates.size() - start_size; +} + static void LoadLibCxxFormatters (lldb::TypeCategoryImplSP cpp_category_sp) {