diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp index f5eb469e7bb3d..e13f880896fc0 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -125,18 +125,19 @@ static Value *mergeDistinctValues(QualType Type, Value &Val1, Value *MergedVal = nullptr; if (auto *RecordVal1 = dyn_cast(&Val1)) { - [[maybe_unused]] auto *RecordVal2 = cast(&Val2); - - // Values to be merged are always associated with the same location in - // `LocToVal`. The location stored in `RecordVal` should therefore also - // be the same. - assert(&RecordVal1->getLoc() == &RecordVal2->getLoc()); - - // `RecordVal1` and `RecordVal2` may have different properties associated - // with them. Create a new `RecordValue` without any properties so that we - // soundly approximate both values. If a particular analysis needs to merge - // properties, it should do so in `DataflowAnalysis::merge()`. - MergedVal = &MergedEnv.create(RecordVal1->getLoc()); + auto *RecordVal2 = cast(&Val2); + + if (&RecordVal1->getLoc() == &RecordVal2->getLoc()) + // `RecordVal1` and `RecordVal2` may have different properties associated + // with them. Create a new `RecordValue` with the same location but + // without any properties so that we soundly approximate both values. If a + // particular analysis needs to merge properties, it should do so in + // `DataflowAnalysis::merge()`. + MergedVal = &MergedEnv.create(RecordVal1->getLoc()); + else + // If the locations for the two records are different, need to create a + // completely new value. + MergedVal = MergedEnv.createValue(Type); } else { MergedVal = MergedEnv.createValue(Type); } diff --git a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp index a318d9ab7391a..6e37011a052c5 100644 --- a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp @@ -96,6 +96,75 @@ TEST_F(EnvironmentTest, CreateValueRecursiveType) { EXPECT_THAT(PV, NotNull()); } +TEST_F(EnvironmentTest, JoinRecords) { + using namespace ast_matchers; + + std::string Code = R"cc( + struct S {}; + // Need to use the type somewhere so that the `QualType` gets created; + S s; + )cc"; + + auto Unit = + tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); + auto &Context = Unit->getASTContext(); + + ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); + + auto Results = + match(qualType(hasDeclaration(recordDecl(hasName("S")))).bind("SType"), + Context); + const QualType *TyPtr = selectFirst("SType", Results); + ASSERT_THAT(TyPtr, NotNull()); + QualType Ty = *TyPtr; + ASSERT_FALSE(Ty.isNull()); + + auto *ConstructExpr = CXXConstructExpr::CreateEmpty(Context, 0); + ConstructExpr->setType(Ty); + ConstructExpr->setValueKind(VK_PRValue); + + // Two different `RecordValue`s with the same location are joined into a + // third `RecordValue` with that same location. + { + Environment Env1(DAContext); + auto &Val1 = *cast(Env1.createValue(Ty)); + RecordStorageLocation &Loc = Val1.getLoc(); + Env1.setValue(*ConstructExpr, Val1); + + Environment Env2(DAContext); + auto &Val2 = Env2.create(Loc); + Env2.setValue(Loc, Val2); + Env2.setValue(*ConstructExpr, Val2); + + Environment::ValueModel Model; + Environment EnvJoined = Environment::join(Env1, Env2, Model); + auto *JoinedVal = cast(EnvJoined.getValue(*ConstructExpr)); + EXPECT_NE(JoinedVal, &Val1); + EXPECT_NE(JoinedVal, &Val2); + EXPECT_EQ(&JoinedVal->getLoc(), &Loc); + } + + // Two different `RecordValue`s with different locations are joined into a + // third `RecordValue` with a location different from the other two. + { + Environment Env1(DAContext); + auto &Val1 = *cast(Env1.createValue(Ty)); + Env1.setValue(*ConstructExpr, Val1); + + Environment Env2(DAContext); + auto &Val2 = *cast(Env2.createValue(Ty)); + Env2.setValue(*ConstructExpr, Val2); + + Environment::ValueModel Model; + Environment EnvJoined = Environment::join(Env1, Env2, Model); + auto *JoinedVal = cast(EnvJoined.getValue(*ConstructExpr)); + EXPECT_NE(JoinedVal, &Val1); + EXPECT_NE(JoinedVal, &Val2); + EXPECT_NE(&JoinedVal->getLoc(), &Val1.getLoc()); + EXPECT_NE(&JoinedVal->getLoc(), &Val2.getLoc()); + } +} + TEST_F(EnvironmentTest, InitGlobalVarsFun) { using namespace ast_matchers;