//===-- 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" #include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/OperationKinds.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Basic/OperatorKinds.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Casting.h" #include #include #include namespace clang { namespace dataflow { static const Expr *skipExprWithCleanups(const Expr *E) { if (auto *C = dyn_cast_or_null(E)) return C->getSubExpr(); return E; } class TransferVisitor : public ConstStmtVisitor { public: TransferVisitor(Environment &Env) : Env(Env) {} void VisitBinaryOperator(const BinaryOperator *S) { if (S->getOpcode() == BO_Assign) { // 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); auto *LHSLoc = Env.getStorageLocation(*LHS, SkipPast::Reference); if (LHSLoc == nullptr) return; // The CFG does not contain `ParenExpr` as top-level statements in basic // blocks, however sub-expressions can still be of that type. assert(S->getRHS() != nullptr); const Expr *RHS = S->getRHS()->IgnoreParens(); assert(RHS != nullptr); Value *RHSVal = Env.getValue(*RHS, SkipPast::Reference); if (RHSVal == nullptr) return; // 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); } // FIXME: Add support for BO_EQ, BO_NE. } 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(*DeclLoc)); Env.setStorageLocation(*S, Loc); Env.setValue(Loc, Val); } } void VisitDeclStmt(const DeclStmt *S) { // Group decls are converted into single decls in the CFG so the cast below // is safe. const auto &D = *cast(S->getSingleDecl()); 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. if (Value *Val = Env.createValue(D.getType())) Env.setValue(Loc, *Val); 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(*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. if (Value *Val = Env.createValue(D.getType())) Env.setValue(Loc, *Val); } 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. if (Value *Val = Env.createValue(D.getType())) Env.setValue(Loc, *Val); } else { llvm_unreachable("structs and classes must always be assigned values"); } } void VisitImplicitCastExpr(const ImplicitCastExpr *S) { // 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: { auto *SubExprVal = Env.getValue(*SubExpr, SkipPast::Reference); if (SubExprVal == nullptr) break; auto &ExprLoc = Env.createStorageLocation(*S); Env.setStorageLocation(*S, ExprLoc); Env.setValue(ExprLoc, *SubExprVal); break; } 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: // FIXME: Add support for CK_UserDefinedConversion, // CK_ConstructorConversion, CK_UncheckedDerivedToBase. break; } } void VisitUnaryOperator(const UnaryOperator *S) { // 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: { // Skip past a reference to handle dereference of a dependent pointer. const auto *SubExprVal = cast_or_null( Env.getValue(*SubExpr, SkipPast::Reference)); if (SubExprVal == nullptr) break; auto &Loc = Env.createStorageLocation(*S); Env.setStorageLocation(*S, Loc); Env.setValue(Loc, Env.takeOwnership(std::make_unique( SubExprVal->getPointeeLoc()))); 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(*PointeeLoc)); Env.setStorageLocation(*S, PointerLoc); Env.setValue(PointerLoc, PointerVal); break; } default: // FIXME: Add support for UO_LNot. break; } } 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(*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; // 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( 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(MemberLoc))); } } 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(Env.getThisPointeeStorageLocation()); auto &FieldLoc = ThisLoc.getChild(*Field); Env.setValue(FieldLoc, *InitExprVal); } 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); if (Value *Val = Env.createValue(S->getType())) Env.setValue(Loc, *Val); } 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; Env.setValue(*ObjectLoc, *Val); } } 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); if (Value *Val = Env.createValue(S->getType())) Env.setValue(Loc, *Val); } void VisitCallExpr(const CallExpr *S) { 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); } } 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); } void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *S) { const Expr *SubExpr = S->getSubExpr(); assert(SubExpr != nullptr); auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None); if (SubExprLoc == nullptr) return; Env.setStorageLocation(*S, *SubExprLoc); } void VisitCXXStaticCastExpr(const CXXStaticCastExpr *S) { if (S->getCastKind() == CK_NoOp) { const Expr *SubExpr = S->getSubExpr(); assert(SubExpr != nullptr); auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None); if (SubExprLoc == nullptr) return; Env.setStorageLocation(*S, *SubExprLoc); } } 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); } 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(Val)->setChild(*Field, *InitVal); } } // FIXME: Implement array initialization. } void VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *S) { auto &Loc = Env.createStorageLocation(*S); Env.setStorageLocation(*S, Loc); Env.setValue(Loc, Env.getBoolLiteralValue(S->getValue())); } private: Environment &Env; }; void transfer(const Stmt &S, Environment &Env) { assert(!isa(&S)); TransferVisitor(Env).Visit(&S); } } // namespace dataflow } // namespace clang