Index: lldb/packages/Python/lldbsuite/test/lang/cpp/covariant-return-types/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/lang/cpp/covariant-return-types/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules Index: lldb/packages/Python/lldbsuite/test/lang/cpp/covariant-return-types/TestCovariantReturnTypes.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/lang/cpp/covariant-return-types/TestCovariantReturnTypes.py @@ -0,0 +1,40 @@ +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + NO_DEBUG_INFO_TESTCASE = True + + def test(self): + self.build() + lldbutil.run_to_source_breakpoint(self,"// break here", lldb.SBFileSpec("main.cpp")) + + # Test covariant return types for pointers to class that contains the called function. + self.expect_expr("derived.getPtr()", result_type="Derived *") + self.expect_expr("base_ptr_to_derived->getPtr()", result_type="Base *") + self.expect_expr("base.getPtr()", result_type="Base *") + # The same tests with reference types. LLDB drops the reference when it turns the + # result into a SBValue so check for the the underlying type of the result. + self.expect_expr("derived.getRef()", result_type="Derived") + self.expect_expr("base_ptr_to_derived->getRef()", result_type="Base") + self.expect_expr("base.getRef()", result_type="Base") + + # Test covariant return types for pointers to class that does *not* contain the called function. + self.expect_expr("derived.getOtherPtr()", result_type="OtherDerived *") + self.expect_expr("base_ptr_to_derived->getOtherPtr()", result_type="OtherBase *") + self.expect_expr("base.getOtherPtr()", result_type="OtherBase *") + # The same tests with reference types. LLDB drops the reference when it turns the + # result into a SBValue so check for the the underlying type of the result. + self.expect_expr("derived.getOtherRef()", result_type="OtherDerived") + self.expect_expr("base_ptr_to_derived->getOtherRef()", result_type="OtherBase") + self.expect_expr("base.getOtherRef()", result_type="OtherBase") + + # Test that we call the right function and get the right value back. + self.expect_expr("derived.getOtherPtr()->value()", result_summary='"derived"') + self.expect_expr("base_ptr_to_derived->getOtherPtr()->value()", result_summary='"derived"') + self.expect_expr("base.getOtherPtr()->value()", result_summary='"base"') + self.expect_expr("derived.getOtherRef().value()", result_summary='"derived"') + self.expect_expr("base_ptr_to_derived->getOtherRef().value()", result_summary='"derived"') + self.expect_expr("base.getOtherRef().value()", result_summary='"base"') Index: lldb/packages/Python/lldbsuite/test/lang/cpp/covariant-return-types/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/lang/cpp/covariant-return-types/main.cpp @@ -0,0 +1,40 @@ +struct OtherBase { + // Allow checking actual type from the test by giving + // this class and the subclass unique values here. + virtual const char *value() { return "base"; } +}; +struct OtherDerived : public OtherBase { + const char *value() override { return "derived"; } +}; + +// Those have to be globals as they would be completed if they +// are members (which would make this test always pass). +OtherBase other_base; +OtherDerived other_derived; + +struct Base { + // Function with covariant return type that is same class. + virtual Base* getPtr() { return this; } + virtual Base& getRef() { return *this; } + // Function with covariant return type that is a different class. + virtual OtherBase* getOtherPtr() { return &other_base; } + virtual OtherBase& getOtherRef() { return other_base; } +}; + +struct Derived : public Base { + Derived* getPtr() override { return this; } + Derived& getRef() override { return *this; } + OtherDerived* getOtherPtr() override { return &other_derived; } + OtherDerived& getOtherRef() override { return other_derived; } +}; + +int main() { + Derived derived; + Base base; + Base *base_ptr_to_derived = &derived; + (void)base_ptr_to_derived->getPtr(); + (void)base_ptr_to_derived->getRef(); + (void)base_ptr_to_derived->getOtherPtr(); + (void)base_ptr_to_derived->getOtherRef(); + return 0; // break here +} Index: lldb/source/Symbol/ClangASTImporter.cpp =================================================================== --- lldb/source/Symbol/ClangASTImporter.cpp +++ lldb/source/Symbol/ClangASTImporter.cpp @@ -1002,6 +1002,25 @@ } } +/// Takes a CXXMethodDecl and completes the return type if necessary. This +/// is currently only necessary for virtual functions with covariant return +/// types where Clang's CodeGen expects that the underlying records are already +/// completed. +static void MaybeCompleteReturnType(ClangASTImporter &importer, + CXXMethodDecl *to_method) { + if (!to_method->isVirtual()) + return; + QualType return_type = to_method->getReturnType(); + if (!return_type->isPointerType() && !return_type->isReferenceType()) + return; + + clang::RecordDecl *rd = return_type->getPointeeType()->getAsRecordDecl(); + if (!rd) + return; + + importer.CompleteTagDecl(rd); +} + void ClangASTImporter::ASTImporterDelegate::Imported(clang::Decl *from, clang::Decl *to) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); @@ -1157,6 +1176,9 @@ } } } + + if (clang::CXXMethodDecl *to_method = dyn_cast(to)) + MaybeCompleteReturnType(m_master, to_method); } clang::Decl *