diff --git a/clang/include/clang/AST/AbstractSet.h b/clang/include/clang/AST/AbstractSet.h index 274d384f48dc..b3a9a458fcd4 100644 --- a/clang/include/clang/AST/AbstractSet.h +++ b/clang/include/clang/AST/AbstractSet.h @@ -62,8 +62,8 @@ namespace clang { // Returns the NamedDecl, if any, associated with the Representative // expression for this AbstractSet. // This NamedDecl is used by bounds declaration checking to emit - // diagnostics for statements that invalidate the inferred bounds of - // the lvalue expressions in the AbstractSet. + // diagnostics for statements that invalidate the required (declared) + // bounds of the lvalue expressions in the AbstractSet. const NamedDecl *GetDecl() const { if (DeclRefExpr *DRE = dyn_cast(Representative)) return DRE->getDecl(); diff --git a/clang/include/clang/Sema/BoundsDeclExtent.h b/clang/include/clang/Sema/BoundsDeclExtent.h new file mode 100644 index 000000000000..9a08a6ea71fc --- /dev/null +++ b/clang/include/clang/Sema/BoundsDeclExtent.h @@ -0,0 +1,283 @@ +//===== BoundsDeclarationAnalysis.h - Compute flow-senstive bounds decls ====// +// +// The LLVM Compiler Infrastructure +// +// 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 the interface for a dataflow analysis for determiing the +// flow-sensitive bounds declarations that apply to a statement. +//===---------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BOUNDS_DECL_EXTENT_H +#define LLVM_CLANG_BOUNDS_DECL_EXTENT_H + +#include "clang/AST/CanonBounds.h" +#include "clang/AST/ExprUtils.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/Analysis/CFG.h" +#include "clang/Sema/BoundsUtils.h" +#include "clang/Sema/CheckedCAnalysesPrepass.h" +#include "clang/Sema/Sema.h" + +namespace clang { + + // BoundsDeclEnv is an environment: it maps variables to flow-sensitive bounds + // declarations. + using BoundsDeclEnv = llvm::DenseMap; + + //===-------------------------------------------------------------------===// + // Class definition of the BoundsDeclUtil class. This class contains + // helper methods that are used by the BounsdDeclExtent class. + //===-------------------------------------------------------------------===// + + class BoundsDeclUtil { + private: + Sema &SemaRef; + CFG *Cfg; + ASTContext &Ctx; + Lexicographic Lex; + + public: + BoundsDeclUtil(Sema &SemaRef, CFG *Cfg, + ASTContext &Ctx, Lexicographic Lex) : + SemaRef(SemaRef), Cfg(Cfg), Ctx(Ctx), Lex(Lex) {} + + // Compute the set difference of sets A and B. + // @param[in] A is a set. + // @param[in] B is a set. + // @return The set difference of sets A and B. + template + T Difference(T &A, U &B) const; + + // Compute the intersection of sets A and B. + // @param[in] A is a set. + // @param[in] B is a set. + // @return The intersection of sets A and B. + template + T Intersect(T &A, T &B) const; + + // Compute the union of sets A and B. + // @param[in] A is a set. + // @param[in] B is a set. + // @return The union of sets A and B. + template + T Union(T &A, T &B) const; + + // Determine whether sets A and B are equal. Equality is determined by + // comparing each element in the two input sets. + // @param[in] A is a set. + // @param[in] B is a set. + // @return Whether sets A and B are equal. + template + bool IsEqual(T &A, T &B) const; + + }; // end of BoundsDeclUtil class. + + // Note: Template specializations of a class member must be present at the + // same namespace level as the class. So we need to declare template + // specializations outside the class declaration. + + // Template specialization for computing the difference between BoundsDeclEnv + // and VarSetTy. + template<> + BoundsDeclEnv BoundsDeclUtil::Difference( + BoundsDeclEnv &A, VarSetTy &B) const; + + // Template specialization for computing the union of BoundsDeclEnv. + template<> + BoundsDeclEnv BoundsDeclUtil::Union( + BoundsDeclEnv &A, BoundsDeclEnv &B) const; + + // Template specialization for computing the intersection of BoundsDeclEnv. + template<> + BoundsDeclEnv BoundsDeclUtil::Intersect( + BoundsDeclEnv &A, BoundsDeclEnv &B) const; + + // Template specialization for determining the equality of BoundsDeclEnv. + template<> + bool BoundsDeclUtil::IsEqual( + BoundsDeclEnv &A, BoundsDeclEnv &B) const; + +} // end namespace clang + +namespace clang { + //===-------------------------------------------------------------------===// + // Compute the extents of bounds declarations for a functions, for functions + // that have bounds declarations in _Where clausess, following + // the Checked C specification. + //===-------------------------------------------------------------------===// + + class BoundsDeclExtent { + private: + Sema &SemaRef; + CFG *Cfg; + ASTContext &Ctx; + Lexicographic Lex; + llvm::raw_ostream &OS; + BoundsDeclUtil BDUtil; + + // Per-block information + class BlockInfo { + public: + // The In and Out sets for the block. + BoundsDeclEnv In, Out; + + // The Gen set for the block. + BoundsDeclEnv Gen; + + // The Kill set for the block. + VarSetTy Kill; + }; // end of BlockInfo class. + + // Per-block info + std::vector BlockState; + + // A queue of unique ElevatedCFGBlocks involved in the fixpoint of the + // dataflow analysis. + using WorkListTy = QueueSet; + + // All variables that have bounds declared for them in _Where clauses. + VarSetTy InterestingVars; + + public: + BoundsDeclExtent(Sema &SemaRef, CFG *Cfg) : + SemaRef(SemaRef), Cfg(Cfg), Ctx(SemaRef.Context), + Lex(Lexicographic(Ctx, nullptr)), OS(llvm::outs()), + BDUtil(BoundsDeclUtil(SemaRef, Cfg, Ctx, Lex)), + BlockState(Cfg ? Cfg->getNumBlockIDs() : 0) {} + + // Run the dataflow analysis. + // @param[in] FD is the current function. + void compute(FunctionDecl *FD); + + // Does the VarDecl have a bounds declaration in a _Where clause? + bool hasWhereBoundsDecl(const VarDecl *V); + + // Whether the function has any where bounds decls + bool hasAnyWhereBoundsDecls(); + + // Helper class for iterating through the statements of a block and getting + // their in/out sets. + class BlockIterator { + private: + BoundsDeclExtent *ExtentAnalysis; // pointer to the closing class; + const CFGBlock *Block; + + // The CFG element that the forward iterator is at. + size_t CurrentElem; + + // The in/out sets for the statement. + // Invariant: CurrentStmtIn/Out are the in/out set for CFGElement at + // CurrentElem. The invariant is on CFGElements, not statements, to cover + // the case where the iterator is initialized for a block, but the first CFGElement + // is not a statement. + BoundsDeclEnv CurrentStmtIn, CurrentStmtOut; + + public: + BlockIterator(BoundsDeclExtent *BE) : ExtentAnalysis(BE) {} + + // Set the iterator to the first statement of this block, if there + // is a statement. + void setBlock(const CFGBlock *B); + + // Advance the iterator to Stmt. + void advance(const Stmt *Stmt); + + // Get the Out set for the current statement that the iterator is at. + BoundsDeclEnv getStmtOut() const { return CurrentStmtOut; } + + // Get the In set for the current statement that the iterator is at. + BoundsDeclEnv getStmtIn() const { return CurrentStmtIn; } + + private: + void processGenKill(const CFGElement Elem, BoundsDeclEnv &Env); + }; + + // Pretty print the flow-sensitive bounds for all statements + // in the current function. + // @param[in] FD is the current function. + // @param[in] PrintOption == 0: Dump bounds declarations + // PrintOption == 1: Dump dataflow sets. + void dumpFlowSensitiveBoundsDecls(FunctionDecl *FD, int PrintOption); + + // Pretty print a BoundsDeclEnv. + // @param[in] Env maps variables to their bounds declarations. + // @param[in] EmptyMessage is the message displayed if the container is + // empty. + // @param[in] PrintOption == 0: Dump widened bounds + // PrintOption == 1: Dump dataflow sets for bounds widening + void printBoundsEnv(BoundsDeclEnv BoundEnv, int PrintOption) const; + + // Pretty print a set of variables. + // @param[in] VarSet is a set of variables. + // @param[in] EmptyMessage is the message displayed if the container is + // empty. + // @param[in] PrintOption == 0: Dump widened bounds + // PrintOption == 1: Dump dataflow sets for bounds widening + void printVarSet(VarSetTy VarSet, int PrintOption) const; + + // Pretty print a statement. + // @param[in] CurrStmt is the statement to be printed. + void printStmt(const Stmt *CurrStmt) const; + + private: + // Compute Gen and Kill sets for the block. + // @param[in] B is the current CFGBlock. + void computeGenKillSets(const CFGBlock *B); + + // Compute the In set for the block. + // @param[in] B is the current CFGBlock. + // @return Return true if the In set of the block has changed, false + // otherwise. + bool computeInSet(const CFGBlock *B); + + // Compute the Out set for the block. + // @param[in] B is the current CFGBlock. + // @return Return true if the Out set of the block has changed, false + // otherwise. + bool computeOutSet(const CFGBlock *B); + + // Initialize the In set for the entry block. + // @param[in] FD is the current function. + void initEntryInSet(FunctionDecl *FD); + + // Add the successors of the current block to WorkList. + // @param[in] CurrBlock is the current block. + // @param[in] WorkList stores the blocks remaining to be processed for the + // fixpoint computation. + void addSuccsToWorkList(const CFGBlock *CurrBlock, WorkListTy &WorkList); + + // Compute the set of variables that have bounds declarations in _Where + // clauses. + void computeFlowSensitiveVars(FunctionDecl *FD); + + void getEnvFromStmt(Stmt *S, BoundsDeclEnv &Env, VarSetTy *IncludeDecls); + + // Construct an environment for the bounds declared in a declaration. The + // environment is restricted to variables with flow-sensitive bounds + // declarations. + // @param[in] V is a variable declaration + // @param[out] Env is a map of variables to their bounds + // expressions. This field is updated by this function. + void getEnvFromDecl(VarDecl *V, BoundsDeclEnv &Env, + VarSetTy *FlowSensitiveVars); + + // Cosntruct an environemtn for the bounds declared in a where clause. + // @param[in] WC is the where clause. + // @param[out] Env is a map of variables to their bounds declarations. + // expressions. This field is updated by this function. + void getEnvFromWhereClause(WhereClause *WC, BoundsDeclEnv &Env); + + // Order the blocks by block number to get a deterministic iteration order + // for the blocks. + // @return Blocks ordered by block number from higher to lower since block + // numbers decrease from entry to exit. + std::vector getOrderedBlocks() const; + + }; // end of BoundsDeclExtent class. + +} // end namespace clang +#endif diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1aefc6e4b7a4..40eb5bcee25b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5719,6 +5719,7 @@ class Sema final { private: QualType ValidateBoundsExprArgument(Expr *Arg); + BoundsDeclFact *FindWhereClauseBounds(WhereClause *WC, const VarDecl *V); public: ExprResult ActOnNullaryBoundsExpr(SourceLocation BoundKWLoc, @@ -5997,7 +5998,7 @@ class Sema final { // range bounds are attached to the VarDecl D to avoid recomputing the // normalized bounds for D. BoundsExpr *NormalizeBounds(const VarDecl *D); - + // If the BoundsDeclFact F has a byte_count or count bounds expression, // NormalizeBounds expands it to a range bounds expression. The expanded // range bounds are attached to the BoundsDeclFact F to avoid recomputing @@ -6006,10 +6007,13 @@ class Sema final { // Returns the declared bounds for the lvalue expression E. Assignments // to E must satisfy these bounds. After checking a top-level statement, - // the inferred bounds of E must imply these declared bounds. + // the inferred bounds of E must imply these declared bounds. The + // WhereClause may have bounds declaration that override the bounds + // declared at a variable declaration for E, if E is a variable. BoundsExpr *GetLValueDeclaredBounds(Expr *E, CheckedScopeSpecifier CSS = - CheckedScopeSpecifier::CSS_Unchecked); + CheckedScopeSpecifier::CSS_Unchecked, + WhereClause *WC = nullptr); // // Track variables that in-scope bounds declarations depend upon. diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index ed9d2504572a..13b8e8cb011d 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3655,6 +3655,8 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, } } + // TODO: handle type arguments for Checked C generic type applications here. + // Need to support trailing type qualifiers (e.g. "id

const"). // If a type specifier follows, it will be diagnosed elsewhere. continue; diff --git a/clang/lib/Sema/BoundsDeclExtent.cpp b/clang/lib/Sema/BoundsDeclExtent.cpp new file mode 100644 index 000000000000..3e6204780877 --- /dev/null +++ b/clang/lib/Sema/BoundsDeclExtent.cpp @@ -0,0 +1,562 @@ +//===== BoundsWideningAnalysis.h - Dataflow analysis for bounds widening ====// +// +// The LLVM Compiler Infrastructure +// +// 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 implements a dataflow analysis that determines that extent +// of flow-sensitive bounds declarations (bounds declarations in _Where clauses). +// This is a simple forward analysis of reaching definitions. If there are +// conflicting definitions, lower the declared bounds to bottom (unknown bounds). +//===---------------------------------------------------------------------===// + +#include "clang/Sema/BoundsDeclExtent.h" + +namespace clang { + +//===---------------------------------------------------------------------===// +// Implementation of the methods in the BoundsDeclExtent class. +//===---------------------------------------------------------------------===// + +void BoundsDeclExtent::compute(FunctionDecl *FD) { + assert(Cfg && "expected CFG to exist"); + + // Compute the set of variables that have bounds declarations in _Where + // clauses. + computeFlowSensitiveVars(FD); + // The common case - exit early + if (!hasAnyWhereBoundsDecls()) + return; + + for (CFG::iterator I = Cfg->begin(), E = Cfg->end(); I != E; ++I) { + const CFGBlock *B = *I; + if (!B) + continue; + + // Compute Gen and Kill sets for the block. + computeGenKillSets(B); + + // Initialize the In sets for the entry block. + initEntryInSet(FD); + } + + // WorkList stores the blocks that remain to be processed for the fixed point + // computation. WorkList is a queue and maintains a reverse post order + // traversal when we iterate WorkList. + WorkListTy WorkList; + + // Initialize it to the entry block. Then add the remaining blocks to ensure + // that even unreachable blocks are traversed once. + WorkList.append(&Cfg->getEntry()); + + for (CFG::iterator I = Cfg->begin(), E = Cfg->end(); I != E; ++I) + if (*I && *I != &Cfg->getEntry()) + WorkList.append(*I); + + // Iteratively compute in/out sets for blocks until a fixed point is reached. + while (!WorkList.empty()) { + const CFGBlock *B = WorkList.next(); + WorkList.remove(B); + + bool Changed = false; + Changed |= computeInSet(B); + Changed |= computeOutSet(B); + + if (Changed) + addSuccsToWorkList(B, WorkList); + } +} + +void BoundsDeclExtent::computeGenKillSets(const CFGBlock *B) { + BlockInfo &Info = BlockState[B->getBlockID()]; + // Traverse statements in the block and compute Gen and Kill sets for each + // statement. + for (CFGBlock::const_iterator I = B->begin(), E = B->end(); I != E; + ++I) { + CFGElement Elem = *I; + if (Elem.getKind() == CFGElement::LifetimeEnds) { + // Every variable going out of scope is indicated by a LifetimeEnds + // CFGElement. + CFGLifetimeEnds LE = Elem.castAs(); + VarDecl *V = const_cast(LE.getVarDecl()); + if (V && InterestingVars.contains(V)) { + Info.Kill.insert(V); + Info.Gen.erase(V); + } + } else if (Elem.getKind() == CFGElement::Statement) { + if (Stmt *CurrStmt = const_cast(Elem.castAs().getStmt())) + getEnvFromStmt(CurrStmt, Info.Gen, &InterestingVars); + } + } +} + +bool BoundsDeclExtent::computeInSet(const CFGBlock *B) { + if (!B) + return false; + + BlockInfo &Info = BlockState[B->getBlockID()]; + auto OrigIn = Info.In; + + bool first = true; + // Intersect the out sets of the predecessors blocks of B. + // Note: if there are no valid predecessors, EB->In is the empty + // environment and always stays that way. + for (const CFGBlock *PredBlock : B->preds()) { + if (!PredBlock) { + BlockInfo &PredInfo = BlockState[PredBlock->getBlockID()]; + if (first) { + Info.In = PredInfo.Out; + first = false; + } else + Info.In = BDUtil.Intersect(Info.In, PredInfo.Out); + } + } + + // Return true if the In set has changed, false otherwise. + return !BDUtil.IsEqual(OrigIn, Info.In); +} + +bool BoundsDeclExtent::computeOutSet(const CFGBlock *B) { + if (!B) + return false; + + BlockInfo &Info = BlockState[B->getBlockID()]; + auto OrigOut = Info.In; + Info.Out = BDUtil.Difference(Info.In,Info.Kill); + Info.Out = BDUtil.Union(Info.Out, Info.Gen); + + // Return true if the Out set has changed, false otherwise. + return !BDUtil.IsEqual(OrigOut, Info.Out); +} + +void BoundsDeclExtent::initEntryInSet(FunctionDecl *FD) { + // Initialize the In set for the entry block. + CFGBlock &Entry = Cfg->getEntry(); + BlockInfo &Info = BlockState[Entry.getBlockID()]; + for (ParmVarDecl *PD : FD->parameters()) + getEnvFromDecl(PD, Info.In, nullptr); +} + +void BoundsDeclExtent::addSuccsToWorkList(const CFGBlock *CurrBlock, + WorkListTy &WorkList) { + if (!CurrBlock) + return; + + for (const CFGBlock *SuccBlock : CurrBlock->succs()) { + if (SuccBlock) + WorkList.append(SuccBlock); + } +} + +void BoundsDeclExtent::computeFlowSensitiveVars(FunctionDecl *FD) { + BoundsDeclEnv Env; + for (ParmVarDecl *PD : FD->parameters()) + getEnvFromDecl(PD, Env, nullptr); + + for (CFG::const_iterator I = Cfg->begin(), E = Cfg->end(); I != E; ++I) { + CFGBlock *B = *I; + for (CFGBlock::const_iterator SI = B->begin(), + SE = B->end(); + SI != SE; ++SI) { + CFGElement Elem = *SI; + if (Elem.getKind() == CFGElement::Statement) { + Stmt *CurrStmt = const_cast(Elem.castAs().getStmt()); + if (CurrStmt) + getEnvFromStmt(CurrStmt, Env, nullptr); + } + } + } + + // Convert domain(Env) to a set. + InterestingVars.clear(); + for (auto VarBoundsPair : Env) + InterestingVars.insert(VarBoundsPair.first); +} + +// If FlowSensitiveVars is non-null, add bounds declared at declarations of variables +// that are in FlowSensitiveVars to Env. +void BoundsDeclExtent::getEnvFromStmt(Stmt *S, BoundsDeclEnv &Env, + VarSetTy *FlowSensitiveVars) { + if (auto *DS = dyn_cast(S)) { + for (Decl *D : DS->decls()) + if (auto *V = dyn_cast(D)) + getEnvFromDecl(V, Env, FlowSensitiveVars); + } else if (auto *VS = dyn_cast(S)) + getEnvFromWhereClause(VS->getWhereClause(), Env); + else if (auto *NS = dyn_cast(S)) + // TODO: Currently, a null statement does not occur in the list of + // statements of a block. + getEnvFromWhereClause(NS->getWhereClause(), Env); +} + + +void BoundsDeclExtent::getEnvFromDecl(VarDecl *V, BoundsDeclEnv &Env, + VarSetTy *FlowSensitiveVars) { + if (FlowSensitiveVars && FlowSensitiveVars->contains(V) && V->hasBoundsExpr()) + Env[V] = V->getBoundsExpr(); + getEnvFromWhereClause(V->getWhereClause(), Env); +} + +void BoundsDeclExtent::getEnvFromWhereClause(WhereClause *WC, BoundsDeclEnv &Env) { + if (!WC) + return; + + for (auto *Fact : WC->getFacts()) { + if (auto *F = dyn_cast(Fact)) { + VarDecl *V = F->getVarDecl(); + Env[V] = F->getBoundsExpr(); + } + } +} + +bool BoundsDeclExtent::hasAnyWhereBoundsDecls() { + return InterestingVars.size() > 0; +} + +bool BoundsDeclExtent::hasWhereBoundsDecl(const VarDecl *V) { + return InterestingVars.contains(V); +} + +void BoundsDeclExtent::BlockIterator::setBlock(const CFGBlock *B) { + if (!B) + return; + + Block = B; + CurrentElem = 0; + CurrentStmtIn = ExtentAnalysis->BlockState[B->getBlockID()].In; + CurrentStmtOut = CurrentStmtIn; + if (B->size() >= 1) + processGenKill((*B)[0], CurrentStmtOut); +} + +void BoundsDeclExtent::BlockIterator::processGenKill(CFGElement Elem, BoundsDeclEnv &Env) { + if (Elem.getKind() == CFGElement::LifetimeEnds) { + // Kill + CFGLifetimeEnds LE = Elem.castAs(); + VarDecl *V = const_cast(LE.getVarDecl()); + if (V && ExtentAnalysis->InterestingVars.contains(V)) + Env.erase(V); + else if (Elem.getKind() == CFGElement::Statement) { + // Gen + if (Stmt *CurrStmt = const_cast(Elem.castAs().getStmt())) + ExtentAnalysis->getEnvFromStmt(CurrStmt, Env, &ExtentAnalysis->InterestingVars); + } + } +} + +void BoundsDeclExtent::BlockIterator::advance(const Stmt *NextStmt) { + while (CurrentElem < Block->size()) { + CFGElement Elem = (*Block)[CurrentElem]; + if (Elem.getKind() == CFGElement::Statement) { + if (const Stmt *CurrStmt = Elem.castAs().getStmt()) + if (CurrStmt == NextStmt) + return; + } + CurrentElem++; + CurrentStmtIn = CurrentStmtOut; + processGenKill(Elem, CurrentStmtOut); + } + llvm_unreachable("Statement not found"); +} + +void BoundsDeclExtent::printVarSet(VarSetTy VarSet, + int PrintOption) const { + if (VarSet.size() == 0) { + if (PrintOption == 0) + OS << "\n"; + else + OS << " {}\n"; + return; + } + + // A VarSetTy has const iterator. So we cannot simply sort a VarSetTy and + // need to copy the elements to a vector to sort. + std::vector Vars(VarSet.begin(), VarSet.end()); + + llvm::sort(Vars.begin(), Vars.end(), + [](const VarDecl *A, const VarDecl *B) { + return A->getQualifiedNameAsString().compare( + B->getQualifiedNameAsString()) < 0; + }); + + for (const VarDecl *V : Vars) + OS << " " << V->getQualifiedNameAsString() << "\n"; +} + +void BoundsDeclExtent::printBoundsEnv(BoundsDeclEnv BoundsMap, + int PrintOption) const { + if (BoundsMap.size() == 0) { + if (PrintOption == 0) + OS << "\n"; + else + OS << " {}\n"; + return; + } + + std::vector Vars; + for (auto VarBoundsPair : BoundsMap) + Vars.push_back(VarBoundsPair.first); + + llvm::sort(Vars.begin(), Vars.end(), + [](const VarDecl *A, const VarDecl *B) { + return A->getQualifiedNameAsString().compare( + B->getQualifiedNameAsString()) < 0; + }); + + for (const VarDecl *V : Vars) { + OS << " " << V->getQualifiedNameAsString() << ": "; + + const BoundsExpr *Bounds = BoundsMap[V]; + if (!Bounds) + OS << "bottom"; + else + Bounds->printPretty(OS, nullptr, Ctx.getPrintingPolicy()); + } +} + +void BoundsDeclExtent::printStmt(const Stmt *CurrStmt) const { + if (!CurrStmt) { + OS << "\n"; + return; + } + + std::string Str; + llvm::raw_string_ostream SS(Str); + CurrStmt->printPretty(SS, nullptr, Ctx.getPrintingPolicy()); + + OS << SS.str(); + if (SS.str().back() != '\n') + OS << "\n"; +} + +void BoundsDeclExtent::dumpFlowSensitiveBoundsDecls(FunctionDecl *FD, + int PrintOption) { + BlockIterator Iterator(this); + + OS << "\n--------------------------------------"; + // Print the function name. + OS << "\nFunction: " << FD->getName(); + + for (const CFGBlock *CurrBlock : getOrderedBlocks()) { + unsigned BlockID = CurrBlock->getBlockID(); + + // Print the current block number. + OS << "\nBlock: B" << BlockID; + if (CurrBlock == &Cfg->getEntry()) + OS << " (Entry)"; + + // Print the predecessor blocks of the current block. + OS << ", Pred: "; + for (const CFGBlock *PredBlock : CurrBlock->preds()) { + if (PredBlock) + OS << "B" << PredBlock->getBlockID() << ", "; + } + + // Print the successor blocks of the current block. + OS << "Succ: "; + for (const CFGBlock *SuccBlock : CurrBlock->succs()) { + if (SuccBlock) { + OS << "B" << SuccBlock->getBlockID(); + + if (SuccBlock != *(CurrBlock->succs().end() - 1)) + OS << ", "; + } + } + + BlockInfo &Info = BlockState[BlockID]; + + if (PrintOption == 1) { + // Print the In set for the block. + OS << "\n In:\n"; + printBoundsEnv(Info.In, PrintOption); + + // Print the Out set for the block. + OS << " Out:\n"; + printBoundsEnv(Info.Out, PrintOption); + } + + if (CurrBlock->empty()) { + OS << "\n"; + continue; + } + + Iterator.setBlock(CurrBlock); + for (CFGElement Elem : *CurrBlock) { + if (Elem.getKind() != CFGElement::Statement) + continue; + const Stmt *CurrStmt = Elem.castAs().getStmt(); + if (!CurrStmt) + continue; + Iterator.advance(CurrStmt); + + if (PrintOption == 0) { + OS << "\n Flow-sensitive bounds before stmt: "; + } else if (PrintOption == 1) { + OS << "\n Stmt: "; + } + + // Print the current statement. + printStmt(CurrStmt); + + if (PrintOption == 0) { + // Print widened bounds before the statement. + printBoundsEnv(Iterator.getStmtIn(), PrintOption); + + } else if (PrintOption == 1) { + // Print the In set for the statement. + OS << " In:\n"; + printBoundsEnv(Iterator.getStmtIn(), PrintOption); + + // Print the Out set for the statement. + OS << " Out:\n"; + printBoundsEnv(Iterator.getStmtOut(), PrintOption); + } + } + } +} + +std::vector BoundsDeclExtent::getOrderedBlocks() const { + size_t NumBlockIds = Cfg->getNumBlockIDs(); + std::vector Result(Cfg->getNumBlockIDs()); + // We order the CFG blocks based on block ID. Block IDs decrease from entry + // to exit. So we sort in the reverse order. + for (CFG::const_iterator I = Cfg->begin(), E = Cfg->end(); I != E; ++I) { + const CFGBlock *Block = *I; + Result[NumBlockIds - Block->getBlockID()] = Block; + } + return Result; +} + +// end of methods for the BoundsDeclExtent class. + +//===---------------------------------------------------------------------===// +// Implementation of the methods in the BoundsDeclUtil class. This class +// contains helper methods that are used by the BoundsDeclExtent class to +// perform the dataflow analysis. +//===---------------------------------------------------------------------===// + +// Common templated set operation functions. +template +T BoundsDeclUtil::Difference(T &A, U &B) const { + if (!A.size() || !B.size()) + return A; + + auto CopyA = A; + for (auto Item : A) { + if (B.count(Item)) + CopyA.erase(Item); + } + return CopyA; +} + +template +T BoundsDeclUtil::Union(T &A, T &B) const { + auto CopyA = A; + for (auto Item : B) + CopyA.insert(Item); + + return CopyA; +} + +template +T BoundsDeclUtil::Intersect(T &A, T &B) const { + if (!A.size() || !B.size()) + return T(); + + auto CopyA = A; + for (auto Item : A) { + if (!B.count(Item)) + CopyA.erase(Item); + } + return CopyA; +} + +template +bool BoundsDeclUtil::IsEqual(T &A, T &B) const { + return A.size() == B.size() && + A.size() == Intersect(A, B).size(); +} + +// Template specializations of common set operation functions. +template<> +BoundsDeclEnv BoundsDeclUtil::Difference( + BoundsDeclEnv &A, VarSetTy &B) const { + + if (!A.size() || !B.size()) + return A; + + auto CopyA = A; + for (auto VarBoundsPair : A) { + if (B.count(VarBoundsPair.first)) + CopyA.erase(VarBoundsPair.first); + } + return CopyA; +} + +template<> +BoundsDeclEnv BoundsDeclUtil::Intersect( + BoundsDeclEnv &A, BoundsDeclEnv &B) const { + + if (!A.size() || !B.size()) + return BoundsDeclEnv(); + + auto CopyA = A; + for (auto VarBoundsPair : B) { + const VarDecl *V = VarBoundsPair.first; + auto VarBoundsIt = CopyA.find(V); + if (VarBoundsIt == CopyA.end()) { + CopyA.erase(V); + continue; + } + + BoundsExpr *BoundsA = VarBoundsIt->second; + BoundsExpr *BoundsB = VarBoundsPair.second; + + if (Lex.CompareExprSemantically(BoundsA, BoundsB)) + CopyA[V] = BoundsA; + else + CopyA[V] = Ctx.getPrebuiltBoundsUnknown(); + } + return CopyA; +} + +template<> +BoundsDeclEnv BoundsDeclUtil::Union( + BoundsDeclEnv &A, BoundsDeclEnv &B) const { + + auto CopyA = A; + for (auto VarBoundsPair : B) + CopyA[VarBoundsPair.first] = VarBoundsPair.second; + + return CopyA; +} + +template<> +bool BoundsDeclUtil::IsEqual( + BoundsDeclEnv &A, BoundsDeclEnv &B) const { + + if (A.size() != B.size()) + return false; + + auto CopyA = A; + for (auto VarBoundsPair : B) { + const VarDecl *V = VarBoundsPair.first; + + auto VarBoundsIt = CopyA.find(V); + if (VarBoundsIt == CopyA.end()) + return false; + + const BoundsExpr *BoundsA = VarBoundsIt->second; + const BoundsExpr *BoundsB = VarBoundsPair.second; + + if (!Lex.CompareExprSemantically(BoundsA, BoundsB)) + return false; + } + return true; +} +// end of methods for the BoundsDeclUtil class. + +} // end namespace clang diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 69eabb68caec..fc7d189364a5 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -14,6 +14,7 @@ add_clang_library(clangSema AvailableFactsAnalysis.cpp BoundsUtils.cpp BoundsWideningAnalysis.cpp + BoundsDeclExtent.cpp CheckedCAlias.cpp CheckedCAnalysesPrepass.cpp CheckedCInterop.cpp diff --git a/clang/lib/Sema/SemaBounds.cpp b/clang/lib/Sema/SemaBounds.cpp index bc2712e3fe74..e6afc2346267 100644 --- a/clang/lib/Sema/SemaBounds.cpp +++ b/clang/lib/Sema/SemaBounds.cpp @@ -47,6 +47,7 @@ #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Sema/AvailableFactsAnalysis.h" #include "clang/Sema/BoundsUtils.h" +#include "clang/Sema/BoundsDeclExtent.h" #include "clang/Sema/BoundsWideningAnalysis.h" #include "clang/Sema/CheckedCAnalysesPrepass.h" #include "llvm/ADT/SmallBitVector.h" @@ -681,6 +682,9 @@ namespace { ASTContext &Context; std::pair &Facts; + // Flow-sensitive bounds declaration information. + BoundsDeclExtent FlowSensitiveBoundsDecls; + // Having a BoundsWideningAnalysis object here allows us to easily invoke // methods for bounds widening and get back the widened bounds info needed // for bounds inference/checking. @@ -2688,6 +2692,7 @@ namespace { ReturnBounds(nullptr), Context(SemaRef.Context), Facts(Facts), + FlowSensitiveBoundsDecls(BoundsDeclExtent(SemaRef,Cfg)), BoundsWideningAnalyzer(BoundsWideningAnalysis(SemaRef, Cfg, Info.BoundsVarsLower, Info.BoundsVarsUpper)), @@ -2716,6 +2721,7 @@ namespace { ReturnBounds(nullptr), Context(SemaRef.Context), Facts(Facts), + FlowSensitiveBoundsDecls(BoundsDeclExtent(SemaRef, Cfg)), BoundsWideningAnalyzer(BoundsWideningAnalysis(SemaRef, nullptr, Info.BoundsVarsLower, Info.BoundsVarsUpper)), @@ -2944,6 +2950,9 @@ namespace { BoundsContextTy InitialObservedBounds; bool InBundledBlock = false; + // Determine the extent of flow-sensitive bounds declarations + FlowSensitiveBoundsDecls.compute(FunctionDeclaration); + // Run the bounds widening analysis on this function. BoundsWideningAnalyzer.WidenBounds(FD, NestedElements); if (S.getLangOpts().DumpWidenedBounds) @@ -4491,11 +4500,24 @@ namespace { BoundsMapTy BoundsWidenedAndNotKilled = BoundsWideningAnalyzer.GetBoundsWidenedAndNotKilled(Block, S); + // If the current statement has a WhereClause, it may override the + // declared bounds for a variable. + WhereClause *WC = nullptr; + if (const NullStmt *NS = dyn_cast(S)) + WC = NS->getWhereClause(); + else if (const ValueStmt *VS = dyn_cast(S)) + WC = VS->getWhereClause(); + else if (const DeclStmt* DS = dyn_cast(S)) { + if (DS->isSingleDecl()) + if (const VarDecl *VD = dyn_cast(DS->getSingleDecl())) + WC = VD->getWhereClause(); + } + for (auto const &Pair : State.ObservedBounds) { const AbstractSet *A = Pair.first; BoundsExpr *ObservedBounds = Pair.second; BoundsExpr *DeclaredBounds = - this->S.GetLValueDeclaredBounds(A->GetRepresentative(), CSS); + this->S.GetLValueDeclaredBounds(A->GetRepresentative(), CSS, WC); if (!DeclaredBounds || DeclaredBounds->isUnknown()) continue; if (SkipBoundsValidation(A, CSS, State)) @@ -4503,6 +4525,9 @@ namespace { if (ObservedBounds->isUnknown()) DiagnoseUnknownObservedBounds(S, A, DeclaredBounds, State); else { + // TODO: this seems incorrect. It is suppressing warning messages + // for statements with widened bounds that have invertible assignments + // affecting bounds. // If the lvalue expressions in A are variables represented by a // declaration Var, we should issue diagnostics for observed bounds // if Var is not in the set BoundsWidenedAndKilled which represents @@ -4658,12 +4683,13 @@ namespace { SourceRange SrcRange = St->getSourceRange(); auto BDCType = Sema::BoundsDeclarationCheck::BDC_Statement; - // For a declaration, show the diagnostic message that starts at the + // For a declaration of V, show the diagnostic message that starts at the // location of v rather than the beginning of St and return. If the // message starts at the beginning of a declaration T v = e, then extra // diagnostics may be emitted for T. SourceLocation Loc = St->getBeginLoc(); - if (V && isa(St)) { + DeclStmt *DS = dyn_cast(St); + if (V && DS && DS->isSingleDecl() && DS->getSingleDecl() == V) { Loc = V->getLocation(); BDCType = Sema::BoundsDeclarationCheck::BDC_Initialization; S.Diag(Loc, DiagId) << BDCType << A->GetRepresentative() @@ -4790,7 +4816,7 @@ namespace { // UpdateAfterAssignment updates the checking state after the lvalue // expression LValue is updated in an assignment E of the form - // LValue = Src. + // LValue = Src.I[ // UpdateAfterAssignment also returns updated bounds for Src. // // TargetBounds are the bounds for the target of LValue. @@ -4829,7 +4855,7 @@ namespace { Expr *OriginalValue = GetOriginalValue(LValue, Target, Src, State.EquivExprs, OriginalValueUsesLValue); - // If LValue has target bounds, get the AbstractSet that contains LValue. + // Get the AbstractSet that contains LValue. // LValueAbstractSet will be used in UpdateBoundsAfterAssignment to // record the observed bounds of all lvalue expressions in this set. // If LValue belongs to an LValueAbstractSet, then the rvalue expression @@ -4848,9 +4874,16 @@ namespace { // the bounds context after this assignment, the target bounds for arr[0] // are bounds(*arr, *arr + 0). Therefore, (temporary) equality should be // recorded between *arr and "xyz", rather than between arr[0] and "xyz". + const AbstractSet *LValueAbstractSet = nullptr; Expr *EqualityTarget = Target; - if (TargetBounds && !TargetBounds->isUnknown()) { + bool IsFlowSensitive = false; + if (DeclRefExpr *DR = dyn_cast(LValue)) + if (VarDecl *V = dyn_cast(DR->getDecl())) + if (FlowSensitiveBoundsDecls.hasWhereBoundsDecl(V)) + IsFlowSensitive = true; + + if ((TargetBounds && !TargetBounds->isUnknown()) || IsFlowSensitive) { LValueAbstractSet = AbstractSetMgr.GetOrCreateAbstractSet(LValue); Expr *Rep = LValueAbstractSet->GetRepresentative(); EqualityTarget = @@ -6846,13 +6879,30 @@ BoundsExpr *Sema::NormalizeBounds(const BoundsDeclFact *F) { return ExpandedBounds; } +BoundsDeclFact *Sema::FindWhereClauseBounds(WhereClause *WC, const VarDecl *V) { + if (!WC) + return nullptr; + + for (WhereClauseFact *Fact : WC->getFacts()) { + if (BoundsDeclFact *F = dyn_cast(Fact)) { + if (F->getVarDecl() == V) + return F; + } + } + + return nullptr; +} + // Returns the declared bounds for the lvalue expression E. Assignments // to E must satisfy these bounds. After checking a top-level statement, // the inferred bounds of E must imply these declared bounds. -BoundsExpr *Sema::GetLValueDeclaredBounds(Expr *E, CheckedScopeSpecifier CSS) { +BoundsExpr *Sema::GetLValueDeclaredBounds(Expr *E, CheckedScopeSpecifier CSS, WhereClause *WC) { if (DeclRefExpr *DRE = VariableUtil::GetLValueVariable(*this, E)) { - if (const VarDecl *V = dyn_cast_or_null(DRE->getDecl())) - return NormalizeBounds(V); + if (VarDecl *V = dyn_cast_or_null(DRE->getDecl())) + if (const BoundsDeclFact *F = FindWhereClauseBounds(WC, V)) + return NormalizeBounds(F); + else + return NormalizeBounds(V); } PrepassInfo Info;