Index: clang/lib/Analysis/FlowSensitive/RecordOps.cpp =================================================================== --- clang/lib/Analysis/FlowSensitive/RecordOps.cpp +++ clang/lib/Analysis/FlowSensitive/RecordOps.cpp @@ -17,29 +17,35 @@ void clang::dataflow::copyRecord(AggregateStorageLocation &Src, AggregateStorageLocation &Dst, Environment &Env) { + auto SrcType = Src.getType().getCanonicalType().getUnqualifiedType(); + auto DstType = Dst.getType().getCanonicalType().getUnqualifiedType(); + + auto SrcDecl = SrcType->getAsCXXRecordDecl(); + auto DstDecl = DstType->getAsCXXRecordDecl(); + + bool compatibleTypes = + SrcType == DstType || + (SrcDecl && DstDecl && SrcDecl->isDerivedFrom(DstDecl)); + LLVM_DEBUG({ - if (Dst.getType().getCanonicalType().getUnqualifiedType() != - Src.getType().getCanonicalType().getUnqualifiedType()) { + if (!compatibleTypes) { llvm::dbgs() << "Source type " << Src.getType() << "\n"; llvm::dbgs() << "Destination type " << Dst.getType() << "\n"; } }); - assert(Dst.getType().getCanonicalType().getUnqualifiedType() == - Src.getType().getCanonicalType().getUnqualifiedType()); - - for (auto [Field, SrcFieldLoc] : Src.children()) { - assert(SrcFieldLoc != nullptr); + assert(compatibleTypes); - StorageLocation &DstFieldLoc = Dst.getChild(*Field); + for (auto [Field, DstFieldLoc] : Dst.children()) { + StorageLocation &SrcFieldLoc = Src.getChild(*Field); if (Field->getType()->isRecordType()) { - copyRecord(cast(*SrcFieldLoc), - cast(DstFieldLoc), Env); + copyRecord(cast(SrcFieldLoc), + cast(*DstFieldLoc), Env); } else { - if (Value *Val = Env.getValue(*SrcFieldLoc)) - Env.setValue(DstFieldLoc, *Val); + if (Value *Val = Env.getValue(SrcFieldLoc)) + Env.setValue(*DstFieldLoc, *Val); else - Env.clearValue(DstFieldLoc); + Env.clearValue(*DstFieldLoc); } } Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp =================================================================== --- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -2232,6 +2232,51 @@ }); } +TEST(TransferTest, UncheckedDerivedToBaseCopyConstructor) { + using ast_matchers::cxxConstructorDecl; + using ast_matchers::hasName; + using ast_matchers::ofClass; + + std::string Code = R"( + class A { + public: + A() = default; + A(const A&) = default; + int a; + }; + + class B : public A { + public: + B() = default; + B(const B& b) =default; + B* copy() { + return new B(*this); + } + }; + )"; + ASSERT_THAT_ERROR( + checkDataflow( + AnalysisInputs( + Code, cxxConstructorDecl(ofClass(hasName("B"))), + [](ASTContext &C, Environment &) { + return NoopAnalysis(C, /*ApplyBuiltinTransfer=*/true); + }) + .withASTBuildArgs( + {"-fsyntax-only", "-fno-delayed-template-parsing", + "-std=" + std::string(LangStandard::getLangStandardForKind( + LangStandard::lang_cxx17) + .getName())}), + /*VerifyResults=*/ + [](const llvm::StringMap> &Results, + const AnalysisOutputs &) { + // Regression test to verify that base-class initializers do not + // trigger an assertion. If we add support for such initializers in + // the future, we can expand this test to check more specific + // properties. + }), + llvm::Succeeded()); +} + TEST(TransferTest, CopyConstructorWithParens) { std::string Code = R"( struct A {