[lldb][TypeSystemClang] Honor DW_AT_rvalue_reference when creating C++ FunctionPrototypes

Currently funciton lookup in the expression evaluator
fails to disambiguate member functions the are overloaded
on lvalue/rvalue reference-qualifiers. This happens because
we unconditionally set a `FunctionPrototype`s
`ExtProtoInfo::RefQualifier` to `RQ_None`. We lose
the ref-qualifiers in the synthesized AST and `clang::Sema`
fails to pick a correct overload candidate.

DWARF emits information about a function's ref-qualifiers
in the form of a boolean `DW_AT_rvalue_reference` (for rvalues)
and `DW_AT_reference` (for lvalues).

This patch sets the `FunctionPrototype::ExtProtoInfo::RefQualifier`
based on the DWARF attributes above.

**Testing**

* Added API test

llvm/llvm-project issue #57866

Differential Revision: https://reviews.llvm.org/D134661
This commit is contained in:
Michael Buch 2022-09-26 18:38:37 +02:00
parent a6383bb51c
commit 60eb06be6d
7 changed files with 89 additions and 15 deletions

View File

@ -36,12 +36,12 @@
#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"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Type.h"
#include "llvm/Demangle/Demangle.h"
#include <map>
#include <memory>
@ -412,6 +412,12 @@ ParsedDWARFTypeAttributes::ParsedDWARFTypeAttributes(const DWARFDIE &die) {
case DW_AT_export_symbols:
exports_symbols = form_value.Boolean();
break;
case DW_AT_rvalue_reference:
ref_qual = clang::RQ_RValue;
break;
case DW_AT_reference:
ref_qual = clang::RQ_LValue;
break;
}
}
}
@ -974,9 +980,10 @@ TypeSP DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die,
// clang_type will get the function prototype clang type after this
// call
CompilerType clang_type = m_ast.CreateFunctionType(
return_clang_type, function_param_types.data(),
function_param_types.size(), is_variadic, type_quals, calling_convention);
CompilerType clang_type =
m_ast.CreateFunctionType(return_clang_type, function_param_types.data(),
function_param_types.size(), is_variadic,
type_quals, calling_convention, attrs.ref_qual);
if (attrs.name) {
bool type_handled = false;

View File

@ -10,6 +10,7 @@
#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFASTPARSERCLANG_H
#include "clang/AST/CharUnits.h"
#include "clang/AST/Type.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
@ -303,6 +304,10 @@ struct ParsedDWARFTypeAttributes {
uint32_t bit_stride = 0;
uint32_t byte_stride = 0;
uint32_t encoding = 0;
clang::RefQualifierKind ref_qual =
clang::RQ_None; ///< Indicates ref-qualifier of
///< C++ member function if present.
///< Is RQ_None otherwise.
};
#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFASTPARSERCLANG_H

View File

@ -2168,11 +2168,10 @@ FunctionDecl *TypeSystemClang::CreateFunctionDeclaration(
return func_decl;
}
CompilerType
TypeSystemClang::CreateFunctionType(const CompilerType &result_type,
const CompilerType *args, unsigned num_args,
bool is_variadic, unsigned type_quals,
clang::CallingConv cc) {
CompilerType TypeSystemClang::CreateFunctionType(
const CompilerType &result_type, const CompilerType *args,
unsigned num_args, bool is_variadic, unsigned type_quals,
clang::CallingConv cc, clang::RefQualifierKind ref_qual) {
if (!result_type || !ClangUtil::IsClangType(result_type))
return CompilerType(); // invalid return type
@ -2201,7 +2200,7 @@ TypeSystemClang::CreateFunctionType(const CompilerType &result_type,
proto_info.Variadic = is_variadic;
proto_info.ExceptionSpec = EST_None;
proto_info.TypeQuals = clang::Qualifiers::fromFastMask(type_quals);
proto_info.RefQualifier = RQ_None;
proto_info.RefQualifier = ref_qual;
return GetType(getASTContext().getFunctionType(
ClangUtil::GetQualType(result_type), qual_type_args, proto_info));

View File

@ -23,6 +23,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTFwd.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/SmallVector.h"
@ -398,10 +399,11 @@ public:
llvm::StringRef name, const CompilerType &function_Type,
clang::StorageClass storage, bool is_inline);
CompilerType CreateFunctionType(const CompilerType &result_type,
const CompilerType *args, unsigned num_args,
bool is_variadic, unsigned type_quals,
clang::CallingConv cc = clang::CC_C);
CompilerType
CreateFunctionType(const CompilerType &result_type, const CompilerType *args,
unsigned num_args, bool is_variadic, unsigned type_quals,
clang::CallingConv cc = clang::CC_C,
clang::RefQualifierKind ref_qual = clang::RQ_None);
clang::ParmVarDecl *
CreateParameterDeclaration(clang::DeclContext *decl_ctx,

View File

@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp
include Makefile.rules

View File

@ -0,0 +1,39 @@
"""
Tests that C++ expression evaluation can
disambiguate between rvalue and lvalue
reference-qualified functions.
"""
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class TestCase(TestBase):
def test(self):
self.build()
lldbutil.run_to_source_breakpoint(self, "Break here", lldb.SBFileSpec("main.cpp"))
# const lvalue
self.expect_expr("const_foo.func()", result_type="uint32_t", result_value="0")
# const rvalue
self.expect_expr("static_cast<Foo const&&>(Foo{}).func()",
result_type="int64_t", result_value="1")
# non-const lvalue
self.expect_expr("foo.func()", result_type="uint32_t", result_value="2")
# non-const rvalue
self.expect_expr("Foo{}.func()", result_type="int64_t", result_value="3")
self.filecheck("target modules dump ast", __file__)
# CHECK: |-CXXMethodDecl {{.*}} func 'uint32_t () const &'
# CHECK-NEXT: | `-AsmLabelAttr {{.*}}
# CHECK-NEXT: |-CXXMethodDecl {{.*}} func 'int64_t () const &&'
# CHECK-NEXT: | `-AsmLabelAttr {{.*}}
# CHECK-NEXT: |-CXXMethodDecl {{.*}} func 'uint32_t () &'
# CHECK-NEXT: | `-AsmLabelAttr {{.*}}
# CHECK-NEXT: `-CXXMethodDecl {{.*}} func 'int64_t () &&'
# CHECK-NEXT: `-AsmLabelAttr {{.*}}

View File

@ -0,0 +1,19 @@
#include <cstdint>
#include <cstdio>
struct Foo {
uint32_t func() const & { return 0; }
int64_t func() const && { return 1; }
uint32_t func() & { return 2; }
int64_t func() && { return 3; }
};
int main() {
Foo foo;
const Foo const_foo;
auto res = foo.func() + const_foo.func() + Foo{}.func() +
static_cast<Foo const &&>(Foo{}).func();
std::puts("Break here");
return res;
}