Skip to content

Commit

Permalink
[clang][dataflow] Model the behavior of various optional members
Browse files Browse the repository at this point in the history
Model `make_optional`, optional's default constructor, `emplace`,
`reset`, and `operator bool` members.

Reviewed-by: xazax.hun

Differential Revision: https://reviews.llvm.org/D121378
  • Loading branch information
sgatev committed Mar 14, 2022
1 parent 45ab190 commit 9e0fc67
Show file tree
Hide file tree
Showing 2 changed files with 444 additions and 8 deletions.
Expand Up @@ -11,6 +11,8 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include <cassert>
#include <memory>
#include <utility>

namespace clang {
namespace dataflow {
Expand Down Expand Up @@ -41,6 +43,21 @@ static auto isOptionalOperatorCallWithName(llvm::StringRef OperatorName) {
callee(cxxMethodDecl(ofClass(optionalClass()))));
}

static auto isMakeOptionalCall() {
return callExpr(
callee(functionDecl(hasAnyName(
"std::make_optional", "base::make_optional", "absl::make_optional"))),
hasOptionalType());
}

/// Creates a symbolic value for an `optional` value using `HasValueVal` as the
/// symbolic value of its "has_value" property.
StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
auto OptionalVal = std::make_unique<StructValue>();
OptionalVal->setProperty("has_value", HasValueVal);
return Env.takeOwnership(std::move(OptionalVal));
}

/// Returns the symbolic value that represents the "has_value" property of the
/// optional value `Val`. Returns null if `Val` is null.
static BoolValue *getHasValue(Value *Val) {
Expand Down Expand Up @@ -75,6 +92,13 @@ static void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
State.Lattice.getSourceLocations().insert(ObjectExpr->getBeginLoc());
}

void transferMakeOptionalCall(const CallExpr *E, LatticeTransferState &State) {
auto &Loc = State.Env.createStorageLocation(*E);
State.Env.setStorageLocation(*E, Loc);
State.Env.setValue(
Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
}

static void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
LatticeTransferState &State) {
if (auto *OptionalVal = cast_or_null<StructValue>(
Expand All @@ -89,13 +113,36 @@ static void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
}
}

void transferEmplaceCall(const CXXMemberCallExpr *E,
LatticeTransferState &State) {
if (auto *OptionalLoc = State.Env.getStorageLocation(
*E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer)) {
State.Env.setValue(
*OptionalLoc,
createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
}
}

void transferResetCall(const CXXMemberCallExpr *E,
LatticeTransferState &State) {
if (auto *OptionalLoc = State.Env.getStorageLocation(
*E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer)) {
State.Env.setValue(
*OptionalLoc,
createOptionalValue(State.Env, State.Env.getBoolLiteralValue(false)));
}
}

static auto buildTransferMatchSwitch() {
return MatchSwitchBuilder<LatticeTransferState>()
// Attach a symbolic "has_value" state to optional values that we see for
// the first time.
.CaseOf(expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
initializeOptionalReference)

// make_optional
.CaseOf(isMakeOptionalCall(), transferMakeOptionalCall)

// optional::value
.CaseOf(
isOptionalMemberCallWithName("value"),
Expand All @@ -115,6 +162,16 @@ static auto buildTransferMatchSwitch() {
.CaseOf(isOptionalMemberCallWithName("has_value"),
transferOptionalHasValueCall)

// optional::operator bool
.CaseOf(isOptionalMemberCallWithName("operator bool"),
transferOptionalHasValueCall)

// optional::emplace
.CaseOf(isOptionalMemberCallWithName("emplace"), transferEmplaceCall)

// optional::reset
.CaseOf(isOptionalMemberCallWithName("reset"), transferResetCall)

.Build();
}

Expand Down

0 comments on commit 9e0fc67

Please sign in to comment.