diff --git a/clang/include/clang/Analysis/FlowSensitive/StorageLocation.h b/clang/include/clang/Analysis/FlowSensitive/StorageLocation.h --- a/clang/include/clang/Analysis/FlowSensitive/StorageLocation.h +++ b/clang/include/clang/Analysis/FlowSensitive/StorageLocation.h @@ -18,6 +18,7 @@ #include "clang/AST/Type.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorOr.h" #include #define DEBUG_TYPE "dataflow" @@ -135,6 +136,19 @@ return It->second; } + /// Returns the child storage location for `D` if it exists. + /// + /// Same as `getChild()`, but returns llvm::Error if the field `D` does not + /// exist. Most users should use `getChild()` instead, except for the code + /// that performs deep copy of two records of the same class, where a field + /// in one record may not exist in another record. + llvm::ErrorOr getChildOrError(const ValueDecl &D) const { + auto It = Children.find(&D); + if (It == Children.end()) + return std::errc::invalid_argument; + return It->second; + } + /// Changes the child storage location for a field `D` of reference type. /// All other fields cannot change their storage location and always retain /// the storage location passed to the `RecordStorageLocation` constructor. diff --git a/clang/lib/Analysis/FlowSensitive/RecordOps.cpp b/clang/lib/Analysis/FlowSensitive/RecordOps.cpp --- a/clang/lib/Analysis/FlowSensitive/RecordOps.cpp +++ b/clang/lib/Analysis/FlowSensitive/RecordOps.cpp @@ -36,7 +36,10 @@ assert(compatibleTypes); for (auto [Field, DstFieldLoc] : Dst.children()) { - StorageLocation *SrcFieldLoc = Src.getChild(*Field); + llvm::ErrorOr SrcField = Src.getChildOrError(*Field); + if (!SrcField) + continue; + StorageLocation *SrcFieldLoc = SrcField.get(); assert(Field->getType()->isReferenceType() || (SrcFieldLoc != nullptr && DstFieldLoc != nullptr)); diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -16,10 +16,8 @@ #include "clang/Analysis/FlowSensitive/StorageLocation.h" #include "clang/Analysis/FlowSensitive/Value.h" #include "clang/Basic/LangStandard.h" -#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Support/Casting.h" #include "llvm/Testing/Support/Error.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -2023,6 +2021,28 @@ }); } +TEST(TransferTest, AssignmentOperatorWithInitAndInheritance) { + // This is a crash repro. + std::string Code = R"( + struct B { int Foo; }; + struct S : public B {}; + void target() { + S S1 = { 1 }; + S S2; + S S3; + S1 = S2; // Only Dst has InitListExpr. + S3 = S1; // Only Src has InitListExpr. + } + )"; + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) { + // FIXME: Add tests to check if `Foo` exists in S1, S2, and S3 once + // initialization with inheritance is fixed. + }); +} + TEST(TransferTest, AssignmentOperatorFromCallResult) { std::string Code = R"( struct A {}; @@ -2253,6 +2273,49 @@ ASTContext &ASTCtx) {}); } +TEST(TransferTest, CopyConstructorWithInheritance) { + // This is a crash repro. + std::string Code = R"( + struct B { int Foo; }; + struct S : public B {}; + void target() { + S S1 = { 1 }; + S S2(S1); + // [p1] + S2.Foo = 2; + // [p2] + } + )"; + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) { + // FIXME: Add tests to check if `Foo` exists both in S1 and S2 once + // initialization with inheritance is fixed. + }); +} + +TEST(TransferTest, CopyConstructorWithBaseInitAndInheritance) { + // This is a crash repro. + std::string Code = R"( + struct Foo { int Bar; }; + struct B { Foo F = { 1 }; }; + struct S : public B {}; + void target() { + S S1 = {}; + S S2(S1); + // [p] + } + )"; + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) { + // FIXME: Add tests to check if `Bar` exists both in S1 and S2 once + // initialization with inheritance is fixed. + }); +} + TEST(TransferTest, MoveConstructor) { std::string Code = R"( namespace std { @@ -2328,6 +2391,22 @@ }); } +TEST(TransferTest, ReturnStructWithInheritance) { + // This is a crash repro. + std::string Code = R"( + struct B { int Foo; }; + struct S : public B { }; + S target() { + S S1 = { 1 }; + return S1; + } + )"; + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) {}); +} + TEST(TransferTest, BindTemporary) { std::string Code = R"( struct A {