[clang][dataflow] Handle null pointers of type std::nullptr_t

Treat `std::nullptr_t` as a regular scalar type to avoid tripping
assertions when analyzing code that uses `std::nullptr_t`.

Differential Revision: https://reviews.llvm.org/D129097
This commit is contained in:
Eric Li 2022-07-04 20:46:07 +00:00
parent d1af09ad96
commit f10d271ae2
3 changed files with 30 additions and 5 deletions

View File

@ -155,6 +155,7 @@ public:
/// Returns a pointer value that represents a null pointer. Calls with
/// `PointeeType` that are canonically equivalent will return the same result.
/// A null `PointeeType` can be used for the pointee of `std::nullptr_t`.
PointerValue &getOrCreateNullPointerValue(QualType PointeeType);
/// Returns a symbolic boolean value that models a boolean literal equal to
@ -251,6 +252,17 @@ public:
bool equivalentBoolValues(BoolValue &Val1, BoolValue &Val2);
private:
struct NullableQualTypeDenseMapInfo : private llvm::DenseMapInfo<QualType> {
static QualType getEmptyKey() {
// Allow a NULL `QualType` by using a different value as the empty key.
return QualType::getFromOpaquePtr(reinterpret_cast<Type *>(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 +323,8 @@ private:
// required to initialize the `PointeeLoc` field in `PointerValue`. Consider
// creating a type-independent `NullPointerValue` without a `PointeeLoc`
// field.
llvm::DenseMap<QualType, PointerValue *> NullPointerVals;
llvm::DenseMap<QualType, PointerValue *, NullableQualTypeDenseMapInfo>
NullPointerVals;
AtomicBoolValue &TrueVal;
AtomicBoolValue &FalseVal;

View File

@ -24,8 +24,8 @@ namespace dataflow {
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<const ValueDecl *, StorageLocation *> FieldLocs;
@ -57,8 +57,8 @@ DataflowAnalysisContext::getStableStorageLocation(const Expr &E) {
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);

View File

@ -2213,12 +2213,14 @@ TEST(TransferTest, IntegralToBooleanCastFromBool) {
TEST(TransferTest, NullToPointerCast) {
std::string Code = R"(
using my_nullptr_t = decltype(nullptr);
struct Baz {};
void target() {
int *FooX = nullptr;
int *FooY = nullptr;
bool **Bar = nullptr;
Baz *Baz = nullptr;
my_nullptr_t Null = 0;
// [[p]]
}
)";
@ -2242,6 +2244,9 @@ TEST(TransferTest, NullToPointerCast) {
const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
ASSERT_THAT(BazDecl, NotNull());
const ValueDecl *NullDecl = findValueDecl(ASTCtx, "Null");
ASSERT_THAT(NullDecl, NotNull());
const auto *FooXVal =
cast<PointerValue>(Env.getValue(*FooXDecl, SkipPast::None));
const auto *FooYVal =
@ -2250,6 +2255,8 @@ TEST(TransferTest, NullToPointerCast) {
cast<PointerValue>(Env.getValue(*BarDecl, SkipPast::None));
const auto *BazVal =
cast<PointerValue>(Env.getValue(*BazDecl, SkipPast::None));
const auto *NullVal =
cast<PointerValue>(Env.getValue(*NullDecl, SkipPast::None));
EXPECT_EQ(FooXVal, FooYVal);
EXPECT_NE(FooXVal, BarVal);
@ -2267,6 +2274,11 @@ TEST(TransferTest, NullToPointerCast) {
const StorageLocation &BazPointeeLoc = BazVal->getPointeeLoc();
EXPECT_TRUE(isa<AggregateStorageLocation>(BazPointeeLoc));
EXPECT_THAT(Env.getValue(BazPointeeLoc), IsNull());
const StorageLocation &NullPointeeLoc =
NullVal->getPointeeLoc();
EXPECT_TRUE(isa<ScalarStorageLocation>(NullPointeeLoc));
EXPECT_THAT(Env.getValue(NullPointeeLoc), IsNull());
});
}