2021-12-29 11:31:02 +00:00
|
|
|
//===-- Transfer.cpp --------------------------------------------*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file defines transfer functions that evaluate program statements and
|
|
|
|
// update an environment accordingly.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "clang/Analysis/FlowSensitive/Transfer.h"
|
|
|
|
#include "clang/AST/Decl.h"
|
|
|
|
#include "clang/AST/DeclBase.h"
|
2022-01-13 13:53:52 +00:00
|
|
|
#include "clang/AST/DeclCXX.h"
|
2021-12-29 11:31:02 +00:00
|
|
|
#include "clang/AST/Expr.h"
|
2022-01-11 12:15:53 +00:00
|
|
|
#include "clang/AST/ExprCXX.h"
|
2022-01-04 13:47:14 +00:00
|
|
|
#include "clang/AST/OperationKinds.h"
|
2021-12-29 11:31:02 +00:00
|
|
|
#include "clang/AST/Stmt.h"
|
|
|
|
#include "clang/AST/StmtVisitor.h"
|
2022-07-26 17:54:13 +00:00
|
|
|
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
|
2021-12-29 11:31:02 +00:00
|
|
|
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
|
2022-07-26 17:54:13 +00:00
|
|
|
#include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
|
2022-03-25 20:01:18 +00:00
|
|
|
#include "clang/Analysis/FlowSensitive/Value.h"
|
2022-04-01 12:51:23 +00:00
|
|
|
#include "clang/Basic/Builtins.h"
|
2022-01-04 13:47:14 +00:00
|
|
|
#include "clang/Basic/OperatorKinds.h"
|
2022-01-24 16:17:22 +00:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2021-12-29 11:31:02 +00:00
|
|
|
#include "llvm/Support/Casting.h"
|
2022-10-06 17:56:41 +00:00
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
2021-12-29 11:31:02 +00:00
|
|
|
#include <cassert>
|
2022-01-04 13:47:14 +00:00
|
|
|
#include <memory>
|
2022-01-24 16:17:22 +00:00
|
|
|
#include <tuple>
|
2021-12-29 11:31:02 +00:00
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace dataflow {
|
|
|
|
|
2023-03-28 07:56:24 +00:00
|
|
|
const Environment *StmtToEnvMap::getEnvironment(const Stmt &S) const {
|
|
|
|
auto BlockIt = CFCtx.getStmtToBlock().find(&ignoreCFGOmittedNodes(S));
|
|
|
|
assert(BlockIt != CFCtx.getStmtToBlock().end());
|
|
|
|
if (!CFCtx.isBlockReachable(*BlockIt->getSecond()))
|
|
|
|
return nullptr;
|
|
|
|
const auto &State = BlockToState[BlockIt->getSecond()->getBlockID()];
|
|
|
|
assert(State);
|
|
|
|
return &State->Env;
|
|
|
|
}
|
|
|
|
|
2022-03-25 20:01:18 +00:00
|
|
|
static BoolValue &evaluateBooleanEquality(const Expr &LHS, const Expr &RHS,
|
|
|
|
Environment &Env) {
|
2022-05-04 21:08:43 +00:00
|
|
|
if (auto *LHSValue =
|
|
|
|
dyn_cast_or_null<BoolValue>(Env.getValue(LHS, SkipPast::Reference)))
|
|
|
|
if (auto *RHSValue =
|
|
|
|
dyn_cast_or_null<BoolValue>(Env.getValue(RHS, SkipPast::Reference)))
|
2022-03-25 20:01:18 +00:00
|
|
|
return Env.makeIff(*LHSValue, *RHSValue);
|
|
|
|
|
|
|
|
return Env.makeAtomicBoolValue();
|
|
|
|
}
|
|
|
|
|
2022-10-06 17:56:41 +00:00
|
|
|
// Functionally updates `V` such that any instances of `TopBool` are replaced
|
|
|
|
// with fresh atomic bools. Note: This implementation assumes that `B` is a
|
|
|
|
// tree; if `B` is a DAG, it will lose any sharing between subvalues that was
|
|
|
|
// present in the original .
|
|
|
|
static BoolValue &unpackValue(BoolValue &V, Environment &Env);
|
|
|
|
|
|
|
|
template <typename Derived, typename M>
|
|
|
|
BoolValue &unpackBinaryBoolValue(Environment &Env, BoolValue &B, M build) {
|
|
|
|
auto &V = *cast<Derived>(&B);
|
|
|
|
BoolValue &Left = V.getLeftSubValue();
|
|
|
|
BoolValue &Right = V.getRightSubValue();
|
|
|
|
BoolValue &ULeft = unpackValue(Left, Env);
|
|
|
|
BoolValue &URight = unpackValue(Right, Env);
|
|
|
|
|
|
|
|
if (&ULeft == &Left && &URight == &Right)
|
|
|
|
return V;
|
|
|
|
|
|
|
|
return (Env.*build)(ULeft, URight);
|
|
|
|
}
|
|
|
|
|
|
|
|
static BoolValue &unpackValue(BoolValue &V, Environment &Env) {
|
|
|
|
switch (V.getKind()) {
|
|
|
|
case Value::Kind::Integer:
|
|
|
|
case Value::Kind::Reference:
|
|
|
|
case Value::Kind::Pointer:
|
|
|
|
case Value::Kind::Struct:
|
|
|
|
llvm_unreachable("BoolValue cannot have any of these kinds.");
|
|
|
|
|
|
|
|
case Value::Kind::AtomicBool:
|
|
|
|
return V;
|
|
|
|
|
|
|
|
case Value::Kind::TopBool:
|
|
|
|
// Unpack `TopBool` into a fresh atomic bool.
|
|
|
|
return Env.makeAtomicBoolValue();
|
|
|
|
|
|
|
|
case Value::Kind::Negation: {
|
|
|
|
auto &N = *cast<NegationValue>(&V);
|
|
|
|
BoolValue &Sub = N.getSubVal();
|
|
|
|
BoolValue &USub = unpackValue(Sub, Env);
|
|
|
|
|
|
|
|
if (&USub == &Sub)
|
|
|
|
return V;
|
|
|
|
return Env.makeNot(USub);
|
|
|
|
}
|
|
|
|
case Value::Kind::Conjunction:
|
|
|
|
return unpackBinaryBoolValue<ConjunctionValue>(Env, V,
|
|
|
|
&Environment::makeAnd);
|
|
|
|
case Value::Kind::Disjunction:
|
|
|
|
return unpackBinaryBoolValue<DisjunctionValue>(Env, V,
|
|
|
|
&Environment::makeOr);
|
|
|
|
case Value::Kind::Implication:
|
|
|
|
return unpackBinaryBoolValue<ImplicationValue>(
|
|
|
|
Env, V, &Environment::makeImplication);
|
|
|
|
case Value::Kind::Biconditional:
|
|
|
|
return unpackBinaryBoolValue<BiconditionalValue>(Env, V,
|
|
|
|
&Environment::makeIff);
|
|
|
|
}
|
2022-10-14 18:33:02 +00:00
|
|
|
llvm_unreachable("All reachable cases in switch return");
|
2022-10-06 17:56:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Unpacks the value (if any) associated with `E` and updates `E` to the new
|
2023-01-03 15:41:38 +00:00
|
|
|
// value, if any unpacking occured. Also, does the lvalue-to-rvalue conversion,
|
|
|
|
// by skipping past the reference.
|
2022-10-06 17:56:41 +00:00
|
|
|
static Value *maybeUnpackLValueExpr(const Expr &E, Environment &Env) {
|
2023-01-13 18:33:52 +00:00
|
|
|
// FIXME: this is too flexible: it _allows_ a reference, while it should
|
|
|
|
// _require_ one, since lvalues should always be wrapped in `ReferenceValue`.
|
2022-10-06 17:56:41 +00:00
|
|
|
auto *Loc = Env.getStorageLocation(E, SkipPast::Reference);
|
|
|
|
if (Loc == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
auto *Val = Env.getValue(*Loc);
|
|
|
|
|
|
|
|
auto *B = dyn_cast_or_null<BoolValue>(Val);
|
|
|
|
if (B == nullptr)
|
|
|
|
return Val;
|
|
|
|
|
|
|
|
auto &UnpackedVal = unpackValue(*B, Env);
|
|
|
|
if (&UnpackedVal == Val)
|
|
|
|
return Val;
|
|
|
|
Env.setValue(*Loc, UnpackedVal);
|
|
|
|
return &UnpackedVal;
|
|
|
|
}
|
|
|
|
|
2023-03-28 08:07:51 +00:00
|
|
|
namespace {
|
|
|
|
|
2021-12-29 11:31:02 +00:00
|
|
|
class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
|
|
|
|
public:
|
2022-12-27 17:34:30 +00:00
|
|
|
TransferVisitor(const StmtToEnvMap &StmtToEnv, Environment &Env)
|
|
|
|
: StmtToEnv(StmtToEnv), Env(Env) {}
|
2021-12-29 11:31:02 +00:00
|
|
|
|
2022-01-04 13:47:14 +00:00
|
|
|
void VisitBinaryOperator(const BinaryOperator *S) {
|
2022-05-04 21:08:43 +00:00
|
|
|
const Expr *LHS = S->getLHS();
|
2022-03-11 11:52:53 +00:00
|
|
|
assert(LHS != nullptr);
|
|
|
|
|
2022-05-04 21:08:43 +00:00
|
|
|
const Expr *RHS = S->getRHS();
|
2022-03-11 11:52:53 +00:00
|
|
|
assert(RHS != nullptr);
|
|
|
|
|
2022-02-16 16:47:37 +00:00
|
|
|
switch (S->getOpcode()) {
|
|
|
|
case BO_Assign: {
|
2022-01-04 13:47:14 +00:00
|
|
|
auto *LHSLoc = Env.getStorageLocation(*LHS, SkipPast::Reference);
|
|
|
|
if (LHSLoc == nullptr)
|
2022-02-16 16:47:37 +00:00
|
|
|
break;
|
2022-01-04 13:47:14 +00:00
|
|
|
|
2023-01-03 15:41:38 +00:00
|
|
|
// No skipping should be necessary, because any lvalues should have
|
|
|
|
// already been stripped off in evaluating the LValueToRValue cast.
|
|
|
|
auto *RHSVal = Env.getValue(*RHS, SkipPast::None);
|
2022-01-04 13:47:14 +00:00
|
|
|
if (RHSVal == nullptr)
|
2022-02-16 16:47:37 +00:00
|
|
|
break;
|
2022-01-04 13:47:14 +00:00
|
|
|
|
|
|
|
// Assign a value to the storage location of the left-hand side.
|
|
|
|
Env.setValue(*LHSLoc, *RHSVal);
|
|
|
|
|
|
|
|
// Assign a storage location for the whole expression.
|
|
|
|
Env.setStorageLocation(*S, *LHSLoc);
|
2022-02-16 16:47:37 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case BO_LAnd:
|
|
|
|
case BO_LOr: {
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
2023-03-23 07:45:40 +00:00
|
|
|
|
|
|
|
BoolValue *LHSVal = getLogicOperatorSubExprValue(*LHS);
|
|
|
|
// If the LHS was not reachable, this BinaryOperator would also not be
|
|
|
|
// reachable, and we would never get here.
|
|
|
|
assert(LHSVal != nullptr);
|
|
|
|
BoolValue *RHSVal = getLogicOperatorSubExprValue(*RHS);
|
|
|
|
if (RHSVal == nullptr) {
|
|
|
|
// If the RHS isn't reachable and we evaluate this BinaryOperator,
|
|
|
|
// then the value of the LHS must have triggered the short-circuit
|
|
|
|
// logic. This implies that the value of the entire expression must be
|
|
|
|
// equal to the value of the LHS.
|
|
|
|
Env.setValue(Loc, *LHSVal);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-02-16 16:47:37 +00:00
|
|
|
if (S->getOpcode() == BO_LAnd)
|
2023-03-23 07:45:40 +00:00
|
|
|
Env.setValue(Loc, Env.makeAnd(*LHSVal, *RHSVal));
|
2022-02-16 16:47:37 +00:00
|
|
|
else
|
2023-03-23 07:45:40 +00:00
|
|
|
Env.setValue(Loc, Env.makeOr(*LHSVal, *RHSVal));
|
2022-02-16 16:47:37 +00:00
|
|
|
break;
|
|
|
|
}
|
2022-03-25 20:01:18 +00:00
|
|
|
case BO_NE:
|
|
|
|
case BO_EQ: {
|
|
|
|
auto &LHSEqRHSValue = evaluateBooleanEquality(*LHS, *RHS, Env);
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
|
|
|
Env.setValue(Loc, S->getOpcode() == BO_EQ ? LHSEqRHSValue
|
|
|
|
: Env.makeNot(LHSEqRHSValue));
|
|
|
|
break;
|
|
|
|
}
|
2022-06-16 21:57:28 +00:00
|
|
|
case BO_Comma: {
|
|
|
|
if (auto *Loc = Env.getStorageLocation(*RHS, SkipPast::None))
|
|
|
|
Env.setStorageLocation(*S, *Loc);
|
|
|
|
break;
|
|
|
|
}
|
2022-02-16 16:47:37 +00:00
|
|
|
default:
|
|
|
|
break;
|
2022-01-04 13:47:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisitDeclRefExpr(const DeclRefExpr *S) {
|
2022-12-07 16:03:37 +00:00
|
|
|
const ValueDecl *VD = S->getDecl();
|
|
|
|
assert(VD != nullptr);
|
|
|
|
auto *DeclLoc = Env.getStorageLocation(*VD, SkipPast::None);
|
2022-01-04 13:47:14 +00:00
|
|
|
if (DeclLoc == nullptr)
|
|
|
|
return;
|
|
|
|
|
2023-01-03 15:41:38 +00:00
|
|
|
// If the value is already an lvalue, don't double-wrap it.
|
|
|
|
if (isa_and_nonnull<ReferenceValue>(Env.getValue(*DeclLoc))) {
|
|
|
|
// We only expect to encounter a `ReferenceValue` for a reference type
|
|
|
|
// (always) or for `BindingDecl` (sometimes). For the latter, we can't
|
|
|
|
// rely on type, because their type does not indicate whether they are a
|
|
|
|
// reference type. The assert is not strictly necessary, since we don't
|
|
|
|
// depend on its truth to proceed. But, it verifies our assumptions,
|
|
|
|
// which, if violated, probably indicate a problem elsewhere.
|
|
|
|
assert((VD->getType()->isReferenceType() || isa<BindingDecl>(VD)) &&
|
|
|
|
"Only reference-typed declarations or `BindingDecl`s should map "
|
|
|
|
"to `ReferenceValue`s");
|
2022-01-04 13:47:14 +00:00
|
|
|
Env.setStorageLocation(*S, *DeclLoc);
|
|
|
|
} else {
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
2023-04-03 14:45:13 +00:00
|
|
|
auto &Val = Env.create<ReferenceValue>(*DeclLoc);
|
2022-01-04 13:47:14 +00:00
|
|
|
Env.setStorageLocation(*S, Loc);
|
|
|
|
Env.setValue(Loc, Val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-29 11:31:02 +00:00
|
|
|
void VisitDeclStmt(const DeclStmt *S) {
|
2022-01-14 18:27:39 +00:00
|
|
|
// Group decls are converted into single decls in the CFG so the cast below
|
|
|
|
// is safe.
|
|
|
|
const auto &D = *cast<VarDecl>(S->getSingleDecl());
|
2022-02-23 13:38:51 +00:00
|
|
|
|
|
|
|
// Static local vars are already initialized in `Environment`.
|
|
|
|
if (D.hasGlobalStorage())
|
|
|
|
return;
|
|
|
|
|
2022-12-07 16:03:37 +00:00
|
|
|
// The storage location for `D` could have been created earlier, before the
|
|
|
|
// variable's declaration statement (for example, in the case of
|
|
|
|
// BindingDecls).
|
|
|
|
auto *MaybeLoc = Env.getStorageLocation(D, SkipPast::None);
|
|
|
|
if (MaybeLoc == nullptr) {
|
|
|
|
MaybeLoc = &Env.createStorageLocation(D);
|
|
|
|
Env.setStorageLocation(D, *MaybeLoc);
|
|
|
|
}
|
|
|
|
auto &Loc = *MaybeLoc;
|
2022-01-14 18:27:39 +00:00
|
|
|
|
|
|
|
const Expr *InitExpr = D.getInit();
|
|
|
|
if (InitExpr == nullptr) {
|
|
|
|
// No initializer expression - associate `Loc` with a new value.
|
2022-01-17 15:17:05 +00:00
|
|
|
if (Value *Val = Env.createValue(D.getType()))
|
|
|
|
Env.setValue(Loc, *Val);
|
2022-01-14 18:27:39 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (D.getType()->isReferenceType()) {
|
|
|
|
// Initializing a reference variable - do not create a reference to
|
|
|
|
// reference.
|
2023-01-03 15:41:38 +00:00
|
|
|
// FIXME: reuse the ReferenceValue instead of creating a new one.
|
2022-01-14 18:27:39 +00:00
|
|
|
if (auto *InitExprLoc =
|
|
|
|
Env.getStorageLocation(*InitExpr, SkipPast::Reference)) {
|
2023-04-03 14:45:13 +00:00
|
|
|
auto &Val = Env.create<ReferenceValue>(*InitExprLoc);
|
2022-01-14 18:27:39 +00:00
|
|
|
Env.setValue(Loc, Val);
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
2022-04-14 13:41:06 +00:00
|
|
|
} else if (auto *InitExprVal = Env.getValue(*InitExpr, SkipPast::None)) {
|
|
|
|
Env.setValue(Loc, *InitExprVal);
|
2022-01-14 18:27:39 +00:00
|
|
|
}
|
|
|
|
|
2022-02-24 17:07:41 +00:00
|
|
|
if (Env.getValue(Loc) == nullptr) {
|
|
|
|
// We arrive here in (the few) cases where an expression is intentionally
|
|
|
|
// "uninterpreted". There are two ways to handle this situation: propagate
|
|
|
|
// the status, so that uninterpreted initializers result in uninterpreted
|
|
|
|
// variables, or provide a default value. We choose the latter so that
|
|
|
|
// later refinements of the variable can be used for reasoning about the
|
|
|
|
// surrounding code.
|
|
|
|
//
|
|
|
|
// FIXME. If and when we interpret all language cases, change this to
|
|
|
|
// assert that `InitExpr` is interpreted, rather than supplying a default
|
|
|
|
// value (assuming we don't update the environment API to return
|
|
|
|
// references).
|
|
|
|
if (Value *Val = Env.createValue(D.getType()))
|
|
|
|
Env.setValue(Loc, *Val);
|
|
|
|
}
|
|
|
|
|
2023-01-03 15:41:38 +00:00
|
|
|
// `DecompositionDecl` must be handled after we've interpreted the loc
|
|
|
|
// itself, because the binding expression refers back to the
|
|
|
|
// `DecompositionDecl` (even though it has no written name).
|
2022-02-24 17:07:41 +00:00
|
|
|
if (const auto *Decomp = dyn_cast<DecompositionDecl>(&D)) {
|
|
|
|
// If VarDecl is a DecompositionDecl, evaluate each of its bindings. This
|
|
|
|
// needs to be evaluated after initializing the values in the storage for
|
|
|
|
// VarDecl, as the bindings refer to them.
|
|
|
|
// FIXME: Add support for ArraySubscriptExpr.
|
2022-12-07 16:03:37 +00:00
|
|
|
// FIXME: Consider adding AST nodes used in BindingDecls to the CFG.
|
2022-02-24 17:07:41 +00:00
|
|
|
for (const auto *B : Decomp->bindings()) {
|
2022-12-07 16:03:37 +00:00
|
|
|
if (auto *ME = dyn_cast_or_null<MemberExpr>(B->getBinding())) {
|
|
|
|
auto *DE = dyn_cast_or_null<DeclRefExpr>(ME->getBase());
|
|
|
|
if (DE == nullptr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// ME and its base haven't been visited because they aren't included
|
|
|
|
// in the statements of the CFG basic block.
|
|
|
|
VisitDeclRefExpr(DE);
|
|
|
|
VisitMemberExpr(ME);
|
|
|
|
|
|
|
|
if (auto *Loc = Env.getStorageLocation(*ME, SkipPast::Reference))
|
|
|
|
Env.setStorageLocation(*B, *Loc);
|
|
|
|
} else if (auto *VD = B->getHoldingVar()) {
|
|
|
|
// Holding vars are used to back the BindingDecls of tuple-like
|
|
|
|
// types. The holding var declarations appear *after* this statement,
|
2023-01-06 13:34:12 +00:00
|
|
|
// so we have to create a location for them here to share with `B`. We
|
2022-12-07 16:03:37 +00:00
|
|
|
// don't visit the binding, because we know it will be a DeclRefExpr
|
2023-01-03 15:41:38 +00:00
|
|
|
// to `VD`. Note that, by construction of the AST, `VD` will always be
|
|
|
|
// a reference -- either lvalue or rvalue.
|
2022-12-07 16:03:37 +00:00
|
|
|
auto &VDLoc = Env.createStorageLocation(*VD);
|
|
|
|
Env.setStorageLocation(*VD, VDLoc);
|
|
|
|
Env.setStorageLocation(*B, VDLoc);
|
|
|
|
}
|
2022-02-24 17:07:41 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
2022-01-04 13:47:14 +00:00
|
|
|
void VisitImplicitCastExpr(const ImplicitCastExpr *S) {
|
2022-05-02 21:36:04 +00:00
|
|
|
const Expr *SubExpr = S->getSubExpr();
|
2022-01-13 13:53:52 +00:00
|
|
|
assert(SubExpr != nullptr);
|
|
|
|
|
|
|
|
switch (S->getCastKind()) {
|
2022-04-04 15:31:52 +00:00
|
|
|
case CK_IntegralToBoolean: {
|
|
|
|
// This cast creates a new, boolean value from the integral value. We
|
|
|
|
// model that with a fresh value in the environment, unless it's already a
|
|
|
|
// boolean.
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
|
|
|
if (auto *SubExprVal = dyn_cast_or_null<BoolValue>(
|
|
|
|
Env.getValue(*SubExpr, SkipPast::Reference)))
|
|
|
|
Env.setValue(Loc, *SubExprVal);
|
|
|
|
else
|
|
|
|
// FIXME: If integer modeling is added, then update this code to create
|
|
|
|
// the boolean based on the integer model.
|
|
|
|
Env.setValue(Loc, Env.makeAtomicBoolValue());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-01-13 13:53:52 +00:00
|
|
|
case CK_LValueToRValue: {
|
2022-10-06 17:56:41 +00:00
|
|
|
// When an L-value is used as an R-value, it may result in sharing, so we
|
2023-01-03 15:41:38 +00:00
|
|
|
// need to unpack any nested `Top`s. We also need to strip off the
|
|
|
|
// `ReferenceValue` associated with the lvalue.
|
2022-10-06 17:56:41 +00:00
|
|
|
auto *SubExprVal = maybeUnpackLValueExpr(*SubExpr, Env);
|
2022-01-04 13:47:14 +00:00
|
|
|
if (SubExprVal == nullptr)
|
2022-01-13 13:53:52 +00:00
|
|
|
break;
|
2022-01-04 13:47:14 +00:00
|
|
|
|
|
|
|
auto &ExprLoc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, ExprLoc);
|
|
|
|
Env.setValue(ExprLoc, *SubExprVal);
|
2022-01-13 13:53:52 +00:00
|
|
|
break;
|
|
|
|
}
|
2022-04-04 15:31:52 +00:00
|
|
|
|
|
|
|
case CK_IntegralCast:
|
|
|
|
// FIXME: This cast creates a new integral value from the
|
|
|
|
// subexpression. But, because we don't model integers, we don't
|
|
|
|
// distinguish between this new value and the underlying one. If integer
|
|
|
|
// modeling is added, then update this code to create a fresh location and
|
|
|
|
// value.
|
2022-03-14 14:52:35 +00:00
|
|
|
case CK_UncheckedDerivedToBase:
|
|
|
|
case CK_ConstructorConversion:
|
|
|
|
case CK_UserDefinedConversion:
|
|
|
|
// FIXME: Add tests that excercise CK_UncheckedDerivedToBase,
|
|
|
|
// CK_ConstructorConversion, and CK_UserDefinedConversion.
|
2022-01-13 13:53:52 +00:00
|
|
|
case CK_NoOp: {
|
|
|
|
// FIXME: Consider making `Environment::getStorageLocation` skip noop
|
|
|
|
// expressions (this and other similar expressions in the file) instead of
|
|
|
|
// assigning them storage locations.
|
|
|
|
auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
|
|
|
|
if (SubExprLoc == nullptr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
Env.setStorageLocation(*S, *SubExprLoc);
|
|
|
|
break;
|
|
|
|
}
|
2022-06-27 14:14:01 +02:00
|
|
|
case CK_NullToPointer:
|
|
|
|
case CK_NullToMemberPointer: {
|
|
|
|
auto &Loc = Env.createStorageLocation(S->getType());
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
|
|
|
|
|
|
|
auto &NullPointerVal =
|
|
|
|
Env.getOrCreateNullPointerValue(S->getType()->getPointeeType());
|
|
|
|
Env.setValue(Loc, NullPointerVal);
|
|
|
|
break;
|
|
|
|
}
|
2023-04-18 04:49:38 +00:00
|
|
|
case CK_FunctionToPointerDecay: {
|
|
|
|
StorageLocation *PointeeLoc =
|
|
|
|
Env.getStorageLocation(*SubExpr, SkipPast::Reference);
|
|
|
|
if (PointeeLoc == nullptr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
auto &PointerLoc = Env.createStorageLocation(*S);
|
|
|
|
auto &PointerVal = Env.create<PointerValue>(*PointeeLoc);
|
|
|
|
Env.setStorageLocation(*S, PointerLoc);
|
|
|
|
Env.setValue(PointerLoc, PointerVal);
|
|
|
|
break;
|
|
|
|
}
|
2022-01-13 13:53:52 +00:00
|
|
|
default:
|
|
|
|
break;
|
2022-01-04 13:47:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisitUnaryOperator(const UnaryOperator *S) {
|
2022-05-02 21:36:04 +00:00
|
|
|
const Expr *SubExpr = S->getSubExpr();
|
2022-01-17 16:23:24 +00:00
|
|
|
assert(SubExpr != nullptr);
|
|
|
|
|
|
|
|
switch (S->getOpcode()) {
|
|
|
|
case UO_Deref: {
|
2022-01-19 09:28:02 +00:00
|
|
|
// Skip past a reference to handle dereference of a dependent pointer.
|
2022-01-04 13:47:14 +00:00
|
|
|
const auto *SubExprVal = cast_or_null<PointerValue>(
|
2022-01-17 16:23:24 +00:00
|
|
|
Env.getValue(*SubExpr, SkipPast::Reference));
|
2022-01-04 13:47:14 +00:00
|
|
|
if (SubExprVal == nullptr)
|
2022-01-17 16:23:24 +00:00
|
|
|
break;
|
2022-01-04 13:47:14 +00:00
|
|
|
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
2023-04-03 14:45:13 +00:00
|
|
|
Env.setValue(Loc,
|
|
|
|
Env.create<ReferenceValue>(SubExprVal->getPointeeLoc()));
|
2022-01-17 16:23:24 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case UO_AddrOf: {
|
|
|
|
// Do not form a pointer to a reference. If `SubExpr` is assigned a
|
|
|
|
// `ReferenceValue` then form a value that points to the location of its
|
|
|
|
// pointee.
|
|
|
|
StorageLocation *PointeeLoc =
|
|
|
|
Env.getStorageLocation(*SubExpr, SkipPast::Reference);
|
|
|
|
if (PointeeLoc == nullptr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
auto &PointerLoc = Env.createStorageLocation(*S);
|
2023-04-03 14:45:13 +00:00
|
|
|
auto &PointerVal = Env.create<PointerValue>(*PointeeLoc);
|
2022-01-17 16:23:24 +00:00
|
|
|
Env.setStorageLocation(*S, PointerLoc);
|
|
|
|
Env.setValue(PointerLoc, PointerVal);
|
|
|
|
break;
|
|
|
|
}
|
2022-02-16 16:47:37 +00:00
|
|
|
case UO_LNot: {
|
|
|
|
auto *SubExprVal =
|
|
|
|
dyn_cast_or_null<BoolValue>(Env.getValue(*SubExpr, SkipPast::None));
|
|
|
|
if (SubExprVal == nullptr)
|
2022-02-17 09:37:02 +00:00
|
|
|
break;
|
2022-02-16 16:47:37 +00:00
|
|
|
|
|
|
|
auto &ExprLoc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, ExprLoc);
|
2022-03-04 11:03:29 +00:00
|
|
|
Env.setValue(ExprLoc, Env.makeNot(*SubExprVal));
|
2022-02-17 09:37:02 +00:00
|
|
|
break;
|
2022-02-16 16:47:37 +00:00
|
|
|
}
|
2022-01-17 16:23:24 +00:00
|
|
|
default:
|
|
|
|
break;
|
2022-01-04 13:47:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-11 12:15:53 +00:00
|
|
|
void VisitCXXThisExpr(const CXXThisExpr *S) {
|
|
|
|
auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
|
2022-05-25 19:21:08 +00:00
|
|
|
if (ThisPointeeLoc == nullptr)
|
|
|
|
// Unions are not supported yet, and will not have a location for the
|
|
|
|
// `this` expression's pointee.
|
|
|
|
return;
|
2022-01-11 12:15:53 +00:00
|
|
|
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
2023-04-03 14:45:13 +00:00
|
|
|
Env.setValue(Loc, Env.create<PointerValue>(*ThisPointeeLoc));
|
2022-01-11 12:15:53 +00:00
|
|
|
}
|
|
|
|
|
2023-04-18 03:42:24 +00:00
|
|
|
void VisitCXXNewExpr(const CXXNewExpr *S) {
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
|
|
|
if (Value *Val = Env.createValue(S->getType()))
|
|
|
|
Env.setValue(Loc, *Val);
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisitCXXDeleteExpr(const CXXDeleteExpr *S) {
|
|
|
|
// Empty method.
|
|
|
|
// We consciously don't do anything on deletes. Diagnosing double deletes
|
|
|
|
// (for example) should be done by a specific analysis, not by the
|
|
|
|
// framework.
|
|
|
|
}
|
|
|
|
|
2022-08-04 17:42:01 +00:00
|
|
|
void VisitReturnStmt(const ReturnStmt *S) {
|
2022-12-27 17:34:30 +00:00
|
|
|
if (!Env.getAnalysisOptions().ContextSensitiveOpts)
|
2022-12-20 19:41:19 +00:00
|
|
|
return;
|
|
|
|
|
2022-08-04 17:42:01 +00:00
|
|
|
auto *Ret = S->getRetValue();
|
|
|
|
if (Ret == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto *Val = Env.getValue(*Ret, SkipPast::None);
|
|
|
|
if (Val == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// FIXME: Support reference-type returns.
|
|
|
|
if (Val->getKind() == Value::Kind::Reference)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto *Loc = Env.getReturnStorageLocation();
|
|
|
|
assert(Loc != nullptr);
|
2022-12-20 19:41:19 +00:00
|
|
|
// FIXME: Support reference-type returns.
|
|
|
|
if (Loc->getType()->isReferenceType())
|
|
|
|
return;
|
|
|
|
|
2022-08-04 17:42:01 +00:00
|
|
|
// FIXME: Model NRVO.
|
|
|
|
Env.setValue(*Loc, *Val);
|
|
|
|
}
|
|
|
|
|
2022-01-11 12:15:53 +00:00
|
|
|
void VisitMemberExpr(const MemberExpr *S) {
|
|
|
|
ValueDecl *Member = S->getMemberDecl();
|
|
|
|
assert(Member != nullptr);
|
|
|
|
|
|
|
|
// FIXME: Consider assigning pointer values to function member expressions.
|
|
|
|
if (Member->isFunctionOrFunctionTemplate())
|
|
|
|
return;
|
|
|
|
|
2023-01-09 22:54:53 +00:00
|
|
|
// FIXME: if/when we add support for modeling enums, use that support here.
|
|
|
|
if (isa<EnumConstantDecl>(Member))
|
|
|
|
return;
|
|
|
|
|
2022-02-23 13:38:51 +00:00
|
|
|
if (auto *D = dyn_cast<VarDecl>(Member)) {
|
|
|
|
if (D->hasGlobalStorage()) {
|
|
|
|
auto *VarDeclLoc = Env.getStorageLocation(*D, SkipPast::None);
|
|
|
|
if (VarDeclLoc == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (VarDeclLoc->getType()->isReferenceType()) {
|
2023-01-24 14:51:32 +00:00
|
|
|
assert(isa_and_nonnull<ReferenceValue>(Env.getValue((*VarDeclLoc))) &&
|
|
|
|
"reference-typed declarations map to `ReferenceValue`s");
|
2022-02-23 13:38:51 +00:00
|
|
|
Env.setStorageLocation(*S, *VarDeclLoc);
|
|
|
|
} else {
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
2023-04-03 14:45:13 +00:00
|
|
|
Env.setValue(Loc, Env.create<ReferenceValue>(*VarDeclLoc));
|
2022-02-23 13:38:51 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-11 12:15:53 +00:00
|
|
|
// The receiver can be either a value or a pointer to a value. Skip past the
|
|
|
|
// indirection to handle both cases.
|
|
|
|
auto *BaseLoc = cast_or_null<AggregateStorageLocation>(
|
|
|
|
Env.getStorageLocation(*S->getBase(), SkipPast::ReferenceThenPointer));
|
|
|
|
if (BaseLoc == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto &MemberLoc = BaseLoc->getChild(*Member);
|
|
|
|
if (MemberLoc.getType()->isReferenceType()) {
|
2023-01-24 14:51:32 +00:00
|
|
|
// Based on its type, `MemberLoc` must be mapped either to nothing or to a
|
|
|
|
// `ReferenceValue`. For the former, we won't set a storage location for
|
|
|
|
// this expression, so as to maintain an invariant lvalue expressions;
|
|
|
|
// namely, that their location maps to a `ReferenceValue`. In this,
|
|
|
|
// lvalues are unlike other expressions, where it is valid for their
|
|
|
|
// location to map to nothing (because they are not modeled).
|
|
|
|
//
|
|
|
|
// Note: we need this invariant for lvalues so that, when accessing a
|
|
|
|
// value, we can distinguish an rvalue from an lvalue. An alternative
|
|
|
|
// design, which takes the expression's value category into account, would
|
|
|
|
// avoid the need for this invariant.
|
|
|
|
if (auto *V = Env.getValue(MemberLoc)) {
|
|
|
|
assert(isa<ReferenceValue>(V) &&
|
|
|
|
"reference-typed declarations map to `ReferenceValue`s");
|
|
|
|
Env.setStorageLocation(*S, MemberLoc);
|
|
|
|
}
|
2022-01-11 12:15:53 +00:00
|
|
|
} else {
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
2023-04-03 14:45:13 +00:00
|
|
|
Env.setValue(Loc, Env.create<ReferenceValue>(MemberLoc));
|
2022-01-11 12:15:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-12 16:28:59 +00:00
|
|
|
void VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *S) {
|
|
|
|
const Expr *InitExpr = S->getExpr();
|
|
|
|
assert(InitExpr != nullptr);
|
|
|
|
|
|
|
|
Value *InitExprVal = Env.getValue(*InitExpr, SkipPast::None);
|
|
|
|
if (InitExprVal == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const FieldDecl *Field = S->getField();
|
|
|
|
assert(Field != nullptr);
|
|
|
|
|
|
|
|
auto &ThisLoc =
|
|
|
|
*cast<AggregateStorageLocation>(Env.getThisPointeeStorageLocation());
|
|
|
|
auto &FieldLoc = ThisLoc.getChild(*Field);
|
|
|
|
Env.setValue(FieldLoc, *InitExprVal);
|
|
|
|
}
|
|
|
|
|
2022-01-13 13:53:52 +00:00
|
|
|
void VisitCXXConstructExpr(const CXXConstructExpr *S) {
|
|
|
|
const CXXConstructorDecl *ConstructorDecl = S->getConstructor();
|
|
|
|
assert(ConstructorDecl != nullptr);
|
|
|
|
|
|
|
|
if (ConstructorDecl->isCopyOrMoveConstructor()) {
|
2023-02-22 10:36:39 +00:00
|
|
|
// It is permissible for a copy/move constructor to have additional
|
|
|
|
// parameters as long as they have default arguments defined for them.
|
|
|
|
assert(S->getNumArgs() != 0);
|
2022-01-13 13:53:52 +00:00
|
|
|
|
|
|
|
const Expr *Arg = S->getArg(0);
|
|
|
|
assert(Arg != nullptr);
|
|
|
|
|
|
|
|
if (S->isElidable()) {
|
|
|
|
auto *ArgLoc = Env.getStorageLocation(*Arg, SkipPast::Reference);
|
|
|
|
if (ArgLoc == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Env.setStorageLocation(*S, *ArgLoc);
|
|
|
|
} else if (auto *ArgVal = Env.getValue(*Arg, SkipPast::Reference)) {
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
|
|
|
Env.setValue(Loc, *ArgVal);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
2022-01-17 15:17:05 +00:00
|
|
|
if (Value *Val = Env.createValue(S->getType()))
|
|
|
|
Env.setValue(Loc, *Val);
|
2022-08-10 14:01:18 +00:00
|
|
|
|
|
|
|
transferInlineCall(S, ConstructorDecl);
|
2022-01-13 13:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *S) {
|
|
|
|
if (S->getOperator() == OO_Equal) {
|
|
|
|
assert(S->getNumArgs() == 2);
|
|
|
|
|
|
|
|
const Expr *Arg0 = S->getArg(0);
|
|
|
|
assert(Arg0 != nullptr);
|
|
|
|
|
|
|
|
const Expr *Arg1 = S->getArg(1);
|
|
|
|
assert(Arg1 != nullptr);
|
|
|
|
|
|
|
|
// Evaluate only copy and move assignment operators.
|
2023-04-24 13:11:02 +00:00
|
|
|
const auto *Method =
|
|
|
|
dyn_cast_or_null<CXXMethodDecl>(S->getDirectCallee());
|
|
|
|
if (!Method)
|
|
|
|
return;
|
|
|
|
if (!Method->isCopyAssignmentOperator() &&
|
|
|
|
!Method->isMoveAssignmentOperator())
|
2022-01-13 13:53:52 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
auto *ObjectLoc = Env.getStorageLocation(*Arg0, SkipPast::Reference);
|
|
|
|
if (ObjectLoc == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto *Val = Env.getValue(*Arg1, SkipPast::Reference);
|
|
|
|
if (Val == nullptr)
|
|
|
|
return;
|
|
|
|
|
2022-03-16 22:20:05 +00:00
|
|
|
// Assign a value to the storage location of the object.
|
2022-01-13 13:53:52 +00:00
|
|
|
Env.setValue(*ObjectLoc, *Val);
|
2022-03-16 22:20:05 +00:00
|
|
|
|
|
|
|
// FIXME: Add a test for the value of the whole expression.
|
|
|
|
// Assign a storage location for the whole expression.
|
|
|
|
Env.setStorageLocation(*S, *ObjectLoc);
|
2022-01-13 13:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *S) {
|
|
|
|
if (S->getCastKind() == CK_ConstructorConversion) {
|
|
|
|
const Expr *SubExpr = S->getSubExpr();
|
|
|
|
assert(SubExpr != nullptr);
|
|
|
|
|
|
|
|
auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
|
|
|
|
if (SubExprLoc == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Env.setStorageLocation(*S, *SubExprLoc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *S) {
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
2022-01-17 15:17:05 +00:00
|
|
|
if (Value *Val = Env.createValue(S->getType()))
|
|
|
|
Env.setValue(Loc, *Val);
|
2022-01-13 13:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VisitCallExpr(const CallExpr *S) {
|
2022-04-01 12:51:23 +00:00
|
|
|
// Of clang's builtins, only `__builtin_expect` is handled explicitly, since
|
|
|
|
// others (like trap, debugtrap, and unreachable) are handled by CFG
|
|
|
|
// construction.
|
2022-01-13 13:53:52 +00:00
|
|
|
if (S->isCallToStdMove()) {
|
|
|
|
assert(S->getNumArgs() == 1);
|
|
|
|
|
|
|
|
const Expr *Arg = S->getArg(0);
|
|
|
|
assert(Arg != nullptr);
|
|
|
|
|
|
|
|
auto *ArgLoc = Env.getStorageLocation(*Arg, SkipPast::None);
|
|
|
|
if (ArgLoc == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Env.setStorageLocation(*S, *ArgLoc);
|
2022-04-01 12:51:23 +00:00
|
|
|
} else if (S->getDirectCallee() != nullptr &&
|
|
|
|
S->getDirectCallee()->getBuiltinID() ==
|
|
|
|
Builtin::BI__builtin_expect) {
|
|
|
|
assert(S->getNumArgs() > 0);
|
|
|
|
assert(S->getArg(0) != nullptr);
|
|
|
|
// `__builtin_expect` returns by-value, so strip away any potential
|
|
|
|
// references in the argument.
|
2022-05-04 21:08:43 +00:00
|
|
|
auto *ArgLoc = Env.getStorageLocation(*S->getArg(0), SkipPast::Reference);
|
2022-04-01 12:51:23 +00:00
|
|
|
if (ArgLoc == nullptr)
|
|
|
|
return;
|
|
|
|
Env.setStorageLocation(*S, *ArgLoc);
|
2022-07-26 17:54:13 +00:00
|
|
|
} else if (const FunctionDecl *F = S->getDirectCallee()) {
|
2022-08-10 14:01:18 +00:00
|
|
|
transferInlineCall(S, F);
|
2022-01-13 13:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *S) {
|
|
|
|
const Expr *SubExpr = S->getSubExpr();
|
|
|
|
assert(SubExpr != nullptr);
|
|
|
|
|
|
|
|
auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
|
|
|
|
if (SubExprLoc == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Env.setStorageLocation(*S, *SubExprLoc);
|
|
|
|
}
|
|
|
|
|
2022-01-14 18:27:39 +00:00
|
|
|
void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *S) {
|
|
|
|
const Expr *SubExpr = S->getSubExpr();
|
|
|
|
assert(SubExpr != nullptr);
|
2022-01-04 13:47:14 +00:00
|
|
|
|
2022-01-14 18:27:39 +00:00
|
|
|
auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
|
|
|
|
if (SubExprLoc == nullptr)
|
2022-01-04 13:47:14 +00:00
|
|
|
return;
|
|
|
|
|
2022-01-14 18:27:39 +00:00
|
|
|
Env.setStorageLocation(*S, *SubExprLoc);
|
|
|
|
}
|
2022-01-13 13:53:52 +00:00
|
|
|
|
2022-01-14 18:27:39 +00:00
|
|
|
void VisitCXXStaticCastExpr(const CXXStaticCastExpr *S) {
|
|
|
|
if (S->getCastKind() == CK_NoOp) {
|
|
|
|
const Expr *SubExpr = S->getSubExpr();
|
|
|
|
assert(SubExpr != nullptr);
|
2022-01-04 13:47:14 +00:00
|
|
|
|
2022-01-14 18:27:39 +00:00
|
|
|
auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
|
|
|
|
if (SubExprLoc == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Env.setStorageLocation(*S, *SubExprLoc);
|
2022-01-04 13:47:14 +00:00
|
|
|
}
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
2022-01-19 13:56:21 +00:00
|
|
|
void VisitConditionalOperator(const ConditionalOperator *S) {
|
|
|
|
// FIXME: Revisit this once flow conditions are added to the framework. For
|
|
|
|
// `a = b ? c : d` we can add `b => a == c && !b => a == d` to the flow
|
|
|
|
// condition.
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
|
|
|
if (Value *Val = Env.createValue(S->getType()))
|
|
|
|
Env.setValue(Loc, *Val);
|
|
|
|
}
|
|
|
|
|
2022-01-24 16:17:22 +00:00
|
|
|
void VisitInitListExpr(const InitListExpr *S) {
|
|
|
|
QualType Type = S->getType();
|
|
|
|
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
|
|
|
|
|
|
|
auto *Val = Env.createValue(Type);
|
|
|
|
if (Val == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Env.setValue(Loc, *Val);
|
|
|
|
|
|
|
|
if (Type->isStructureOrClassType()) {
|
2023-02-27 18:15:39 +00:00
|
|
|
// Unnamed bitfields are only used for padding and are not appearing in
|
|
|
|
// `InitListExpr`'s inits. However, those fields do appear in RecordDecl's
|
|
|
|
// field list, and we thus need to remove them before mapping inits to
|
|
|
|
// fields to avoid mapping inits to the wrongs fields.
|
|
|
|
std::vector<FieldDecl *> Fields;
|
|
|
|
llvm::copy_if(
|
|
|
|
Type->getAsRecordDecl()->fields(), std::back_inserter(Fields),
|
|
|
|
[](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); });
|
|
|
|
for (auto It : llvm::zip(Fields, S->inits())) {
|
2022-07-25 20:23:23 +02:00
|
|
|
const FieldDecl *Field = std::get<0>(It);
|
2022-01-24 16:17:22 +00:00
|
|
|
assert(Field != nullptr);
|
|
|
|
|
2022-07-25 20:23:23 +02:00
|
|
|
const Expr *Init = std::get<1>(It);
|
2022-01-24 16:17:22 +00:00
|
|
|
assert(Init != nullptr);
|
|
|
|
|
|
|
|
if (Value *InitVal = Env.getValue(*Init, SkipPast::None))
|
|
|
|
cast<StructValue>(Val)->setChild(*Field, *InitVal);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// FIXME: Implement array initialization.
|
|
|
|
}
|
|
|
|
|
2022-01-26 12:10:38 +00:00
|
|
|
void VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *S) {
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
|
|
|
Env.setValue(Loc, Env.getBoolLiteralValue(S->getValue()));
|
|
|
|
}
|
2022-01-14 18:27:39 +00:00
|
|
|
|
2022-05-04 21:08:43 +00:00
|
|
|
void VisitParenExpr(const ParenExpr *S) {
|
|
|
|
// The CFG does not contain `ParenExpr` as top-level statements in basic
|
|
|
|
// blocks, however manual traversal to sub-expressions may encounter them.
|
|
|
|
// Redirect to the sub-expression.
|
|
|
|
auto *SubExpr = S->getSubExpr();
|
|
|
|
assert(SubExpr != nullptr);
|
|
|
|
Visit(SubExpr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisitExprWithCleanups(const ExprWithCleanups *S) {
|
|
|
|
// The CFG does not contain `ExprWithCleanups` as top-level statements in
|
|
|
|
// basic blocks, however manual traversal to sub-expressions may encounter
|
|
|
|
// them. Redirect to the sub-expression.
|
|
|
|
auto *SubExpr = S->getSubExpr();
|
|
|
|
assert(SubExpr != nullptr);
|
|
|
|
Visit(SubExpr);
|
|
|
|
}
|
|
|
|
|
2022-01-14 18:27:39 +00:00
|
|
|
private:
|
2023-03-23 07:45:40 +00:00
|
|
|
/// If `SubExpr` is reachable, returns a non-null pointer to the value for
|
|
|
|
/// `SubExpr`. If `SubExpr` is not reachable, returns nullptr.
|
|
|
|
BoolValue *getLogicOperatorSubExprValue(const Expr &SubExpr) {
|
2022-03-11 11:52:53 +00:00
|
|
|
// `SubExpr` and its parent logic operator might be part of different basic
|
|
|
|
// blocks. We try to access the value that is assigned to `SubExpr` in the
|
|
|
|
// corresponding environment.
|
2023-03-23 07:45:40 +00:00
|
|
|
const Environment *SubExprEnv = StmtToEnv.getEnvironment(SubExpr);
|
|
|
|
if (!SubExprEnv)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
if (auto *Val = dyn_cast_or_null<BoolValue>(
|
|
|
|
SubExprEnv->getValue(SubExpr, SkipPast::Reference)))
|
|
|
|
return Val;
|
2022-03-11 11:52:53 +00:00
|
|
|
|
2022-05-17 18:08:25 +00:00
|
|
|
if (Env.getStorageLocation(SubExpr, SkipPast::None) == nullptr) {
|
|
|
|
// Sub-expressions that are logic operators are not added in basic blocks
|
|
|
|
// (e.g. see CFG for `bool d = a && (b || c);`). If `SubExpr` is a logic
|
|
|
|
// operator, it may not have been evaluated and assigned a value yet. In
|
|
|
|
// that case, we need to first visit `SubExpr` and then try to get the
|
|
|
|
// value that gets assigned to it.
|
|
|
|
Visit(&SubExpr);
|
|
|
|
}
|
|
|
|
|
2022-03-11 11:52:53 +00:00
|
|
|
if (auto *Val = dyn_cast_or_null<BoolValue>(
|
|
|
|
Env.getValue(SubExpr, SkipPast::Reference)))
|
2023-03-23 07:45:40 +00:00
|
|
|
return Val;
|
2022-03-11 11:52:53 +00:00
|
|
|
|
|
|
|
// If the value of `SubExpr` is still unknown, we create a fresh symbolic
|
|
|
|
// boolean value for it.
|
2023-03-23 07:45:40 +00:00
|
|
|
return &Env.makeAtomicBoolValue();
|
2022-03-11 11:52:53 +00:00
|
|
|
}
|
|
|
|
|
2022-08-10 14:01:18 +00:00
|
|
|
// If context sensitivity is enabled, try to analyze the body of the callee
|
|
|
|
// `F` of `S`. The type `E` must be either `CallExpr` or `CXXConstructExpr`.
|
|
|
|
template <typename E>
|
|
|
|
void transferInlineCall(const E *S, const FunctionDecl *F) {
|
2022-12-27 17:34:30 +00:00
|
|
|
const auto &Options = Env.getAnalysisOptions();
|
2022-08-15 19:58:23 +00:00
|
|
|
if (!(Options.ContextSensitiveOpts &&
|
|
|
|
Env.canDescend(Options.ContextSensitiveOpts->Depth, F)))
|
2022-08-10 14:01:18 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
const ControlFlowContext *CFCtx = Env.getControlFlowContext(F);
|
|
|
|
if (!CFCtx)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// FIXME: We don't support context-sensitive analysis of recursion, so
|
|
|
|
// we should return early here if `F` is the same as the `FunctionDecl`
|
|
|
|
// holding `S` itself.
|
|
|
|
|
|
|
|
auto ExitBlock = CFCtx->getCFG().getExit().getBlockID();
|
|
|
|
|
|
|
|
if (const auto *NonConstructExpr = dyn_cast<CallExpr>(S)) {
|
|
|
|
// Note that it is important for the storage location of `S` to be set
|
|
|
|
// before `pushCall`, because the latter uses it to set the storage
|
|
|
|
// location for `return`.
|
|
|
|
auto &ReturnLoc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, ReturnLoc);
|
|
|
|
}
|
|
|
|
auto CalleeEnv = Env.pushCall(S);
|
|
|
|
|
|
|
|
// FIXME: Use the same analysis as the caller for the callee. Note,
|
|
|
|
// though, that doing so would require support for changing the analysis's
|
|
|
|
// ASTContext.
|
|
|
|
assert(CFCtx->getDecl() != nullptr &&
|
|
|
|
"ControlFlowContexts in the environment should always carry a decl");
|
|
|
|
auto Analysis = NoopAnalysis(CFCtx->getDecl()->getASTContext(),
|
2022-08-15 19:58:23 +00:00
|
|
|
DataflowAnalysisOptions{Options});
|
2022-08-10 14:01:18 +00:00
|
|
|
|
|
|
|
auto BlockToOutputState =
|
|
|
|
dataflow::runDataflowAnalysis(*CFCtx, Analysis, CalleeEnv);
|
|
|
|
assert(BlockToOutputState);
|
|
|
|
assert(ExitBlock < BlockToOutputState->size());
|
|
|
|
|
|
|
|
auto ExitState = (*BlockToOutputState)[ExitBlock];
|
|
|
|
assert(ExitState);
|
|
|
|
|
|
|
|
Env.popCall(ExitState->Env);
|
|
|
|
}
|
|
|
|
|
2022-02-16 16:47:37 +00:00
|
|
|
const StmtToEnvMap &StmtToEnv;
|
2021-12-29 11:31:02 +00:00
|
|
|
Environment &Env;
|
|
|
|
};
|
|
|
|
|
2023-03-28 08:07:51 +00:00
|
|
|
} // namespace
|
|
|
|
|
2022-12-27 17:34:30 +00:00
|
|
|
void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env) {
|
|
|
|
TransferVisitor(StmtToEnv, Env).Visit(&S);
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace dataflow
|
|
|
|
} // namespace clang
|