diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h @@ -251,6 +251,17 @@ bool equivalentBoolValues(BoolValue &Val1, BoolValue &Val2); private: + struct NullableQualTypeDenseMapInfo : private llvm::DenseMapInfo { + static QualType getEmptyKey() { + // Allow a NULL `QualType` by using a different value as the empty key. + return QualType::getFromOpaquePtr(reinterpret_cast(1)); + } + + using DenseMapInfo::getHashValue; + using DenseMapInfo::getTombstoneKey; + using DenseMapInfo::isEqual; + }; + /// Adds all constraints of the flow condition identified by `Token` and all /// of its transitive dependencies to `Constraints`. `VisitedTokens` is used /// to track tokens of flow conditions that were already visited by recursive @@ -311,7 +322,8 @@ // required to initialize the `PointeeLoc` field in `PointerValue`. Consider // creating a type-independent `NullPointerValue` without a `PointeeLoc` // field. - llvm::DenseMap NullPointerVals; + llvm::DenseMap + NullPointerVals; AtomicBoolValue &TrueVal; AtomicBoolValue &FalseVal; diff --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp --- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp @@ -24,8 +24,8 @@ StorageLocation & DataflowAnalysisContext::getStableStorageLocation(QualType Type) { - assert(!Type.isNull()); - if (Type->isStructureOrClassType() || Type->isUnionType()) { + if (!Type.isNull() && + (Type->isStructureOrClassType() || Type->isUnionType())) { // FIXME: Explore options to avoid eager initialization of fields as some of // them might not be needed for a particular analysis. llvm::DenseMap FieldLocs; @@ -57,8 +57,8 @@ PointerValue & DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) { - assert(!PointeeType.isNull()); - auto CanonicalPointeeType = PointeeType.getCanonicalType(); + auto CanonicalPointeeType = + PointeeType.isNull() ? PointeeType : PointeeType.getCanonicalType(); auto Res = NullPointerVals.try_emplace(CanonicalPointeeType, nullptr); if (Res.second) { auto &PointeeLoc = getStableStorageLocation(CanonicalPointeeType); 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 @@ -2219,6 +2219,9 @@ int *FooY = nullptr; bool **Bar = nullptr; Baz *Baz = nullptr; + // Use decltype to avoid needing to include . This generates an + // implicit NullToPointer cast of type `std::nullptr_t`. + decltype(nullptr) Null = 0; // [[p]] } )"; @@ -2242,6 +2245,9 @@ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); ASSERT_THAT(BazDecl, NotNull()); + const ValueDecl *NullDecl = findValueDecl(ASTCtx, "Null"); + ASSERT_THAT(NullDecl, NotNull()); + const auto *FooXVal = cast(Env.getValue(*FooXDecl, SkipPast::None)); const auto *FooYVal = @@ -2250,6 +2256,8 @@ cast(Env.getValue(*BarDecl, SkipPast::None)); const auto *BazVal = cast(Env.getValue(*BazDecl, SkipPast::None)); + const auto *NullVal = + cast(Env.getValue(*NullDecl, SkipPast::None)); EXPECT_EQ(FooXVal, FooYVal); EXPECT_NE(FooXVal, BarVal); @@ -2267,6 +2275,11 @@ const StorageLocation &BazPointeeLoc = BazVal->getPointeeLoc(); EXPECT_TRUE(isa(BazPointeeLoc)); EXPECT_THAT(Env.getValue(BazPointeeLoc), IsNull()); + + const StorageLocation &NullPointeeLoc = + NullVal->getPointeeLoc(); + EXPECT_TRUE(isa(NullPointeeLoc)); + EXPECT_THAT(Env.getValue(NullPointeeLoc), IsNull()); }); }