Add "is_known" synthetic property to null state of PointerValue.
Considering whether a pointer's nullability is known changes the safety criteria for pointer dereferencing - only pointers that are known to be nullable on some path reaching the dereference will be unsafe. Unannotated pointers are treated as nullable pointers which need to be null-checked before dereferencing in this iteration.
PiperOrigin-RevId: 464816394
diff --git a/nullability_verification/pointer_nullability_analysis.cc b/nullability_verification/pointer_nullability_analysis.cc
index 5e1460c..c098111 100644
--- a/nullability_verification/pointer_nullability_analysis.cc
+++ b/nullability_verification/pointer_nullability_analysis.cc
@@ -25,6 +25,7 @@
namespace nullability {
using ast_matchers::MatchFinder;
+using dataflow::AtomicBoolValue;
using dataflow::BoolValue;
using dataflow::Environment;
using dataflow::MatchSwitchBuilder;
@@ -35,51 +36,77 @@
namespace {
+constexpr llvm::StringLiteral kKnown = "is_known";
constexpr llvm::StringLiteral kNotNull = "is_notnull";
-BoolValue& getPointerNotNullProperty(
+std::pair<AtomicBoolValue&, AtomicBoolValue&> getPointerNullState(
const Expr* PointerExpr, TransferState<PointerNullabilityLattice>& State) {
auto* PointerVal =
cast<PointerValue>(State.Env.getValue(*PointerExpr, SkipPast::Reference));
- return *cast<BoolValue>(PointerVal->getProperty(kNotNull));
+ auto& PointerKnown = *cast<AtomicBoolValue>(PointerVal->getProperty(kKnown));
+ auto& PointerNotNull =
+ *cast<AtomicBoolValue>(PointerVal->getProperty(kNotNull));
+ return {PointerKnown, PointerNotNull};
}
-void initPointerNotNullProperty(const Expr* PointerExpr,
- TransferState<PointerNullabilityLattice>& State,
- BoolValue* NotNull = nullptr) {
+void initPointerBoolProperty(PointerValue& PointerVal, llvm::StringRef Name,
+ BoolValue* BoolVal, Environment& Env) {
+ if (PointerVal.getProperty(Name) == nullptr) {
+ PointerVal.setProperty(Name,
+ BoolVal ? *BoolVal : Env.makeAtomicBoolValue());
+ }
+}
+
+/// The nullness information of a pointer is represented by two properties which
+/// indicate if a pointer's nullability (i.e. if the pointer can hold null) is
+/// `Known` and if the pointer's value is `NotNull`.
+void initPointerNullState(const Expr* PointerExpr,
+ TransferState<PointerNullabilityLattice>& State,
+ BoolValue* Known, BoolValue* NotNull = nullptr) {
if (auto* PointerVal = cast_or_null<PointerValue>(
State.Env.getValue(*PointerExpr, SkipPast::Reference))) {
- if (PointerVal->getProperty(kNotNull) == nullptr) {
- NotNull = NotNull ? NotNull : &State.Env.makeAtomicBoolValue();
- PointerVal->setProperty(kNotNull, *NotNull);
- }
+ initPointerBoolProperty(*PointerVal, kKnown, Known, State.Env);
+ initPointerBoolProperty(*PointerVal, kNotNull, NotNull, State.Env);
}
}
void transferInitNotNullPointer(
const Expr* NotNullPointer, const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
- initPointerNotNullProperty(NotNullPointer, State,
- &State.Env.getBoolLiteralValue(true));
+ initPointerNullState(NotNullPointer, State,
+ /*Known=*/&State.Env.getBoolLiteralValue(true),
+ /*NotNull=*/&State.Env.getBoolLiteralValue(true));
}
void transferInitNullPointer(const Expr* NullPointer,
- const MatchFinder::MatchResult& Result,
+ const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
- initPointerNotNullProperty(NullPointer, State,
- &State.Env.getBoolLiteralValue(false));
+ initPointerNullState(NullPointer, State,
+ /*Known=*/&State.Env.getBoolLiteralValue(true),
+ /*NotNull=*/&State.Env.getBoolLiteralValue(false));
}
-void transferInitPointerVariableReference(
+void transferInitNullablePointer(
+ const Expr* NullablePointer,
+ TransferState<PointerNullabilityLattice>& State) {
+ initPointerNullState(NullablePointer, State,
+ /*Known=*/&State.Env.getBoolLiteralValue(true));
+}
+
+void transferInitPointerFromDecl(
const Expr* PointerExpr, const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
- initPointerNotNullProperty(PointerExpr, State);
+ // TODO(wyt): Implement processing of nullability annotations. The current
+ // implementation treats unnannotated pointers as nullable.
+ transferInitNullablePointer(PointerExpr, State);
}
void transferPointerAccess(const Expr* PointerExpr,
TransferState<PointerNullabilityLattice>& State) {
- auto& PointerNotNull = getPointerNotNullProperty(PointerExpr, State);
- if (!State.Env.flowConditionImplies(PointerNotNull)) {
+ auto [PointerKnown, PointerNotNull] = getPointerNullState(PointerExpr, State);
+ auto& PointerNotKnownNull = State.Env.makeNot(
+ State.Env.makeAnd(PointerKnown, State.Env.makeNot(PointerNotNull)));
+ if (!State.Env.flowConditionImplies(PointerNotKnownNull)) {
State.Lattice.addViolation(PointerExpr);
}
}
@@ -91,7 +118,7 @@
}
void transferMemberExprInvolvingPointers(
- const MemberExpr* MemberExpr, const MatchFinder::MatchResult&,
+ const MemberExpr* MemberExpr, const MatchFinder::MatchResult& Result,
TransferState<PointerNullabilityLattice>& State) {
if (MemberExpr->isArrow()) {
// Base expr is a pointer, check that (->) access is safe
@@ -99,10 +126,13 @@
}
if (MemberExpr->getType()->isAnyPointerType()) {
// Accessed member is a pointer, initialise its nullability
- initPointerNotNullProperty(MemberExpr, State);
+ transferInitPointerFromDecl(MemberExpr, Result, State);
}
}
+// TODO(wyt): Implement promotion of nullability knownness for initially unknown
+// pointers when there is evidence that it is nullable, for example when the
+// pointer is compared to nullptr, or casted to boolean.
void transferNullCheckComparison(
const BinaryOperator* BinaryOp, const MatchFinder::MatchResult& result,
TransferState<PointerNullabilityLattice>& State) {
@@ -119,27 +149,31 @@
? State.Env.makeNot(PointerComparison)
: PointerComparison;
- auto& LHSNotNull = getPointerNotNullProperty(BinaryOp->getLHS(), State);
- auto& RHSNotNull = getPointerNotNullProperty(BinaryOp->getRHS(), State);
+ auto [LHSKnown, LHSNotNull] = getPointerNullState(BinaryOp->getLHS(), State);
+ auto [RHSKnown, RHSNotNull] = getPointerNullState(BinaryOp->getRHS(), State);
+ auto& LHSKnownNotNull = State.Env.makeAnd(LHSKnown, LHSNotNull);
+ auto& RHSKnownNotNull = State.Env.makeAnd(RHSKnown, RHSNotNull);
+ auto& LHSKnownNull =
+ State.Env.makeAnd(LHSKnown, State.Env.makeNot(LHSNotNull));
+ auto& RHSKnownNull =
+ State.Env.makeAnd(RHSKnown, State.Env.makeNot(RHSNotNull));
- // !LHS && !RHS => LHS == RHS
+ // nullptr == nullptr
State.Env.addToFlowCondition(State.Env.makeImplication(
- State.Env.makeAnd(State.Env.makeNot(LHSNotNull),
- State.Env.makeNot(RHSNotNull)),
- PointerEQ));
- // !LHS && RHS => LHS != RHS
+ State.Env.makeAnd(LHSKnownNull, RHSKnownNull), PointerEQ));
+ // nullptr != notnull
State.Env.addToFlowCondition(State.Env.makeImplication(
- State.Env.makeAnd(State.Env.makeNot(LHSNotNull), RHSNotNull), PointerNE));
- // LHS && !RHS => LHS != RHS
+ State.Env.makeAnd(LHSKnownNull, RHSKnownNotNull), PointerNE));
+ // notnull != nullptr
State.Env.addToFlowCondition(State.Env.makeImplication(
- State.Env.makeAnd(LHSNotNull, State.Env.makeNot(RHSNotNull)), PointerNE));
+ State.Env.makeAnd(LHSKnownNotNull, RHSKnownNull), PointerNE));
}
void transferNullCheckImplicitCastPtrToBool(
const Expr* CastExpr, const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
- auto& PointerNotNull =
- getPointerNotNullProperty(CastExpr->IgnoreImplicit(), State);
+ auto [PointerKnown, PointerNotNull] =
+ getPointerNullState(CastExpr->IgnoreImplicit(), State);
auto& CastExprLoc = State.Env.createStorageLocation(*CastExpr);
State.Env.setValue(CastExprLoc, PointerNotNull);
State.Env.setStorageLocation(*CastExpr, CastExprLoc);
@@ -148,8 +182,7 @@
auto buildTransferer() {
return MatchSwitchBuilder<TransferState<PointerNullabilityLattice>>()
// Handles initialization of the null states of pointers
- .CaseOf<Expr>(isPointerVariableReference(),
- transferInitPointerVariableReference)
+ .CaseOf<Expr>(isPointerVariableReference(), transferInitPointerFromDecl)
.CaseOf<Expr>(isCXXThisExpr(), transferInitNotNullPointer)
.CaseOf<Expr>(isAddrOf(), transferInitNotNullPointer)
.CaseOf<Expr>(isNullPointerLiteral(), transferInitNullPointer)