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"
|
|
|
|
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.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"
|
|
|
|
#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 {
|
|
|
|
|
2022-01-13 13:53:52 +00:00
|
|
|
static const Expr *skipExprWithCleanups(const Expr *E) {
|
|
|
|
if (auto *C = dyn_cast_or_null<ExprWithCleanups>(E))
|
|
|
|
return C->getSubExpr();
|
|
|
|
return E;
|
|
|
|
}
|
|
|
|
|
2022-03-25 20:01:18 +00:00
|
|
|
static BoolValue &evaluateBooleanEquality(const Expr &LHS, const Expr &RHS,
|
|
|
|
Environment &Env) {
|
|
|
|
// Equality of booleans involves implicit integral casts. Ignore these casts
|
|
|
|
// for now and focus on the values associated with the wrapped expressions.
|
|
|
|
// FIXME: Consider changing this once the framework offers better support for
|
|
|
|
// integral casts.
|
|
|
|
const Expr *LHSNorm = LHS.IgnoreCasts();
|
|
|
|
const Expr *RHSNorm = RHS.IgnoreCasts();
|
|
|
|
assert(LHSNorm != nullptr);
|
|
|
|
assert(RHSNorm != nullptr);
|
|
|
|
|
|
|
|
if (auto *LHSValue = dyn_cast_or_null<BoolValue>(
|
|
|
|
Env.getValue(*LHSNorm, SkipPast::Reference)))
|
|
|
|
if (auto *RHSValue = dyn_cast_or_null<BoolValue>(
|
|
|
|
Env.getValue(*RHSNorm, SkipPast::Reference)))
|
|
|
|
return Env.makeIff(*LHSValue, *RHSValue);
|
|
|
|
|
|
|
|
return Env.makeAtomicBoolValue();
|
|
|
|
}
|
|
|
|
|
2021-12-29 11:31:02 +00:00
|
|
|
class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
|
|
|
|
public:
|
2022-02-16 16:47:37 +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-03-11 11:52:53 +00:00
|
|
|
// The CFG does not contain `ParenExpr` as top-level statements in basic
|
|
|
|
// blocks, however sub-expressions can still be of that type.
|
|
|
|
assert(S->getLHS() != nullptr);
|
|
|
|
const Expr *LHS = S->getLHS()->IgnoreParens();
|
|
|
|
assert(LHS != nullptr);
|
|
|
|
|
|
|
|
assert(S->getRHS() != nullptr);
|
|
|
|
const Expr *RHS = S->getRHS()->IgnoreParens();
|
|
|
|
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
|
|
|
|
2022-03-11 11:52:53 +00:00
|
|
|
auto *RHSVal = Env.getValue(*RHS, SkipPast::Reference);
|
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: {
|
2022-03-11 11:52:53 +00:00
|
|
|
BoolValue &LHSVal = getLogicOperatorSubExprValue(*LHS);
|
|
|
|
BoolValue &RHSVal = getLogicOperatorSubExprValue(*RHS);
|
2022-02-16 16:47:37 +00:00
|
|
|
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
|
|
|
if (S->getOpcode() == BO_LAnd)
|
2022-03-11 11:52:53 +00:00
|
|
|
Env.setValue(Loc, Env.makeAnd(LHSVal, RHSVal));
|
2022-02-16 16:47:37 +00:00
|
|
|
else
|
2022-03-11 11:52:53 +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-02-16 16:47:37 +00:00
|
|
|
default:
|
|
|
|
break;
|
2022-01-04 13:47:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisitDeclRefExpr(const DeclRefExpr *S) {
|
|
|
|
assert(S->getDecl() != nullptr);
|
|
|
|
auto *DeclLoc = Env.getStorageLocation(*S->getDecl(), SkipPast::None);
|
|
|
|
if (DeclLoc == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (S->getDecl()->getType()->isReferenceType()) {
|
|
|
|
Env.setStorageLocation(*S, *DeclLoc);
|
|
|
|
} else {
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
auto &Val = Env.takeOwnership(std::make_unique<ReferenceValue>(*DeclLoc));
|
|
|
|
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-01-14 18:27:39 +00:00
|
|
|
auto &Loc = Env.createStorageLocation(D);
|
|
|
|
Env.setStorageLocation(D, Loc);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The CFG does not contain `ParenExpr` as top-level statements in basic
|
|
|
|
// blocks, however sub-expressions can still be of that type.
|
|
|
|
InitExpr = skipExprWithCleanups(D.getInit()->IgnoreParens());
|
|
|
|
assert(InitExpr != nullptr);
|
|
|
|
|
|
|
|
if (D.getType()->isReferenceType()) {
|
|
|
|
// Initializing a reference variable - do not create a reference to
|
|
|
|
// reference.
|
|
|
|
if (auto *InitExprLoc =
|
|
|
|
Env.getStorageLocation(*InitExpr, SkipPast::Reference)) {
|
|
|
|
auto &Val =
|
|
|
|
Env.takeOwnership(std::make_unique<ReferenceValue>(*InitExprLoc));
|
|
|
|
Env.setValue(Loc, Val);
|
|
|
|
} else {
|
|
|
|
// FIXME: The initializer expression must always be assigned a value.
|
|
|
|
// Replace this with an assert when we have sufficient coverage of
|
|
|
|
// language features.
|
2022-01-17 15:17:05 +00:00
|
|
|
if (Value *Val = Env.createValue(D.getType()))
|
|
|
|
Env.setValue(Loc, *Val);
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
2022-01-14 18:27:39 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto *InitExprVal = Env.getValue(*InitExpr, SkipPast::None)) {
|
|
|
|
Env.setValue(Loc, *InitExprVal);
|
|
|
|
} else if (!D.getType()->isStructureOrClassType()) {
|
|
|
|
// FIXME: The initializer expression must always be assigned a value.
|
|
|
|
// Replace this with an assert when we have sufficient coverage of
|
|
|
|
// language features.
|
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
|
|
|
} else {
|
|
|
|
llvm_unreachable("structs and classes must always be assigned values");
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-04 13:47:14 +00:00
|
|
|
void VisitImplicitCastExpr(const ImplicitCastExpr *S) {
|
2022-01-13 13:53:52 +00:00
|
|
|
// The CFG does not contain `ParenExpr` as top-level statements in basic
|
|
|
|
// blocks, however sub-expressions can still be of that type.
|
|
|
|
assert(S->getSubExpr() != nullptr);
|
|
|
|
const Expr *SubExpr = S->getSubExpr()->IgnoreParens();
|
|
|
|
assert(SubExpr != nullptr);
|
|
|
|
|
|
|
|
switch (S->getCastKind()) {
|
|
|
|
case CK_LValueToRValue: {
|
2022-01-04 13:47:14 +00:00
|
|
|
auto *SubExprVal = Env.getValue(*SubExpr, SkipPast::Reference);
|
|
|
|
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-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;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
2022-01-04 13:47:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VisitUnaryOperator(const UnaryOperator *S) {
|
2022-01-17 16:23:24 +00:00
|
|
|
// The CFG does not contain `ParenExpr` as top-level statements in basic
|
|
|
|
// blocks, however sub-expressions can still be of that type.
|
|
|
|
assert(S->getSubExpr() != nullptr);
|
|
|
|
const Expr *SubExpr = S->getSubExpr()->IgnoreParens();
|
|
|
|
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);
|
|
|
|
Env.setValue(Loc, Env.takeOwnership(std::make_unique<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);
|
|
|
|
auto &PointerVal =
|
|
|
|
Env.takeOwnership(std::make_unique<PointerValue>(*PointeeLoc));
|
|
|
|
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();
|
|
|
|
assert(ThisPointeeLoc != nullptr);
|
|
|
|
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
|
|
|
Env.setValue(Loc, Env.takeOwnership(
|
|
|
|
std::make_unique<PointerValue>(*ThisPointeeLoc)));
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
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()) {
|
|
|
|
Env.setStorageLocation(*S, *VarDeclLoc);
|
|
|
|
} else {
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
|
|
|
Env.setValue(Loc, Env.takeOwnership(
|
|
|
|
std::make_unique<ReferenceValue>(*VarDeclLoc)));
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
|
|
|
|
// FIXME: Add support for union types.
|
|
|
|
if (BaseLoc->getType()->isUnionType())
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto &MemberLoc = BaseLoc->getChild(*Member);
|
|
|
|
if (MemberLoc.getType()->isReferenceType()) {
|
|
|
|
Env.setStorageLocation(*S, MemberLoc);
|
|
|
|
} else {
|
|
|
|
auto &Loc = Env.createStorageLocation(*S);
|
|
|
|
Env.setStorageLocation(*S, Loc);
|
|
|
|
Env.setValue(
|
|
|
|
Loc, Env.takeOwnership(std::make_unique<ReferenceValue>(MemberLoc)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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()) {
|
|
|
|
assert(S->getNumArgs() == 1);
|
|
|
|
|
|
|
|
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-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.
|
|
|
|
auto *Arg0Type = Arg0->getType()->getUnqualifiedDesugaredType();
|
|
|
|
auto *Arg1Type = Arg1->getType()->getUnqualifiedDesugaredType();
|
|
|
|
if (Arg0Type != Arg1Type)
|
|
|
|
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) {
|
|
|
|
// The CFG does not contain `ParenExpr` as top-level statements in basic
|
|
|
|
// blocks, however sub-expressions can still be of that type.
|
|
|
|
assert(S->getSubExpr() != nullptr);
|
|
|
|
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.
|
|
|
|
auto *ArgLoc = Env.getStorageLocation(
|
|
|
|
*S->getArg(0)->IgnoreParenImpCasts(), SkipPast::Reference);
|
|
|
|
if (ArgLoc == nullptr)
|
|
|
|
return;
|
|
|
|
Env.setStorageLocation(*S, *ArgLoc);
|
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()) {
|
|
|
|
for (auto IT : llvm::zip(Type->getAsRecordDecl()->fields(), S->inits())) {
|
|
|
|
const FieldDecl *Field = std::get<0>(IT);
|
|
|
|
assert(Field != nullptr);
|
|
|
|
|
|
|
|
const Expr *Init = std::get<1>(IT);
|
|
|
|
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
|
|
|
|
|
|
|
private:
|
2022-03-11 11:52:53 +00:00
|
|
|
BoolValue &getLogicOperatorSubExprValue(const Expr &SubExpr) {
|
|
|
|
// `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.
|
|
|
|
if (const Environment *SubExprEnv = StmtToEnv.getEnvironment(SubExpr)) {
|
|
|
|
if (auto *Val = dyn_cast_or_null<BoolValue>(
|
|
|
|
SubExprEnv->getValue(SubExpr, SkipPast::Reference)))
|
|
|
|
return *Val;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 isn't 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);
|
|
|
|
if (auto *Val = dyn_cast_or_null<BoolValue>(
|
|
|
|
Env.getValue(SubExpr, SkipPast::Reference)))
|
|
|
|
return *Val;
|
|
|
|
|
|
|
|
// If the value of `SubExpr` is still unknown, we create a fresh symbolic
|
|
|
|
// boolean value for it.
|
|
|
|
return Env.makeAtomicBoolValue();
|
|
|
|
}
|
|
|
|
|
2022-02-16 16:47:37 +00:00
|
|
|
const StmtToEnvMap &StmtToEnv;
|
2021-12-29 11:31:02 +00:00
|
|
|
Environment &Env;
|
|
|
|
};
|
|
|
|
|
2022-02-16 16:47:37 +00:00
|
|
|
void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env) {
|
2021-12-29 11:31:02 +00:00
|
|
|
assert(!isa<ParenExpr>(&S));
|
2022-02-16 16:47:37 +00:00
|
|
|
TransferVisitor(StmtToEnv, Env).Visit(&S);
|
2021-12-29 11:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace dataflow
|
|
|
|
} // namespace clang
|