diff --git a/clang/include/clang/AST/CanonBounds.h b/clang/include/clang/AST/CanonBounds.h index ad3072005885..9d165d5eab9b 100644 --- a/clang/include/clang/AST/CanonBounds.h +++ b/clang/include/clang/AST/CanonBounds.h @@ -131,6 +131,12 @@ namespace clang { bool GetDerefOffset(const Expr *UpperExpr, const Expr *DerefExpr, llvm::APSInt &Offset) const; + /// \brief Get the integer difference between two expressions. + /// The boolean return value indicates whether the two expressions are + /// comparable. + bool GetExprIntDiff(const Expr *E1, const Expr *E2, + llvm::APSInt &Offset) const; + /// \brief Compare declarations that may be used by expressions or /// or types. Result CompareDecl(const NamedDecl *D1, const NamedDecl *D2) const; diff --git a/clang/include/clang/AST/PreorderAST.h b/clang/include/clang/AST/PreorderAST.h index a4262c8d4de8..fa878aa88166 100644 --- a/clang/include/clang/AST/PreorderAST.h +++ b/clang/include/clang/AST/PreorderAST.h @@ -302,6 +302,16 @@ namespace clang { bool GetDerefOffset(Node *UpperExpr, Node *DerefExpr, llvm::APSInt &Offset); + // Get the integer difference between expressions. + // @param[in] E1 is the first expression. + // @param[in] E2 is the second expression. + // @param[out] Offset is the integer difference between E1 and E2. + // @return Returns a boolean indicating whether the expressions are + // comparable. True means the expressions are comparable and their integer + // difference is present in the "Offset" parameter. False means the + // expressions are not comparable. + bool GetExprIntDiff(Node *E1, Node *E2, llvm::APSInt &Offset); + // Set Error in case an error occurs during transformation of the AST. void SetError() { Error = true; } @@ -328,6 +338,18 @@ namespace clang { return GetDerefOffset(/*UpperExpr*/ Root, /*DerefExpr*/ P.Root, Offset); } + // Get the integer difference between two expressions represented as + // preorder ASTs. This function is intended to be called from outside this + // class. + // @param[in] this is the first AST. + // @param[in] P is the second AST. + // @param[out] Offset is the integer difference. + // @return Returns a bool indicating whether the two expressions are + // comparable. + bool GetExprIntDiff(PreorderAST &P, llvm::APSInt &Offset) { + return GetExprIntDiff(Root, P.Root, Offset); + } + // Lexicographically compare the two ASTs. This is intended to be called // from outside this class and invokes Compare on the root nodes of the two // ASTs to recursively compare the AST nodes. diff --git a/clang/include/clang/Sema/BoundsAnalysis.h b/clang/include/clang/Sema/BoundsAnalysis.h deleted file mode 100644 index 65e5a9f3c320..000000000000 --- a/clang/include/clang/Sema/BoundsAnalysis.h +++ /dev/null @@ -1,354 +0,0 @@ -//===---------- BoundsAnalysis.h - Dataflow 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 defines the interface for a dataflow analysis for bounds -// widening. -// -//===---------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_BOUNDS_ANALYSIS_H -#define LLVM_CLANG_BOUNDS_ANALYSIS_H - -#include "clang/AST/CanonBounds.h" -#include "clang/AST/ExprUtils.h" -#include "clang/Analysis/Analyses/PostOrderCFGView.h" -#include "clang/Sema/Sema.h" - -namespace clang { - // Note: We use the shorthand "ntptr" to denote _Nt_array_ptr. We extract the - // declaration of an ntptr as a VarDecl from a DeclRefExpr. - - // BoundsMapTy denotes the unsigned integer offset I by which the bounds of - // an ntptr should be widened. Given VarDecl V with declared bounds as bounds - // (low, high), the bounds of V should be widened to bounds (low, high + I). - using BoundsMapTy = llvm::MapVector; - - // BoundsExprMapTy denotes the widened bounds expression of an ntptr. Given - // VarDecl V with declared bounds (low, high), and an unsigned integer offset - // I by which the declared upper bound should be widened, the widened bounds - // of V are denoted as bounds (low, high + I). - using BoundsExprMapTy = llvm::MapVector; - - // For each edge B1->B2, EdgeBoundsTy denotes the Gen and Out sets. - using EdgeBoundsTy = llvm::DenseMap; - - // DeclSetTy denotes a set of VarDecls. - using DeclSetTy = llvm::DenseSet; - - // A mapping of VarDecl V to all the variables occuring in its bounds - // expression. This is used to compute Kill sets. An assignment to any - // variable occuring in the bounds expression of an ntptr kills any computed - // bounds for that ntptr in that block. - using BoundsVarTy = llvm::DenseMap; - - // OrderedBlocksTy denotes blocks ordered by block numbers. This is useful - // for printing the blocks in a deterministic order. - using OrderedBlocksTy = std::vector; - - // ExprIntPairTy denotes a pair of an expression and an integer constant. - // This is used as a return type when an expression is split into a base and - // an offset. - using ExprIntPairTy = std::pair; - - // StmtDeclSetTy denotes a mapping between a Stmt and a set of VarDecls. This - // is used to store the Kill set for a block. - // A VarDecl V is killed in a Stmt S if: - // 1. V is assigned to in S, or - // 2. any variable used in the bounds expr of V is assigned to in S. - using StmtDeclSetTy = llvm::DenseMap; - - // StmtSet denotes a set of Stmts. - using StmtSet = llvm::SmallPtrSet; - - // The BoundsAnalysis class represents the dataflow analysis for bounds - // widening. The In, Out, Gen and Kill sets used in the dataflow analysis are - // members of this class. It also has methods that act on these sets to - // perform the actual analysis. - class BoundsAnalysis { - private: - Sema &S; - CFG *Cfg; - ASTContext &Ctx; - Lexicographic Lex; - llvm::raw_ostream &OS; - - class ElevatedCFGBlock { - public: - const CFGBlock *Block; - // The In set for the block. - BoundsMapTy In; - // The Gen and Out sets for the block. - EdgeBoundsTy Gen, Out; - // The Kill set for the block. - StmtDeclSetTy Kill; - - // To compute In[B] we compute the intersection of Out[B*->B], where B* - // are all preds of B. When there is a back edge from block B' to B (for - // example in loops), the Out set for block B' will be empty when we - // first enter B. As a result, the intersection operation would always - // result in an empty In set for B. - - // So to handle this, we consider the In and Out sets for all blocks to - // have a default value of "Top" which indicates a set of all members of - // the Gen set. In this way we ensure that the intersection does not - // result in an empty set even if the Out set for a block is actually - // empty. - - // But we also need to handle the case where there is an unconditional - // jump into a block (for example, as a result of a goto). In this case, - // we cannot widen the bounds because we would not have checked for the - // ptr dereference. So in this case we want the intersection to result in - // an empty set. - - // So we mark the In and Out sets of the Entry block as "empty". - // IsInSetEmpty and IsOutSetEmpty indicate whether the In and Out sets - // for a block have been marked as "empty". - bool IsInSetEmpty; - llvm::DenseMap IsOutSetEmpty; - - ElevatedCFGBlock(const CFGBlock *B) : Block(B) {} - }; - - // BlockMapTy stores the mapping from CFGBlocks to ElevatedCFGBlocks. - using BlockMapTy = llvm::DenseMap; - // A queue of unique ElevatedCFGBlocks to run the dataflow analysis on. - using WorkListTy = QueueSet; - - // BlockMap is the map from CFGBlock to ElevatedCFGBlock. Used - // to lookup ElevatedCFGBlock from CFGBlock. - BlockMapTy BlockMap; - - // The mapping of all ntptrs in the function and all variables occurring in - // the bounds expr for each ntptr. - BoundsVarTy NtPtrsInScope; - - // To compute In[B] we compute the intersection of Out[B*->B], where B* are - // all preds of B. When there is a back edge from block B' to B (for - // example in loops), the Out set for block B' will be empty when we first - // enter B. As a result, the intersection operation would always result in - // an empty In set for B. - - // So to handle this, we consider the In and Out sets for all blocks to - // have a default value of "Top" which indicates a set of all members of - // the Gen set. In this way we ensure that the intersection does not result - // in an empty set even if the Out set for a block is actually empty. - - // But we also need to handle the case where there is an unconditional jump - // into a block (for example, as a result of a goto). In this case, we - // cannot widen the bounds because we would not have checked for the ptr - // dereference. So in this case we want the intersection to result in an - // empty set. - - // So we initialize the In and Out sets of all blocks, except the Entry - // block, as "Top". - BoundsMapTy Top; - - public: - BoundsAnalysis(Sema &S, CFG *Cfg) : - S(S), Cfg(Cfg), Ctx(S.Context), - Lex(Lexicographic(Ctx, nullptr)), OS(llvm::outs()) {} - - // Run the dataflow analysis to widen bounds for ntptr's. - // @param[in] FD is the current function. - // @param[in] NestedStmts is a set of top-level statements that are - // nested in another top-level statement. - void WidenBounds(FunctionDecl *FD, StmtSet NestedStmts); - - // Get a mapping of variables to the offsets by which their bounds should - // be widened in block B. - // @param[in] B is the block for which the widened bounds offsets are - // needed. - // @return A mapping of variables to the offsets by which their bounds - // should be widened in block B. - BoundsMapTy GetWidenedBoundsOffsets(const CFGBlock *B); - - // Get the set of variables whose bounds are widened but not killed in - // block B. This function is invoked from SemaBounds.cpp and is used to - // control the emission of diagnostics. - // @param[in] B is the block for which the widened bounds are checked. - // @param[in] St is the statement for which the killed bounds are checked. - // @return A set of variables whose bounds are widened and not killed. - DeclSetTy GetBoundsWidenedAndNotKilled(const CFGBlock *B, const Stmt *St); - - // Get the set of variables killed by the statement St in the block B. - // Note: This method is intended to be invoked from CheckBoundsDeclaration - // or a similar place which does bounds inference/checking. - // @param[in] B is the current CFGBlock. - // @param[in] St is the current statement in block B. - // return A set of variables whose bounds are killed by the statement St in - // block B. - DeclSetTy GetKilledBounds(const CFGBlock *B, const Stmt *St); - - // Get a mapping of variables to their widened bounds expressions in block - // B. This function calls GetWidenedBoundsOffsets to get the offsets by - // which the bounds of each variable should be widened. It then applies - // these offsets to the declared upper bounds and creates an updated - // BoundsExpr for each variable in the block. - // @param[in] B is the block for which the widened bounds expressions are - // needed. - // @return A mapping of variables to their widened bounds expressions in - // block B. - BoundsExprMapTy GetWidenedBounds(const CFGBlock *B); - - // Pretty print the widen bounds analysis. - // printing. - // @param[in] FD is the current function. - void DumpWidenedBounds(FunctionDecl *FD); - - // Get the Kill set for the current block. The Kill set is a mapping of - // Stmts to variables whose bounds are killed by each Stmt in the block. - // Note: This method is intended to be invoked from CheckBoundsDeclaration - // or a similar place which does bounds inference/checking. - // @param[in] B is the current CFGBlock. - // return A mapping of Stmts to variables whose bounds are killed by the - // Stmt. - StmtDeclSetTy GetKilledBounds(const clang::CFGBlock *B); - - private: - // Compute Gen set for each edge in the CFG. If there is an edge B1->B2 and - // the edge condition is of the form "if (*(p + i))" then Gen[B1] = {B2, - // p:i} . The actual computation of i is done in FillGenSet. - void ComputeGenSets(); - - // Compute Kill set for each block in BlockMap. For a block B, if a - // variable V is assigned to in B by Stmt S, then the pair S:V is added to - // the Kill set for the block. - // @param[in] NestedStmts is a set of top-level statements that are - // nested in another top-level statement. - void ComputeKillSets(StmtSet NestedStmts); - - // Compute In set for each block in BlockMap. In[B1] = n Out[B*->B1], where - // B* are all preds of B1. - // @param[in] EB is the block to compute the In set for. - void ComputeInSets(ElevatedCFGBlock *EB); - - // Compute Out set for each outgoing edge of EB. If the Out set on any edge - // of EB changes then the successor of EB on that edge is added to - // Worklist. - // @param[in] EB is the block to compute the Out set for. - // @param[out] The successors of EB are added to WorkList if the Out set of - // EB changes. - void ComputeOutSets(ElevatedCFGBlock *EB, WorkListTy &Worklist); - - // Perform checks, handles conditional expressions, extracts the - // ntptr offset and fills the Gen set for the edge. - // @param[in] E is the expr possibly containing the deref of an ntptr. If E - // contains a pointer deref, the Gen set for the edge EB->SuccEB is - // updated. - // @param[in] Source block for the edge for which the Gen set is updated. - // @param[in] Dest block for the edge for which the Gen set is updated. - void FillGenSet(Expr *E, ElevatedCFGBlock *EB, ElevatedCFGBlock *SuccEB); - - // Uniformize the expr, fill Gen set for the edge EB->SuccEB. - // @param[in] DerefExpr is an ntptr dereference or array subscript expr. - // @param[in] Source block for the edge for which the Gen set is updated. - // @param[in] Dest block for the edge for which the Gen set is updated. - void FillGenSetForEdge(const Expr *DerefExpr, - ElevatedCFGBlock *EB, - ElevatedCFGBlock *SuccEB); - - // Collect all variables used in bounds expr E. - // @param[in] E represents the bounds expr for an ntptr. - // @param[out] BoundsVars is a set of all variables used in the bounds expr - // E. - void CollectBoundsVars(const Expr *E, DeclSetTy &BoundsVars); - - // Assign the widened bounds from the ElevatedBlock to the CFG Block. - void CollectWidenedBounds(); - - // Extract the terminating sub-expression from the expression E. - // @param[in] E is the expression from which we need to extract the terminating sub-expression. - // @return The terminating sub-expression from the expression E. - Expr *GetTerminatorCondition(const Expr *E) const; - - // Get the terminating condition for a block. This could be an if condition - // of the form "if(*(p + i))". - // @param[in] B is the block for which we need the terminating condition. - // @return Expression for the terminating condition of block B. - Expr *GetTerminatorCondition(const CFGBlock *B) const; - - // Check if V is an _Nt_array_ptr or an _Nt_checked array. - // @param[in] V is the VarDecl. - // @return Whether V is an _Nt_array_ptr or an _Nt_checked array. - bool IsNtArrayType(const VarDecl *V) const; - - // WidenedBounds is a DenseMap and hence is not suitable for iteration as - // its iteration order is non-deterministic. So we first need to order the - // blocks. - // @return Blocks ordered by block numbers from higher to lower since block - // numbers decrease from entry to exit. - OrderedBlocksTy GetOrderedBlocks(); - - // Invoke IgnoreValuePreservingOperations to strip off casts. - // @param[in] E is the expression whose casts must be stripped. - // @return E with casts stripped off. - Expr *IgnoreCasts(const Expr *E); - - // We do not want to run dataflow analysis on null or exit blocks. So we - // skip them. - // @param[in] B is the block which may need to the skipped from dataflow - // analysis. - // @return Whether B should be skipped. - bool SkipBlock(const CFGBlock *B) const; - - // Get the DeclRefExpr from an expression E. - // @param[in] An expression E which is known to be either an LValueToRValue - // cast or an ArrayToPointerDecay cast. - // @return The DeclRefExpr from the expression E or nullptr. - DeclRefExpr *GetDeclOperand(const Expr *E); - - // Collect all ntptrs in scope. Currently, this simply collects all ntptrs - // defined in all blocks in the current function. This function inserts the - // VarDecls for the ntptrs in NtPtrsInScope. - // @param[in] FD is the current function. - void CollectNtPtrsInScope(FunctionDecl *FD); - - // If variable V is killed by Stmt S in Block B, add TopLevelStmt:V pair - // to EB->Kill, where TopLevelStmt is the top-level Stmt that contains S. - // @param[in] EB is the ElevatedCFGBlock for the current block. - // @param[in] TopLevelStmt is the top-level Stmt in the block. - // @param[in] S is the current Stmt in the block. - void FillKillSet(ElevatedCFGBlock *EB, const Stmt *TopLevelStmt, const Stmt *S); - - // Initialize the In and Out sets for all blocks, except the Entry block, - // as Top. - void InitInOutSets(); - - // Check if the switch case label is null. - // @param[in] EB is the ElevatedCFGBlock for the current block. - bool CheckIsSwitchCaseNull(ElevatedCFGBlock *EB); - - // 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 and widen the bounds where applicable. - // @param[in] A is a set. - // @param[in] B is a set. - // @return The union of sets A and B containing the widened bounds. - template T Union(T &A, T &B) const; - - // 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; - - // Check whether the sets A and B differ. - // @param[in] A is a set. - // @param[in] B is a set. - // @return Whether sets A and B differ. - template bool Differ(T &A, T &B) const; - }; -} - -#endif diff --git a/clang/include/clang/Sema/BoundsWideningAnalysis.h b/clang/include/clang/Sema/BoundsWideningAnalysis.h index 70ba0ccd4e35..5bedc8c6485c 100644 --- a/clang/include/clang/Sema/BoundsWideningAnalysis.h +++ b/clang/include/clang/Sema/BoundsWideningAnalysis.h @@ -15,55 +15,465 @@ #define LLVM_CLANG_BOUNDS_WIDENING_ANALYSIS_H #include "clang/AST/CanonBounds.h" +#include "clang/AST/ExprUtils.h" +#include "clang/AST/PrettyPrinter.h" #include "clang/Analysis/Analyses/PostOrderCFGView.h" #include "clang/Sema/CheckedCAnalysesPrepass.h" #include "clang/Sema/Sema.h" namespace clang { - // BoundsMapTy maps a null-terminated array variable to its bounds - // expression. - using BoundsMapTy = llvm::DenseMap; - // StmtBoundsMapTy maps each null-terminated array variable that occurs in a - // statement to its bounds expression. + // BoundsMapTy maps a variable that is a pointer to a null-terminated array + // to its bounds expression. + using BoundsMapTy = llvm::DenseMap; + + // StmtBoundsMapTy maps each variable that is a pointer to a null-terminated + // array that occurs in a statement to its bounds expression. using StmtBoundsMapTy = llvm::DenseMap; - // StmtVarSetTy denotes a set of null-terminated array variables that are - // associated with a statement. The set of variables whose bounds are killed - // by a statement has the type StmtVarSetTy. + // StmtVarSetTy denotes a set of variables that are pointers to + // null-terminated arrays and that are associated with a statement. The set + // of variables whose bounds are killed by a statement has the type + // StmtVarSetTy. using StmtVarSetTy = llvm::DenseMap; - // The BoundsWideningAnalysis class represents the dataflow analysis for - // bounds widening. The sets In, Out, Gen and Kill that are used by the - // analysis are members of this class. The class also has methods that act on - // these sets to perform the dataflow analysis. + // StmtSetTy denotes a set of statements. + using StmtSetTy = llvm::SmallPtrSet; + + // StmtMapTy denotes a map of a statement to another statement. This is used + // to store the mapping of a statement to its previous statement in a block. + using StmtMapTy = llvm::DenseMap; + + // ExprVarsTy maps an expression to a set of variables. If E is an expression + // dereferencing a null-terminated array, then ExprVarsTy maps the expression + // (E + 1) to a set of null-terminated arrays whose bounds may potentially be + // widened to (E + 1). + using ExprVarsTy = llvm::DenseMap; + + // OrderedBlocksTy denotes blocks ordered by block numbers. This is useful + // for printing the blocks in a deterministic order. + using OrderedBlocksTy = std::vector; + +} // end namespace clang + +namespace clang { + + //===-------------------------------------------------------------------===// + // Class definition of the BoundsWideningUtil class. This class contains + // helper methods that are used by the BoundsWideningAnalysis class to + // perform the dataflow analysis. The BoundsWideningAnalysis class is defined + // later in this file. + //===-------------------------------------------------------------------===// + + class BoundsWideningUtil { + private: + Sema &SemaRef; + CFG *Cfg; + ASTContext &Ctx; + Lexicographic Lex; + BoundsVarsTy &BoundsVarsLower; + BoundsVarsTy &BoundsVarsUpper; + + public: + BoundsWideningUtil(Sema &SemaRef, CFG *Cfg, + ASTContext &Ctx, Lexicographic Lex, + BoundsVarsTy &BoundsVarsLower, + BoundsVarsTy &BoundsVarsUpper) : + SemaRef(SemaRef), Cfg(Cfg), Ctx(Ctx), Lex(Lex), + BoundsVarsLower(BoundsVarsLower), BoundsVarsUpper(BoundsVarsUpper) {} + + // Check if B2 is a subrange of B1. + // @param[in] B1 is the first range. + // @param[in] B2 is the second range. + // @return Returns true if B2 is a subrange of B1, false otherwise. + bool IsSubRange(RangeBoundsExpr *B1, RangeBoundsExpr *B2) const; + + // Determine if the switch-case has a case label (other than default) that + // tests for null. + // @param[in] CurrBlock is the current block. + // @return Returns true if the switch-case has a label that tests for null. + bool ExistsNullCaseLabel(const CFGBlock *CurrBlock) const; + + // Determine if the current block begins a case of a switch-case. + // @param[in] CurrBlock is the current block. + // @return Returns true if the current block begins a case. + bool IsSwitchCaseBlock(const CFGBlock *CurrBlock) const; + + // Determine if the switch-case label on the current block tests for null. + // @param[in] CurrBlock is the current block. + // @return Returns true if the case label on the current block tests for + // null. + bool CaseLabelTestsForNull(const CFGBlock *CurrBlock) const; + + // Determine if the edge from PredBlock to CurrBlock is a fallthrough. + // @param[in] PredBlock is a predecessor block of the current block. + // @param[in] CurrBlock is the current block. + // @return Returns true if the edge is a fallthrough, false otherwise. + bool IsFallthroughEdge(const CFGBlock *PredBlock, + const CFGBlock *CurrBlock) const; + + // Determine if the edge from PredBlock to CurrBlock is a true edge. + // @param[in] PredBlock is a predecessor block of the current block. + // @param[in] CurrBlock is the current block. + // @return Returns true if the edge is a true edge, false otherwise. + bool IsTrueEdge(const CFGBlock *PredBlock, + const CFGBlock *CurrBlock) const; + + // Get all variables modified by CurrStmt or statements nested in CurrStmt. + // @param[in] CurrStmt is a given statement. + // @param[out] ModifiedVars is a set of variables modified by CurrStmt or + // statements nested in CurrStmt. + void GetModifiedVars(const Stmt *CurrStmt, VarSetTy &ModifiedVars) const; + + // Get the set of variables that are pointers to null-terminated arrays and + // in whose lower bounds expressions the variables in Vars occur. + // @param[in] Vars is a set of variables. + // @param[out] NullTermPtrsWithVarsInLowerBounds is a set of variables that + // are pointers to null-terminated arrays and in whose lower bounds + // expressions the variables in Vars occur. + void GetNullTermPtrsWithVarsInLowerBounds( + VarSetTy &Vars, VarSetTy &NullTermPtrsWithVarsInLowerBounds) const; + + // Get the set of variables that are pointers to null-terminated arrays and + // in whose upper bounds expressions the variables in Vars occur. + // @param[in] Vars is a set of variables. + // @param[out] NullTermPtrsWithVarsInLowerBounds is a set of variables that + // are pointers to null-terminated arrays and in whose upper bounds + // expressions the variables in Vars occur. + void GetNullTermPtrsWithVarsInUpperBounds( + VarSetTy &Vars, VarSetTy &NullTermPtrsWithVarsInUpperBounds) const; + + // Add an offset to a given expression. + // @param[in] E is the given expression. + // @param[in] Offset is the given offset. + // @return Returns the expression E + Offset. + Expr *AddOffsetToExpr(Expr *E, unsigned Offset) const; + + // From the given expression get the dereference expression. A dereference + // expression can be of the form "*(p + 1)" or "p[1]". + // @param[in] E is the given expression. + // @return Returns the dereference expression, if it exists. + Expr *GetDerefExpr(const Expr *E) const; + + // Get the variable in an expression that is a pointer to a null-terminated + // array. + // @param[in] E is the given expression. + // @return The variable in the expression that is a pointer to a + // null-terminated array; nullptr if no such variable exists in the + // expression. + const VarDecl *GetNullTermPtrInExpr(Expr *E) const; + + // Invoke IgnoreValuePreservingOperations to strip off casts. + // @param[in] E is the expression whose casts must be stripped. + // @return E with casts stripped off. + Expr *IgnoreCasts(const Expr *E) const; + + // We do not want to run dataflow analysis on null blocks or the exit + // block. So we skip them. + // @param[in] B is the block which may need to be skipped from dataflow + // analysis. + // @return Whether B should be skipped. + bool SkipBlock(const CFGBlock *B) const; + + // Check if V is an _Nt_array_ptr or an _Nt_checked array. + // @param[in] V is a VarDecl. + // @return Whether V is an _Nt_array_ptr or an _Nt_checked array. + bool IsNtArrayType(const VarDecl *V) const; + + // 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 BoundsWideningUtil 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 BoundsMapTy + // and VarSetTy. + template<> + BoundsMapTy BoundsWideningUtil::Difference( + BoundsMapTy &A, VarSetTy &B) const; + + // Template specialization for computing the union of BoundsMapTy. + template<> + BoundsMapTy BoundsWideningUtil::Union( + BoundsMapTy &A, BoundsMapTy &B) const; + + // Template specialization for computing the intersection of BoundsMapTy. + template<> + BoundsMapTy BoundsWideningUtil::Intersect( + BoundsMapTy &A, BoundsMapTy &B) const; + + // Template specialization for determining the equality of BoundsMapTy. + template<> + bool BoundsWideningUtil::IsEqual( + BoundsMapTy &A, BoundsMapTy &B) const; + +} // end namespace clang + +namespace clang { + //===-------------------------------------------------------------------===// + // Implementation of the methods in the BoundsWideningAnalysis class. This is + // the main class that implements the dataflow analysis for bounds widening + // of null-terminated arrays. The BoundsWideningAnalysis class represents the + // dataflow analysis for bounds widening. The sets In, Out, Gen and Kill that + // are used by the analysis are members of this class. The class also has + // methods that act on these sets to perform the dataflow analysis. This + // class uses helper methods from the BoundsWideningUtil class that are + // defined later in this file. + //===-------------------------------------------------------------------===// + class BoundsWideningAnalysis { private: - Sema &S; + Sema &SemaRef; CFG *Cfg; ASTContext &Ctx; Lexicographic Lex; llvm::raw_ostream &OS; + BoundsWideningUtil BWUtil; class ElevatedCFGBlock { public: const CFGBlock *Block; - // The In and Out sets for a block. - BoundsMapTy In, Out; - // The Gen set for each statement in a block. - StmtBoundsMapTy Gen; - // The Kill set for each statement in a block. - StmtVarSetTy Kill; + // The In, Out and Gen sets for a block. + BoundsMapTy In, Out, Gen; + // The Kill set for a block. + VarSetTy Kill; + // The StmtGen and UnionGen sets for each statement in a block. + StmtBoundsMapTy StmtGen, UnionGen; + // The StmtKill and UnionKill sets for each statement in a block. + StmtVarSetTy StmtKill, UnionKill; + + // A mapping from a statement to its previous statement in a block. + StmtMapTy PrevStmtMap; + // The last statement of the block. This is nullptr if the block is empty. + const Stmt *LastStmt = nullptr; + // The terminating condition that dereferences a pointer. This is nullptr + // if the terminating condition does not dereference a pointer. + Expr *TermCondDerefExpr = nullptr; ElevatedCFGBlock(const CFGBlock *B) : Block(B) {} - }; + + }; // end of ElevatedCFGBlock class. + + private: + + // BlockMapTy denotes the mapping from CFGBlocks to ElevatedCFGBlocks. + using BlockMapTy = llvm::DenseMap; + + // A queue of unique ElevatedCFGBlocks involved in the fixpoint of the + // dataflow analysis. + using WorkListTy = QueueSet; + + // BlockMap maps a CFGBlock to an ElevatedCFGBlock. Given a CFGBlock it is + // used to lookup an ElevatedCFGBlock. + BlockMapTy BlockMap; + + // AllNullTermPtrsInFunc denotes all variables in the function that are + // pointers to null-terminated arrays. + VarSetTy AllNullTermPtrsInFunc; public: - BoundsWideningAnalysis(Sema &S, CFG *Cfg) : - S(S), Cfg(Cfg), Ctx(S.Context), - Lex(Lexicographic(Ctx, nullptr)), - OS(llvm::outs()) {} - }; + // Top is a special bounds expression that denotes the super set of all + // bounds expressions. + static constexpr RangeBoundsExpr *Top = nullptr; + + BoundsWideningAnalysis(Sema &SemaRef, CFG *Cfg, + BoundsVarsTy &BoundsVarsLower, + BoundsVarsTy &BoundsVarsUpper) : + SemaRef(SemaRef), Cfg(Cfg), Ctx(SemaRef.Context), + Lex(Lexicographic(Ctx, nullptr)), OS(llvm::outs()), + BWUtil(BoundsWideningUtil(SemaRef, Cfg, Ctx, Lex, + BoundsVarsLower, BoundsVarsUpper)) {} + + // Run the dataflow analysis to widen bounds for null-terminated arrays. + // @param[in] FD is the current function. + // @param[in] NestedStmts is a set of top-level statements that are nested + // in another top-level statement. + void WidenBounds(FunctionDecl *FD, StmtSetTy NestedStmts); + + // Pretty print the widened bounds for all null-terminated arrays in the + // current function. + // @param[in] FD is the current function. + void DumpWidenedBounds(FunctionDecl *FD); + + // Get the Out set for the statement. This set represents the bounds + // widened after the statement. + // Note: This method can be called from outside this class to get the + // widened bounds after a statement. + // @param[in] B is the current block. + // @param[in] CurrStmt is the current statement. + BoundsMapTy GetStmtOut(const CFGBlock *B, const Stmt *CurrStmt) const; + + // Get the In set for the statement. This set represents the bounds + // widened before the statement. + // Note: This method can be called from outside this class to get the + // widened bounds before a statement. + // @param[in] B is the current block. + // @param[in] CurrStmt is the current statement. + BoundsMapTy GetStmtIn(const CFGBlock *B, const Stmt *CurrStmt) const; + + // Get the bounds that are widened in the current block before the current + // statement and that are not killed by the current statement. + // Note: This method can be called from outside this class and can be used + // to control diagnostics for observed bounds. + // @param[in] B is the current block. + // @param[in] CurrStmt is the current statement. + BoundsMapTy GetBoundsWidenedAndNotKilled(const CFGBlock *B, + const Stmt *CurrStmt) const; + + private: + // Compute Gen and Kill sets for the block and statements in the block. + // @param[in] EB is the current ElevatedCFGBlock. + // @param[in] NestedStmts is a set of top-level statements that are + // nested in another top-level statement. + void ComputeGenKillSets(ElevatedCFGBlock *EB, StmtSetTy NestedStmts); + + // Compute the StmtGen and StmtKill sets for a statement in a block. + // @param[in] EB is the current ElevatedCFGBlock. + // @param[in] CurrStmt is the current statement. + // @param[in] NestedStmts is a set of top-level statements that are + // nested in another top-level statement. + void ComputeStmtGenKillSets(ElevatedCFGBlock *EB, const Stmt *CurrStmt, + StmtSetTy NestedStmts); + + // Compute the union of Gen and Kill sets of all statements up to (and + // including) the current statement in the block. + // @param[in] EB is the current ElevatedCFGBlock. + // @param[in] CurrStmt is the current statement. + // @param[in] PrevStmt is the previous statement of CurrStmt in the linear + // ordering of statements in the block. + void ComputeUnionGenKillSets(ElevatedCFGBlock *EB, const Stmt *CurrStmt, + const Stmt *PrevStmt); + + // Compute the Gen and Kill sets for the block. + // @param[in] EB is the current ElevatedCFGBlock. + void ComputeBlockGenKillSets(ElevatedCFGBlock *EB); + + // Compute the In set for the block. + // @param[in] EB is the current ElevatedCFGBlock. + // @return Return true if the In set of the block has changed, false + // otherwise. + bool ComputeInSet(ElevatedCFGBlock *EB); + + // Compute the Out set for the block. + // @param[in] EB is the current ElevatedCFGBlock. + // @return Return true if the Out set of the block has changed, false + // otherwise. + bool ComputeOutSet(ElevatedCFGBlock *EB); + + // Initialize the In and Out sets for the block. + // @param[in] FD is the current function. + // @param[in] EB is the current ElevatedCFGBlock. + void InitBlockInOutSets(FunctionDecl *FD, ElevatedCFGBlock *EB); + + // Add the successor blocks 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); + + // Prune the Out set of the pred block according to various conditions. + // @param[in] PredEB is a predecessor block of the current block. + // @param[in] CurrEB is the current block. + // @return The pruned Out set for PredEB. + BoundsMapTy PruneOutSet(ElevatedCFGBlock *PredEB, + ElevatedCFGBlock *CurrEB) const; + + // Initialize the list of variables that are pointers to null-terminated + // arrays to the null-terminated arrays that are passed as parameters to + // the function. This function updates the AllNullTermPtrsInFunc set. + // @param[in] FD is the current function. + void InitNullTermPtrsInFunc(FunctionDecl *FD); + + // Update the list of variables that are pointers to null-terminated arrays + // with the variables that are in StmtGen for the current statement in the + // block. This function updates the AllNullTermPtrsInFunc set. + // @param[in] EB is the current ElevatedCFGBlock. + // @param[in] CurrStmt is the current statement. + void UpdateNullTermPtrsInFunc(ElevatedCFGBlock *EB, const Stmt *CurrStmt); + + // Fill the Gen and Kill sets for a statement using the variable and bounds + // expressions in VarsAndBounds. + // @param[in] EB is the current ElevatedCFGBlock. + // @param[in] CurrStmt is the current statement. + // @param[in] VarsAndBounds is a map of variables to their bounds + // expressions. + void FillStmtGenKillSets(ElevatedCFGBlock *EB, const Stmt *CurrStmt, + BoundsMapTy &VarsAndBounds); + + // Get the mapping of variables to their bounds expressions in the bounds + // declaration of a null-terminated array. + // @param[in] EB is the current ElevatedCFGBlock. + // @param[in] V is a variable that is a pointer to a null-terminated array. + // @param[out] VarsAndBounds is a map of variables to their bounds + // expressions. This field is updated by this function. + void GetVarsAndBoundsInDecl(ElevatedCFGBlock *EB, const VarDecl *V, + BoundsMapTy &VarsAndBounds); + + // Get the mapping of variables to their bounds expressions in a where + // clause. + // @param[in] EB is the current ElevatedCFGBlock. + // @param[in] WC is the where clause that annotates CurrStmt. + // @param[out] VarsAndBounds is a map of variables to their bounds + // expressions. This field is updated by this function. + void GetVarsAndBoundsInWhereClause(ElevatedCFGBlock *EB, + WhereClause *WC, + BoundsMapTy &VarsAndBounds); + + // Get the mapping of variables to their bounds expressions from an + // expression that dereferences a null-terminated array. + // @param[in] EB is the current ElevatedCFGBlock. + // @param[out] VarsAndBounds is a map of variables to their bounds + // expressions. This field is updated by this function. + void GetVarsAndBoundsInPtrDeref(ElevatedCFGBlock *EB, + BoundsMapTy &VarsAndBounds); + + // Get the mapping of variables to their resetted bounds because of a + // modification to a variable that occurs in their lower or upper bounds + // expressions. + // @param[in] EB is the current ElevatedCFGBlock. + // @param[in] CurrStmt is the current statement. + // @param[out] VarsAndBounds is a map of variables to their bounds + // expressions. This field is updated by this function. + void GetVarsAndBoundsForModifiedVars(ElevatedCFGBlock *EB, + const Stmt *CurrStmt, + BoundsMapTy &VarsAndBounds); + + // 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. + OrderedBlocksTy GetOrderedBlocks() const; + + }; // end of BoundsWideningAnalysis class. } // end namespace clang #endif diff --git a/clang/include/clang/Sema/CheckedCAnalysesPrepass.h b/clang/include/clang/Sema/CheckedCAnalysesPrepass.h index 32bee45c3708..581215421d23 100644 --- a/clang/include/clang/Sema/CheckedCAnalysesPrepass.h +++ b/clang/include/clang/Sema/CheckedCAnalysesPrepass.h @@ -45,17 +45,19 @@ namespace clang { // 2. V has a declared bounds expression. VarUsageTy VarUses; - // BoundsVars maps each variable Z in a function to the set of all - // variables in whose bounds expressions Z occurs. A variable Z can occur - // in the bounds expression of a variable V if - // 1. Z occurs in the declared bounds expression of V, or - // 2. A where clause declares bounds B of V and Z occurs in B. + // BoundsVarsLower maps each variable Z in a function to the set of all + // variables in whose lower bounds expressions Z occurs. A variable Z can + // occur in the lower bounds expression of a variable V if + // 1. Z occurs in the declared lower bounds expression of V, or + // 2. A where clause declares lower bounds B of V and Z occurs in B. + BoundsVarsTy BoundsVarsLower; - // Note: BoundsVarsTy is a map of keys to values which are sets. As a - // result, there is no defined iteration order for either its keys or its - // values. So in case we want to iterate BoundsVars and need a determinstic - // iteration order we must remember to sort the keys as well as the values. - BoundsVarsTy BoundsVars; + // BoundsVarsUpper maps each variable Z in a function to the set of all + // variables in whose upper bounds expressions Z occurs. A variable Z can + // occur in the upper bounds expression of a variable V if + // 1. Z occurs in the declared upper bounds expression of V, or + // 2. A where clause declares upper bounds B of V and Z occurs in B. + BoundsVarsTy BoundsVarsUpper; // BoundsSiblingFields maps each FieldDecl F in a record declaration S to // a set of fields in S in whose declared bounds F occurs. More precisely, diff --git a/clang/lib/AST/CanonBounds.cpp b/clang/lib/AST/CanonBounds.cpp index 6eb55717f416..90a36052e810 100644 --- a/clang/lib/AST/CanonBounds.cpp +++ b/clang/lib/AST/CanonBounds.cpp @@ -320,6 +320,31 @@ bool Lexicographic::GetDerefOffset(const Expr *UpperExpr, return Res; } +bool Lexicographic::GetExprIntDiff(const Expr *Arg1, const Expr *Arg2, + llvm::APSInt &Offset) const { + Expr *E1 = const_cast(Arg1); + Expr *E2 = const_cast(Arg2); + + PreorderAST P1(Context, E1); + P1.Normalize(); + if (P1.GetError()) { + P1.Cleanup(); + return false; + } + + PreorderAST P2(Context, E2); + P2.Normalize(); + if (P2.GetError()) { + P2.Cleanup(); + return false; + } + + bool Res = P1.GetExprIntDiff(P2, Offset); + P1.Cleanup(); + P2.Cleanup(); + return Res; +} + Result Lexicographic::CompareExpr(const Expr *Arg1, const Expr *Arg2) const { if (Trace) { raw_ostream &OS = llvm::outs(); diff --git a/clang/lib/AST/PreorderAST.cpp b/clang/lib/AST/PreorderAST.cpp index ac0095277e2a..f268c4f49328 100644 --- a/clang/lib/AST/PreorderAST.cpp +++ b/clang/lib/AST/PreorderAST.cpp @@ -490,6 +490,10 @@ bool LeafExprNode::ConstantFold(bool &Error, ASTContext &Ctx) { return false; } +// TODO: Remove this method after the updated implementation of the bounds +// widening analysis merges. This method will be replaced by +// PreorderAST::GetExprIntDiff. See issue +// https://github.com/microsoft/checkedc-clang/issues/1078. bool PreorderAST::GetDerefOffset(Node *UpperNode, Node *DerefNode, llvm::APSInt &Offset) { // Extract the offset by which a pointer is dereferenced. For the pointer we @@ -544,7 +548,7 @@ bool PreorderAST::GetDerefOffset(Node *UpperNode, Node *DerefNode, // Offset should always be of the form (ptr + offset). So we check for // addition. // Note: We have already converted (ptr - offset) to (ptr + -offset). So - // its okay to only check for addition. + // it is okay to only check for addition. if (B1->Opc != BO_Add) return false; @@ -566,6 +570,84 @@ bool PreorderAST::GetDerefOffset(Node *UpperNode, Node *DerefNode, return true; } +bool PreorderAST::GetExprIntDiff(Node *E1, Node *E2, llvm::APSInt &Offset) { + // Get the integer difference between expressions. + + // If E1 and E2 are not comparable, return false. + // Else perform E1 - E2, store the integer result in Offset and return true. + + // E1 and E2 are not comparable if their non-integer parts are not equal. + + // Since we have already normalized exprs like "*p" to "*(p + 0)" we require + // that the root of the preorder AST is a BinaryOperatorNode. + auto *B1 = dyn_cast_or_null(E1); + auto *B2 = dyn_cast_or_null(E2); + + if (!B1 || !B2) + return false; + + // If the opcodes mismatch we cannot have a valid offset. + if (B1->Opc != B2->Opc) + return false; + + // Offset should always be of the form (ptr + offset). So we check for + // addition. + // Note: We have already converted (ptr - offset) to (ptr + -offset). So + // it is okay to only check for addition. + if (B1->Opc != BO_Add) + return false; + + // We have already constant folded the constants. So return false if the + // number of children mismatch. + if (B1->Children.size() != B2->Children.size()) + return false; + + llvm::APSInt Zero(Ctx.getTargetInfo().getIntWidth(), 0); + // Initialize Offset to 0. + Offset = Zero; + + // Check if the children are equivalent. + for (size_t I = 0; I != B1->Children.size(); ++I) { + auto *Child1 = B1->Children[I]; + auto *Child2 = B2->Children[I]; + + if (Child1->Compare(Child2, Lex) == Result::Equal) + continue; + + // If the children are not equal we require that they be integer constant + // leaf nodes. Otherwise we cannot have a valid offset. + auto *L1 = dyn_cast_or_null(Child1); + auto *L2 = dyn_cast_or_null(Child2); + + if (!L1 || !L2) + return false; + + // Return false if either of the leaf nodes is not an integer constant. + llvm::APSInt IntegerPart1; + if (!L1->E->isIntegerConstantExpr(IntegerPart1, Ctx)) + return false; + + llvm::APSInt IntegerPart2; + if (!L2->E->isIntegerConstantExpr(IntegerPart2, Ctx)) + return false; + + // This guards us from a case where the constants were not folded for + // some reason. In theory this should never happen. But we are adding this + // check just in case. + if (llvm::APSInt::compareValues(Offset, Zero) != 0) + return false; + + // Offset = IntegerPart1 - IntegerPart2. + // Return false if we encounter an overflow. + bool Overflow; + Offset = IntegerPart1.ssub_ov(IntegerPart2, Overflow); + if (Overflow) + return false; + } + + return true; +} + Result BinaryOperatorNode::Compare(const Node *Other, Lexicographic Lex) const { Result KindComparison = CompareKinds(Other); if (KindComparison != Result::Equal) diff --git a/clang/lib/Sema/BoundsAnalysis.cpp b/clang/lib/Sema/BoundsAnalysis.cpp deleted file mode 100644 index 8e446843c743..000000000000 --- a/clang/lib/Sema/BoundsAnalysis.cpp +++ /dev/null @@ -1,799 +0,0 @@ -//===--------- BoundsAnalysis.cpp - Bounds Widening Analysis --------------===// -// 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 for bounds widening. -// -//===----------------------------------------------------------------------===// - -#include "clang/Sema/BoundsAnalysis.h" - -namespace clang { - -void BoundsAnalysis::WidenBounds(FunctionDecl *FD, StmtSet NestedStmts) { - assert(Cfg && "expected CFG to exist"); - - WorkListTy WorkList; - - // Add each block to WorkList and create a mapping from Block to - // ElevatedCFGBlock. - // Note: By default, PostOrderCFGView iterates in reverse order. So we always - // get a reverse post order when we iterate PostOrderCFGView. - for (const CFGBlock *B : PostOrderCFGView(Cfg)) { - // SkipBlock will skip all null and exit blocks. PostOrderCFGView does not - // contain any unreachable blocks. So at the end of this loop BlockMap only - // contains reachable blocks. - if (SkipBlock(B)) - continue; - - auto EB = new ElevatedCFGBlock(B); - // Note: WorkList is a queue. So we maintain the reverse post order when we - // iterate WorkList. - WorkList.append(EB); - BlockMap[B] = EB; - - // Mark the In set for the Entry block as "empty". The Out set for the - // Entry block would be marked as "empty" in ComputeOutSets. - EB->IsInSetEmpty = B == &Cfg->getEntry(); - } - - // At this time, BlockMap only contains reachable blocks. We iterate through - // all blocks in the CFG and append all unreachable blocks to the WorkList. - for (auto I = Cfg->begin(), E = Cfg->end(); I != E; ++I) { - const CFGBlock *B = *I; - if (!SkipBlock(B) && !BlockMap.count(B)) { - auto EB = new ElevatedCFGBlock(B); - WorkList.append(EB); - BlockMap[B] = EB; - } - } - - // Collect all ntptrs in scope. - CollectNtPtrsInScope(FD); - - // Compute Gen and Kill sets. - ComputeGenSets(); - ComputeKillSets(NestedStmts); - - // Initialize the In and Out sets to Top. - InitInOutSets(); - - // Compute In and Out sets. - while (!WorkList.empty()) { - ElevatedCFGBlock *EB = WorkList.next(); - WorkList.remove(EB); - - ComputeInSets(EB); - ComputeOutSets(EB, WorkList); - } -} - -void BoundsAnalysis::InitInOutSets() { - for (const auto item : BlockMap) { - ElevatedCFGBlock *EB = item.second; - - if (EB->Block == &Cfg->getEntry()) - continue; - - EB->In = Top; - for (const CFGBlock *succ : EB->Block->succs()) - EB->Out[succ] = Top; - } -} - -bool BoundsAnalysis::CheckIsSwitchCaseNull(ElevatedCFGBlock *EB) { - if (const auto *CS = dyn_cast_or_null(EB->Block->getLabel())) { - - // We mimic how clang (in SemaStmt.cpp) gets the value of a switch case. It - // invokes EvaluateKnownConstInt and we do the same here. SemaStmt has - // already extended/truncated the case value to fit the integer range and - // EvaluateKnownConstInt gives us that value. - llvm::APSInt LHSVal = CS->getLHS()->EvaluateKnownConstInt(Ctx); - llvm::APSInt LHSZero (LHSVal.getBitWidth(), LHSVal.isUnsigned()); - if (llvm::APSInt::compareValues(LHSVal, LHSZero) == 0) - return true; - - // Check if the case statement is of the form "case LHS ... RHS" (a GNU - // extension). - if (CS->caseStmtIsGNURange()) { - llvm::APSInt RHSVal = CS->getRHS()->EvaluateKnownConstInt(Ctx); - llvm::APSInt RHSZero (RHSVal.getBitWidth(), RHSVal.isUnsigned()); - if (llvm::APSInt::compareValues(RHSVal, RHSZero) == 0) - return true; - - // Check if 0 if contained within the range [LHS, RHS]. - return (LHSVal <= LHSZero && RHSZero <= RHSVal) || - (LHSVal >= LHSZero && RHSZero >= RHSVal); - } - return false; - } - return true; -} - -void BoundsAnalysis::ComputeGenSets() { - // If there is an edge B1->B2 and the edge condition is of the form - // "if (*(p + i))" then Gen[B1] = {B2, p:i} . - - // Here, EB is B2. - for (const auto item : BlockMap) { - ElevatedCFGBlock *EB = item.second; - - // Check if this is a switch case and whether the case label is null. - // In a switch, we can only widen the bounds in the following cases: - // 1. Inside a case with a non-null case label. - // 2. Inside the default case, only if there is another case with a null - // case label. - bool IsSwitchCaseNull = CheckIsSwitchCaseNull(EB); - - // Iterate through all preds of EB. - for (const CFGBlock *pred : EB->Block->preds()) { - if (SkipBlock(pred)) - continue; - - // We can add "p:i" only on the true edge. - // For example, - // B1: if (*(p + i)) - // B2: foo(); - // B3: else bar(); - - // Here we have the edges (B1->B2) and (B1->B3). We can add "p:i" only - // on the true edge. Which means we will add the following entry to - // Gen[B1]: {B2, p:i} - - // Check if EB is on a true edge of pred. The false edge (including the - // default case for a switch) is always the last edge in the list of - // edges. So we check that EB is not on the last edge for pred. - - if (pred->succ_size() == 0) - continue; - - const CFGBlock *FalseOrDefaultBlock = *(pred->succs().end() - 1); - if (EB->Block == FalseOrDefaultBlock) - continue; - - // Get the edge condition. - Expr *E = GetTerminatorCondition(pred); - if (!E) - continue; - - // Check if the pred ends in a switch statement. - const Stmt *TerminatorStmt = pred->getTerminatorStmt(); - if (TerminatorStmt && isa(TerminatorStmt)) { - - // According to C11 standard section 6.8.4.2, the controlling - // expression of a switch shall have integer type. - // If we have switch(*p) where p is _Nt_array_ptr then it is - // casted to integer type and an IntegralCast is generated. Here we - // strip off the IntegralCast. - if (auto *CE = dyn_cast(E)) { - if (CE->getCastKind() == CastKind::CK_IntegralCast) - E = CE->getSubExpr(); - } - - // If the current block has a null case label, we cannot widen the - // bounds inside that case. - if (IsSwitchCaseNull) { - // If we are here it means that the current case label is null. - // This means that the default case would represent the non-null - // case. Hence, we can widen the bounds inside the default case. - if (FalseOrDefaultBlock && FalseOrDefaultBlock->getLabel() && - isa(FalseOrDefaultBlock->getLabel())) - FillGenSet(E, BlockMap[pred], BlockMap[FalseOrDefaultBlock]); - - continue; - } - } - - FillGenSet(E, BlockMap[pred], EB); - } - } -} - -void BoundsAnalysis::CollectBoundsVars(const Expr *E, DeclSetTy &BoundsVars) { - if (!E) - return; - - E = IgnoreCasts(E); - - // Collect bounds vars for the lower and upper bounds exprs. - // Example: - // _Nt_array_ptr p : bounds(p + i, p + j); - // LowerExpr: p + i. - // UpperExpr: p + j. - if (const auto *RBE = dyn_cast(E)) { - CollectBoundsVars(RBE->getLowerExpr(), BoundsVars); - CollectBoundsVars(RBE->getUpperExpr(), BoundsVars); - } - - // Collect bounds vars for the LHS and RHS of binary expressions. - if (const auto *BO = dyn_cast(E)) { - CollectBoundsVars(BO->getLHS(), BoundsVars); - CollectBoundsVars(BO->getRHS(), BoundsVars); - } - - if (DeclRefExpr *D = GetDeclOperand(E)) - if (const auto *V = dyn_cast(D->getDecl())) - BoundsVars.insert(V); -} - -DeclRefExpr *BoundsAnalysis::GetDeclOperand(const Expr *E) { - if (auto *CE = dyn_cast_or_null(E)) { - const Expr *SubE = CE->getSubExpr(); - assert(SubE && "Invalid CastExpr expression"); - - if (CE->getCastKind() == CastKind::CK_LValueToRValue || - CE->getCastKind() == CastKind::CK_ArrayToPointerDecay) { - E = Lex.IgnoreValuePreservingOperations(Ctx, const_cast(SubE)); - return dyn_cast_or_null(const_cast(E)); - } - } - return nullptr; -} - -void BoundsAnalysis::CollectNtPtrsInScope(FunctionDecl *FD) { - // TODO: Currently, we simply collect all ntptrs and variables used in their - // declared bounds for the entire function. Ultimately, we need to do a - // liveness analysis of what ntptrs are in scope for a block. - - assert(FD && "invalid function"); - - // Collect ntptrs passed as parameters to the current function. - // Note: NtPtrsInScope is a mapping from ntptr to variables used in the - // declared bounds of the ntptr. In this function we store an empty set for - // the bounds variables. This will later be filled in FillGenSetForEdge. - for (const ParmVarDecl *PD : FD->parameters()) { - if (IsNtArrayType(PD)) - NtPtrsInScope[PD] = DeclSetTy(); - } - - // Collect all ntptrs defined in the current function. BlockMap contains all - // blocks of the current function. We iterate through all blocks in BlockMap - // and try to gather ntptrs. - for (const auto item : BlockMap) { - const CFGBlock *B = item.first; - - for (CFGElement Elem : *B) { - if (Elem.getKind() != CFGElement::Statement) - continue; - const Stmt *S = Elem.castAs().getStmt(); - if (!S) - continue; - - if (const auto *DS = dyn_cast(S)) { - for (const Decl *D : DS->decls()) - if (const auto *V = dyn_cast(D)) - if (IsNtArrayType(V)) - NtPtrsInScope[V] = DeclSetTy(); - } - } - } -} - -void BoundsAnalysis::FillGenSetForEdge(const Expr *DerefExpr, - ElevatedCFGBlock *EB, - ElevatedCFGBlock *SuccEB) { - // The deref expr can be of 2 forms: - // 1. Ptr deref or array subscript: if (*p) or p[i] - // 2. BinaryOperator: if (*(p + e)) - - // For bounds widening, the base of the deref expr and the declared upper - // bounds expr for all ntptrs in scope should be the same. - - // For example: - // _Nt_array_ptr p : bounds(p, p + i); - // _Nt_array_ptr r : bounds(r, r + i); - - // if (*(p + i)) // widen p by 1 - // if (*(p + i + 1)) // widen p by 2 - - // if (*(p + j)) // no widening - - // if (*p) // no widening - - // if (*(p + i + 0) // widen p by 1 - - for (auto item : NtPtrsInScope) { - const VarDecl *V = item.first; - - BoundsExpr *NormalizedBounds = S.NormalizeBounds(V); - if (!NormalizedBounds) - continue; - - const auto *RBE = dyn_cast(NormalizedBounds); - if (!RBE) - continue; - - // Collect all variables involved in the upper and lower bounds exprs for - // the ntptr. An assignment to any such variable would kill the widenend - // bounds for the ntptr. - DeclSetTy BoundsVars; - CollectBoundsVars(NormalizedBounds, BoundsVars); - NtPtrsInScope[V] = BoundsVars; - - // Update the bounds of p on the edge EB->SuccEB only if we haven't already - // updated them. - if (EB->Gen[SuccEB->Block].count(V)) - continue; - - const Expr *UpperExpr = RBE->getUpperExpr(); - assert(UpperExpr && "invalid upper bounds expr"); - - // We cannot widen the bounds if the offset in the deref expr is less than - // the offset in the declared upper bounds expr. For example: - // _Nt_array_ptr p : bounds(p, p + i + 2); // Upper Bounds Offset (UBO) = 2. - - // if (*(p + i)) // Offset = 0 < UBO ==> no widening - // if (*(p + i + 1)) // Offset = 1 < UBO ==> no widening - // if (*(p + i + 2)) // Offset = 2 == UBO ==> widen by 1 - // if (*(p + i + 3)) // Offset = 3 > UBO ==> widen by 2 - - // We also should not widen the bounds if the pointer dereferences are not - // sequential. For example: - // _Nt_array_ptr p : bounds(p, p); - - // if (*p) // Widen by 1 - // if (*(p + 1)) // Widen by 2 - // if (*(p + 3)) // No widening because we have not tested *(p + 2). - // We handle this while computing the union of the In and Gen sets. - // We widen p by 1 only if the bounds of p in (In - Kill) == Gen[p]. - // See the comments in ComputeOutSets for more details. - - // GetDerefOffset gives the offset of the memory dereference relative to - // the declared upper bound expression. This offset is used in the widening - // computation in ComputeOutSets. - - llvm::APSInt Offset; - if (!Lex.GetDerefOffset(UpperExpr, DerefExpr, Offset)) - continue; - - EB->Gen[SuccEB->Block][V] = Offset.getLimitedValue(); - - // Top represents the union of the Gen sets of all edges. We have chosen - // the offsets of ptr variables in Top to be the max unsigned int. The - // reason behind this is that in order to compute the actual In sets for - // blocks we are going to intersect the Out sets on all the incoming edges - // of the block. And in that case we would always pick the ptr with the - // smaller offset. Chosing max unsigned int also makes handling Top much - // easier as we do not need to explicitly store edge info. - Top[V] = std::numeric_limits::max(); - } -} - -void BoundsAnalysis::FillGenSet(Expr *E, - ElevatedCFGBlock *EB, - ElevatedCFGBlock *SuccEB) { - - // Handle if conditions of the form "if (*e1 && *e2)". - if (const auto *BO = dyn_cast(E)) { - if (BO->getOpcode() == BO_LAnd) { - FillGenSet(BO->getLHS(), EB, SuccEB); - FillGenSet(BO->getRHS(), EB, SuccEB); - } - } - - if (auto *CE = dyn_cast(E)) - if (CE->getCastKind() == CastKind::CK_LValueToRValue) - E = CE->getSubExpr(); - - E = IgnoreCasts(E); - - // Fill the Gen set based on whether the edge condition is an array subscript - // or a pointer deref. - if (auto *AE = dyn_cast(E)) { - // An array access can be written A[4] or 4[A] (both are equivalent). - // getBase() and getIdx() always present the normalized view: A[4]. - // In this case getBase() returns "A" and getIdx() returns "4". - const auto *DerefExpr = - BinaryOperator::Create(Ctx, AE->getBase(), AE->getIdx(), - BinaryOperatorKind::BO_Add, AE->getType(), - AE->getValueKind(), AE->getObjectKind(), - AE->getExprLoc(), FPOptionsOverride()); - - FillGenSetForEdge(DerefExpr, EB, SuccEB); - - } else if (auto *UO = dyn_cast(E)) { - if (UO->getOpcode() == UO_Deref) { - assert(UO->getSubExpr() && "invalid UnaryOperator expression"); - - const Expr *DerefExpr = IgnoreCasts(UO->getSubExpr()); - FillGenSetForEdge(DerefExpr, EB, SuccEB); - } - } -} - -void BoundsAnalysis::ComputeKillSets(StmtSet NestedStmts) { - // For a block B, a variable V is added to Kill[B][S] if V is assigned to in - // B by Stmt S or some child S1 of S. - - // Compute vars killed in the current block. - for (const auto item : BlockMap) { - ElevatedCFGBlock *EB = item.second; - - for (CFGElement Elem : *(EB->Block)) { - if (Elem.getKind() == CFGElement::Statement) { - const Stmt *S = Elem.castAs().getStmt(); - if (!S) - continue; - - // Skip top-level statements that are nested in another - // top-level statement. - if (NestedStmts.find(S) != NestedStmts.end()) - continue; - - FillKillSet(EB, S, S); - } - } - } -} - -void BoundsAnalysis::FillKillSet(ElevatedCFGBlock *EB, - const Stmt *TopLevelStmt, - const Stmt *S) { - if (!S) - return; - - Expr *E = nullptr; - if (const auto *UO = dyn_cast(S)) { - if (UO->isIncrementDecrementOp()) { - assert(UO->getSubExpr() && "invalid UnaryOperator expression"); - E = IgnoreCasts(UO->getSubExpr()); - } - } else if (const auto *BO = dyn_cast(S)) { - if (BO->isAssignmentOp()) - E = IgnoreCasts(BO->getLHS()); - } - - if (E) { - if (const auto *D = dyn_cast(E)) { - if (const auto *V = dyn_cast(D->getDecl())) { - - // If the variable being assigned to is an ntptr, add the Stmt:V pair - // to the Kill set for the block. - if (IsNtArrayType(V)) - EB->Kill[TopLevelStmt].insert(V); - - else { - // Else look for the variable in NtPtrsInScope. - - // NtPtrsInScope is a mapping from an ntptr to all the variables used - // in its upper and lower bounds exprs. For example: - - // _Nt_array_ptr p : bounds(p + i, i + p + j + 10); - // _Nt_array_ptr q : bounds(i + q, i + p + q + m); - - // NtPtrsInScope: {p: {p, i, j}, q: {i, q, p, m}} - - for (auto item : NtPtrsInScope) { - const VarDecl *NtPtr = item.first; - DeclSetTy BoundsVars = item.second; - - // If the variable exists in the bounds declaration for the ntptr, - // then add the Stmt:ntptr pair to the Kill set for the block. - if (BoundsVars.count(V)) - EB->Kill[TopLevelStmt].insert(NtPtr); - } - } - } - } - } - - for (const Stmt *St : S->children()) - FillKillSet(EB, TopLevelStmt, St); -} - -void BoundsAnalysis::ComputeInSets(ElevatedCFGBlock *EB) { - // In[B1] = n Out[B*->B1], where B* are all preds of B1. - - BoundsMapTy Intersections; - bool FirstIntersection = true; - - for (const CFGBlock *pred : EB->Block->preds()) { - if (SkipBlock(pred)) - return; - - ElevatedCFGBlock *PredEB = BlockMap[pred]; - - if (FirstIntersection) { - Intersections = PredEB->Out[EB->Block]; - FirstIntersection = false; - } else - Intersections = Intersect(Intersections, PredEB->Out[EB->Block]); - } - - EB->In = Intersections; -} - -void BoundsAnalysis::ComputeOutSets(ElevatedCFGBlock *EB, - WorkListTy &WorkList) { - // Out[B1->B2] = (In[B1] - Kill[B1]) u Gen[B1->B2]. - - // EB->Kill is a mapping from Stmt to ntptrs. We extract just the ntptrs - // killed for the block and use that to compute (In - Kill). - DeclSetTy KilledVars; - for (auto item : EB->Kill) { - const DeclSetTy Vars = item.second; - KilledVars.insert(Vars.begin(), Vars.end()); - } - - BoundsMapTy InMinusKill = Difference(EB->In, KilledVars); - - for (const CFGBlock *succ : EB->Block->succs()) { - if (SkipBlock(succ)) - continue; - - BoundsMapTy OldOut = EB->Out[succ]; - - // Here's how we compute (In - Kill) u Gen: - - // 1. If variable p does not exist in (In - Kill), then - // (Gen[p] == 0) ==> Out[B1->B2] = {p:1}. - // In other words, if p does not exist in (In - Kill) it means that p is - // dereferenced for the first time on the incoming edge to this block, like - // "if (*p)". So we can initialize the bounds of p to 1. But we may also - // run into cases like "if (*(p + 100))". In this case, we cannot - // initialize the bounds of p. So additionally we check if Gen[p] == 0. - - // 2. Else if the bounds of p in (In - Kill) == Gen[V] then widen the - // bounds of p by 1. - // Consider this example: - // B1: if (*p) { // In[B1] = {}, Gen[Entry->B1] = {} ==> bounds(p) = 1. - // B2: if (*(p + 1)) { // In[B2] = {p:1}, Gen[B1->B2] = {p:1} ==> bounds(p) = 2. - // B3: if (*(p + 2)) { // In[B2] = {p:2}, Gen[B1->B2] = {p:2} ==> bounds(p) = 3. - - EB->Out[succ] = Union(InMinusKill, EB->Gen[succ]); - - // The Out set on an edge is marked "empty" if the In set is marked "empty" - // and the Gen set on that edge is empty. - EB->IsOutSetEmpty[succ] = EB->IsInSetEmpty && !EB->Gen[succ].size(); - - if (Differ(OldOut, EB->Out[succ])) - WorkList.append(BlockMap[succ]); - } -} - -DeclSetTy BoundsAnalysis::GetKilledBounds(const CFGBlock *B, const Stmt *St) { - auto I = BlockMap.find(B); - if (I == BlockMap.end()) - return DeclSetTy(); - - ElevatedCFGBlock *EB = I->second; - auto J = EB->Kill.find(St); - if (J == EB->Kill.end()) - return DeclSetTy(); - - return J->second; -} - -BoundsMapTy BoundsAnalysis::GetWidenedBoundsOffsets(const CFGBlock *B) { - auto I = BlockMap.find(B); - if (I == BlockMap.end()) - return BoundsMapTy(); - - ElevatedCFGBlock *EB = I->second; - return EB->In; -} - -BoundsExprMapTy BoundsAnalysis::GetWidenedBounds(const CFGBlock *B) { - // GetWidenedBoundsOffsets returns the mapping from _Nt_array_ptr to the - // offset by which its declared bounds should be widened. In this function we - // apply the offset to the declared upper bounds of the _Nt_array_ptr. - - BoundsExprMapTy Result; - for (const auto item : GetWidenedBoundsOffsets(B)) { - const VarDecl *V = item.first; - unsigned Offset = item.second; - - // We normalize the declared bounds to RangeBoundsExpr here so that we - // can easily apply the offset to the upper bound. - BoundsExpr *Bounds = S.NormalizeBounds(V); - if (RangeBoundsExpr *RBE = dyn_cast(Bounds)) { - const llvm::APInt - APIntOff(Ctx.getTargetInfo().getPointerWidth(0), Offset); - IntegerLiteral *WidenedOffset = - ExprCreatorUtil::CreateIntegerLiteral(Ctx, APIntOff); - - Expr *Lower = RBE->getLowerExpr(); - Expr *Upper = RBE->getUpperExpr(); - - // WidenedUpperBound = UpperBound + WidenedOffset. - Expr *WidenedUpper = ExprCreatorUtil::CreateBinaryOperator( - S, Upper, WidenedOffset, - BinaryOperatorKind::BO_Add); - - RangeBoundsExpr *R = new (Ctx) RangeBoundsExpr(Lower, WidenedUpper, - SourceLocation(), - SourceLocation()); - - Result[V] = R; - } - } - return Result; -} - -DeclSetTy BoundsAnalysis::GetBoundsWidenedAndNotKilled(const CFGBlock *B, - const Stmt *St) { - BoundsMapTy WidenedBounds = GetWidenedBoundsOffsets(B); - if (!WidenedBounds.size()) - return DeclSetTy(); - - DeclSetTy KilledBounds = GetKilledBounds(B, St); - - DeclSetTy Vars; - // WidenedBounds ∧ ~KilledBounds = WidenedBounds - KilledBounds. - for (auto item : Difference(WidenedBounds, KilledBounds)) - Vars.insert(item.first); - - return Vars; -} - -Expr *BoundsAnalysis::GetTerminatorCondition(const Expr *E) const { - if (const auto *BO = dyn_cast(E->IgnoreParens())) - return GetTerminatorCondition(BO->getRHS()); - - // According to C11 standard section 6.5.13, the logical AND Operator - // shall yield 1 if both of its operands compare unequal to 0; - // otherwise, it yields 0. The result has type int. - // If we have if (*p && *(p + 1)) where p is _Nt_array_ptr then - // it is casted to integer type and an IntegralCast is generated. Here - // we strip off the IntegralCast. - if (auto *CE = dyn_cast(E)) - if (CE->getCastKind() == CastKind::CK_IntegralCast) - return const_cast(CE->getSubExpr()); - return const_cast(E); -} - -Expr *BoundsAnalysis::GetTerminatorCondition(const CFGBlock *B) const { - if (const Stmt *S = B->getTerminatorStmt()) { - if (const auto *BO = dyn_cast(S)) - return GetTerminatorCondition(BO->getLHS()); - if (const auto *IfS = dyn_cast(S)) - return GetTerminatorCondition(IfS->getCond()); - if (const auto *WhileS = dyn_cast(S)) - return const_cast(WhileS->getCond()); - if (const auto *ForS = dyn_cast(S)) - return const_cast(ForS->getCond()); - if (const auto *SwitchS = dyn_cast(S)) - return const_cast(SwitchS->getCond()); - } - return nullptr; -} - -Expr *BoundsAnalysis::IgnoreCasts(const Expr *E) { - return Lex.IgnoreValuePreservingOperations(Ctx, const_cast(E)); -} - -bool BoundsAnalysis::IsNtArrayType(const VarDecl *V) const { - return V && (V->getType()->isCheckedPointerNtArrayType() || - V->getType()->isNtCheckedArrayType()); -} - -bool BoundsAnalysis::SkipBlock(const CFGBlock *B) const { - return !B || B == &Cfg->getExit(); -} - -template -T BoundsAnalysis::Intersect(T &A, T &B) const { - if (!A.size()) - return A; - - auto Ret = A; - if (!B.size()) { - Ret.clear(); - return Ret; - } - - // As the container iterated over by Ret is modified within the - // body of the loop, we need to evaluate Ret.end() each time. - for (auto I = Ret.begin(); I != Ret.end();) { - const auto *V = I->first; - - if (!B.count(V)) { - I = Ret.erase(I); - } else { - Ret[V] = std::min(Ret[V], B[V]); - ++I; - } - } - return Ret; -} - -template -T BoundsAnalysis::Union(T &A, T &B) const { - auto Ret = A; - for (const auto item : B) { - const auto *V = item.first; - auto I = item.second; - - if (!Ret.count(V)) { - if (I == 0) - Ret[V] = 1; - } else if (I == Ret[V]) - Ret[V] = I + 1; - } - return Ret; -} - -template -T BoundsAnalysis::Difference(T &A, U &B) const { - if (!A.size() || !B.size()) - return A; - - auto Ret = A; - for (auto I : A) { - const auto *V = I.first; - if (B.count(V)) - Ret.erase(V); - } - return Ret; -} - -template -bool BoundsAnalysis::Differ(T &A, T &B) const { - if (A.size() != B.size()) - return true; - - for (const auto item : A) { - if (!B.count(item.first)) - return true; - if (B[item.first] != item.second) - return true; - } - - for (const auto item : B) { - if (!A.count(item.first)) - return true; - if (A[item.first] != item.second) - return true; - } - return false; -} - -OrderedBlocksTy BoundsAnalysis::GetOrderedBlocks() { - // WidenedBounds is a DenseMap and hence is not suitable for iteration as its - // iteration order is non-deterministic. So we first need to order the - // blocks. The block IDs decrease from entry to exit. So we sort in the - // reverse order. - OrderedBlocksTy OrderedBlocks; - for (auto item : BlockMap) { - // item.first is the CFGBlock. - OrderedBlocks.push_back(item.first); - } - - llvm::sort(OrderedBlocks.begin(), OrderedBlocks.end(), - [] (const CFGBlock *A, const CFGBlock *B) { - return A->getBlockID() > B->getBlockID(); - }); - return OrderedBlocks; -} - -void BoundsAnalysis::DumpWidenedBounds(FunctionDecl *FD) { - OS << "--------------------------------------\n"; - OS << "In function: " << FD->getName() << "\n"; - - for (const CFGBlock *B : GetOrderedBlocks()) { - OS << "--------------------------------------"; - B->print(OS, Cfg, S.getLangOpts(), /* ShowColors */ true); - - BoundsMapTy Vars = GetWidenedBoundsOffsets(B); - using VarPairTy = std::pair; - - llvm::sort(Vars.begin(), Vars.end(), - [](VarPairTy A, VarPairTy B) { - return A.first->getQualifiedNameAsString().compare( - B.first->getQualifiedNameAsString()) < 0; - }); - - for (auto item : Vars) { - OS << "upper_bound(" - << item.first->getQualifiedNameAsString() << ") = " - << item.second << "\n"; - } - } -} - -} // end namespace clang diff --git a/clang/lib/Sema/BoundsWideningAnalysis.cpp b/clang/lib/Sema/BoundsWideningAnalysis.cpp index f949f2685c31..1e884776c0c9 100644 --- a/clang/lib/Sema/BoundsWideningAnalysis.cpp +++ b/clang/lib/Sema/BoundsWideningAnalysis.cpp @@ -14,4 +14,1261 @@ #include "clang/Sema/BoundsWideningAnalysis.h" namespace clang { + +//===---------------------------------------------------------------------===// +// Implementation of the methods in the BoundsWideningAnalysis class. This is +// the main class that implements the dataflow analysis for bounds widening of +// null-terminated arrays. This class uses helper methods from the +// BoundsWideningUtil class that are defined later in this file. +//===---------------------------------------------------------------------===// + +void BoundsWideningAnalysis::WidenBounds(FunctionDecl *FD, + StmtSetTy NestedStmts) { + assert(Cfg && "expected CFG to exist"); + + // Initialize the list of variables that are pointers to null-terminated + // arrays. This list will be initialized with the variables that are passed + // as parameters to the function. + InitNullTermPtrsInFunc(FD); + + // Note: By default, PostOrderCFGView iterates in reverse order. So we always + // get a reverse post order when we iterate PostOrderCFGView. + for (const CFGBlock *B : PostOrderCFGView(Cfg)) { + // SkipBlock will skip all null blocks and the exit block. PostOrderCFGView + // does not traverse any unreachable blocks. So at the end of this loop + // BlockMap only contains reachable blocks. + if (BWUtil.SkipBlock(B)) + continue; + + // Create a mapping from CFGBlock to ElevatedCFGBlock. + auto EB = new ElevatedCFGBlock(B); + BlockMap[B] = EB; + + // Compute Gen and Kill sets for the block and statements in the block. + ComputeGenKillSets(EB, NestedStmts); + + // Initialize the In and Out sets for the block. + InitBlockInOutSets(FD, EB); + } + + // WorkList store the blocks that remain to be processed for the fixedpoint + // computation. WorkList is a queue. So we maintain the reverse post order + // when we iterate WorkList. + // We initialize WorkList with the successor blocks of the entry block. + WorkListTy WorkList; + AddSuccsToWorkList(&Cfg->getEntry(), WorkList); + + // Compute the In and Out sets for blocks. This is the fixedpoint computation + // for the dataflow analysis. + while (!WorkList.empty()) { + ElevatedCFGBlock *EB = WorkList.next(); + WorkList.remove(EB); + + bool Changed = false; + Changed |= ComputeInSet(EB); + Changed |= ComputeOutSet(EB); + + // If the In or the Out set of the block has changed then we add all + // successors of the block to the WorkList. + if (Changed) + AddSuccsToWorkList(EB->Block, WorkList); + } +} + +void BoundsWideningAnalysis::ComputeGenKillSets(ElevatedCFGBlock *EB, + StmtSetTy NestedStmts) { + const Stmt *PrevStmt = nullptr; + + // Traverse statements in the block and compute Gen and Kill sets for each + // statement. + for (CFGBlock::const_iterator I = EB->Block->begin(), + E = EB->Block->end(); + I != E; ++I) { + CFGElement Elem = *I; + if (Elem.getKind() != CFGElement::Statement) + continue; + const Stmt *CurrStmt = Elem.castAs().getStmt(); + if (!CurrStmt) + continue; + + // Compute Gen and Kill sets for the current statement. + ComputeStmtGenKillSets(EB, CurrStmt, NestedStmts); + + // To compute the In sets for blocks we need to union the Gen and Kill sets + // of all statement in the block. This union operation is independent of + // the fixedpoint computation and hence can be done outside the loop to + // speed-up the fixedpoint loop. + ComputeUnionGenKillSets(EB, CurrStmt, PrevStmt); + + // Update the list of null-terminated arrays in the function with the + // null-terminated arrays that became part of Gen set for the current + // statement. + UpdateNullTermPtrsInFunc(EB, CurrStmt); + + EB->PrevStmtMap[CurrStmt] = PrevStmt; + PrevStmt = CurrStmt; + + // Store the last statement of the block. We will use it later in the block + // In, Gen and Kill set computations. + if (I == E - 1) + EB->LastStmt = CurrStmt; + } + + // Compute the Gen and Kill sets for the block. + ComputeBlockGenKillSets(EB); +} + +void BoundsWideningAnalysis::ComputeStmtGenKillSets(ElevatedCFGBlock *EB, + const Stmt *CurrStmt, + StmtSetTy NestedStmts) { + // Initialize the Gen and Kill sets for the statement. + EB->StmtGen[CurrStmt] = BoundsMapTy(); + EB->StmtKill[CurrStmt] = VarSetTy(); + + BoundsMapTy VarsAndBounds; + + const CFGBlock *CurrBlock = EB->Block; + const Stmt *TermStmt = CurrBlock->getTerminatorStmt(); + + // Determine whether CurrStmt generates a dataflow fact. + + // A conditional statement that dereferences a variable that is a pointer to + // a null-terminated array can generate a dataflow fact. For example: if (*(p + // + 1)) The conditional will always be the terminator statement of the block. + if (TermStmt && !isa(TermStmt) && + CurrStmt == CurrBlock->getLastCondition()) { + GetVarsAndBoundsInPtrDeref(EB, VarsAndBounds); + + // A bounds declaration of a null-terminated array generates a dataflow fact. + // For example: _Nt_array_ptr p : bounds(p, p + 1); + } else if (const auto *DS = dyn_cast(CurrStmt)) { + for (const Decl *D : DS->decls()) { + if (const auto *V = dyn_cast(D)) { + if (!V->isInvalidDecl()) { + GetVarsAndBoundsInDecl(EB, V, VarsAndBounds); + + // Additionally, a where clause on a declaration can generate a + // dataflow fact. + // For example: int x = strlen(p) _Where p : bounds(p, p + x); + GetVarsAndBoundsInWhereClause(EB, V->getWhereClause(), VarsAndBounds); + } + } + } + + // A where clause on an expression statement (which is represented in the + // AST as a ValueStmt) can generate a dataflow fact. + // For example: x = strlen(p) _Where p : bounds(p, p + x); + } else if (const auto *VS = dyn_cast(CurrStmt)) { + GetVarsAndBoundsInWhereClause(EB, VS->getWhereClause(), VarsAndBounds); + + // A where clause on a null statement (meaning a standalone where clause) can + // generate a dataflow fact. + // For example: _Where p : bounds(p, p + 1); + // TODO: Currently, a null statement does not occur in the list of + // statements of a block. As a result, there are no Gen and Kill sets for a + // null statement. So currently the above example does not generate a + // dataflow fact. + } else if (const auto *NS = dyn_cast(CurrStmt)) { + GetVarsAndBoundsInWhereClause(EB, NS->getWhereClause(), VarsAndBounds); + } + + // A modification of a variable that occurs either in the lower bounds + // expression or an upper bounds expression of a null-terminated array does + // the following: + // 1. Kills the widened bounds of that null-terminated array, and + // 2. Resets the bounds of the null-terminated array to its declared bounds. + // For example: + // int i; + // _Nt_array_ptr p : bounds(p, p + i); + // if (*(p + i)) { // widen bounds of p to bounds(p, p + i + 1) + + // i = 0; // kill the widened bounds of p, and + // // reset bounds of p to bounds(p, p + i) + // } + + // If a variable modified by CurrStmt occurs in the bounds expression of a + // null-terminated array then the bounds of that null-terminated array should + // be killed and its bounds should be reset to its declared bounds. + // Note: Skip top-level statements that are nested in another top-level + // statement. + if (NestedStmts.find(CurrStmt) == NestedStmts.end()) + GetVarsAndBoundsForModifiedVars(EB, CurrStmt, VarsAndBounds); + + // Using the mapping of variables to bounds expressions in VarsAndBounds fill + // the Gen and Kill sets for the current statement. + FillStmtGenKillSets(EB, CurrStmt, VarsAndBounds); +} + +void BoundsWideningAnalysis::ComputeUnionGenKillSets(ElevatedCFGBlock *EB, + const Stmt *CurrStmt, + const Stmt *PrevStmt) { + // If this is the first statement in the block. + if (!PrevStmt) { + EB->UnionGen[CurrStmt] = EB->StmtGen[CurrStmt]; + EB->UnionKill[CurrStmt] = EB->StmtKill[CurrStmt]; + return; + } + + EB->UnionKill[CurrStmt] = BWUtil.Union(EB->UnionKill[PrevStmt], + EB->StmtKill[CurrStmt]); + + auto Diff = BWUtil.Difference(EB->UnionGen[PrevStmt], + EB->StmtKill[CurrStmt]); + EB->UnionGen[CurrStmt] = BWUtil.Union(Diff, EB->StmtGen[CurrStmt]); +} + +void BoundsWideningAnalysis::ComputeBlockGenKillSets(ElevatedCFGBlock *EB) { + // Initialize the Gen and Kill sets for the block. + EB->Gen = BoundsMapTy(); + EB->Kill = VarSetTy(); + + if (EB->LastStmt) { + EB->Gen = EB->UnionGen[EB->LastStmt]; + EB->Kill = EB->UnionKill[EB->LastStmt]; + } +} + +bool BoundsWideningAnalysis::ComputeInSet(ElevatedCFGBlock *EB) { + const CFGBlock *CurrBlock = EB->Block; + auto OrigIn = EB->In; + + // Iterate through all the predecessor blocks of EB. + for (const CFGBlock *PredBlock : CurrBlock->preds()) { + auto BlockIt = BlockMap.find(PredBlock); + if (BlockIt == BlockMap.end()) + continue; + + ElevatedCFGBlock *PredEB = BlockIt->second; + + // To compute the In set for the block we need to intersect the Out sets of + // all preds of the current block. In order to simplify the intersection + // operation we "prune" (or pre-process) the Out sets of preds here + // according to various conditions. The intersection then happens on the + // pruned Out sets. + BoundsMapTy PrunedOutSet = PruneOutSet(PredEB, EB); + + EB->In = BWUtil.Intersect(EB->In, PrunedOutSet); + } + + // Return true if the In set has changed, false otherwise. + return !BWUtil.IsEqual(EB->In, OrigIn); +} + +BoundsMapTy BoundsWideningAnalysis::PruneOutSet( + ElevatedCFGBlock *PredEB, ElevatedCFGBlock *CurrEB) const { + + const CFGBlock *PredBlock = PredEB->Block; + const CFGBlock *CurrBlock = CurrEB->Block; + + // If the edge from pred to the current block is a fallthrough edge then the + // Out of the pred should simply "flow" through to the current block (meaning + // PredOut should simply be intersected with the In of the current block). + // Fallthrough edges are generated in case of loops. For example: + + // _Nt_array_ptr p : bounds(p, p + 1); + // while (*(p + 1)) + // a = 1; + + // B1: pred: Entry, succ: B2 + // _Nt_array_ptr p : bounds(p, p + 1); + + // B2: pred: B1, B4, succ: B3 + + // B3: pred: B2, succ: B4 + // while (*(p + 1)) + + // B4: pred, B3, succ: B2 + // a = 1; + + // In the above example, the edges B1->B2, B2->B3 and B4->B2 are fallthrough + // edges. So in this case, the Out sets of the pred blocks should simply flow + // through to the successor blocks. Meaning we do not need to prune the Out + // set of the pred block. + + if (BWUtil.IsFallthroughEdge(PredBlock, CurrBlock)) + return PredEB->Out; + + BoundsMapTy PrunedOutSet = PredEB->Out; + + // Check if the edge from pred to the current block is a true edge. + bool IsEdgeTrue = BWUtil.IsTrueEdge(PredBlock, CurrBlock); + + // Get the StmtIn of the last statement in the pred block. If the pred + // block does not have any statements then StmtInOfLastStmtOfPred is set to + // the In set of the pred block. + BoundsMapTy StmtInOfLastStmtOfPred = GetStmtIn(PredBlock, PredEB->LastStmt); + + // Check if the current block is a case of a switch-case. + bool IsSwitchCase = BWUtil.IsSwitchCaseBlock(CurrBlock); + + // If the current block is a case of a switch-case, check if the case label + // tests for null. CaseLabelTestsForNull returns false for the default case. + bool IsCaseLabelNull = IsSwitchCase && + BWUtil.CaseLabelTestsForNull(CurrBlock); + + // PredEB->Out may contain the widened bounds "E + 1" for some variable V. + // Here, we need to determine if "E + 1" should make it to the In set of + // the current block. To determine this, we need to check if the + // dereference is at the upper bound and if we are on a true edge. Else we + // will reset the bounds of V in PrunedOutSet to the bounds of V in + // StmtInOfLastStmtOfPred. + for (auto VarBoundsPair : PredEB->Out) { + const VarDecl *V = VarBoundsPair.first; + auto StmtInIt = StmtInOfLastStmtOfPred.find(V); + + // If V is not present in StmtIn of the last statement (maybe as a result + // of being killed before the last statement) then V cannot be widened in + // this block. So we remove V from the computation of the In set for this + // block. For example: + + // B1: + // 1: _Nt_array_ptr p : bounds(p, p + i); + // 2: i = 0; + // 3: if (*(p + i)) { + // B2: + // 4: a = 1; + // } + + // The bounds of p are killed at statement 2. So StmtIn[3] does not + // contain p but Gen[3] will contain "p : bounds(p, p + i + 1)". So we + // need to remove p from the In set of successor block. + + // Note: We could have done this as part of the Intersect function but + // that would have meant teaching Intersect about StmtIn, etc which would + // have complicated the logic for intersection. So to keep the Intersect + // method simple we check if V is in StmtIn of the last statement of pred + // and accordingly remove it from PrunedOutSet. + + if (StmtInIt == StmtInOfLastStmtOfPred.end()) { + PrunedOutSet.erase(V); + continue; + } + + RangeBoundsExpr *BoundsOfVInStmtIn = StmtInIt->second; + + // If the edge from pred to the current block is not a true edge then we + // cannot widen the bounds upon entry to the current block. So we reset + // the bounds to those before the last statement in the pred block. + // For example: + + // Block B1: + // if (*(p + 1)) { + // Block B2: + // } else { + // Block B3: + // } + + // The edge B1->B2 is a true edge for the condition "if (*(p + 1))". So + // we can widen the bounds upon entry to B2. + // The edge B1->B3 is a false edge for the condition "if (*(p + 1))". So + // we cannot widen the bounds upon entry to B3. + + // Note: Switch cases are handled separately later in this function. + + if (!IsSwitchCase && !IsEdgeTrue) { + PrunedOutSet[V] = BoundsOfVInStmtIn; + continue; + } + + // If the terminating condition of the pred block does not + // dereference V at the current upper bound, then we cannot widen the + // bounds upon entry to the current block. So we reset the bounds of V to + // those before the last statement in pred. For example: + + // Block 1: + // 1: _Nt_array_ptr p : bounds(p, p + 1); + // 2: if (*(p)) { + // Block 2: + // } + // 3: if (*(p + 2)) { + // Block 3: + // } + + // In the example above, the conditionals at statements 2 and 3 do not + // dereference p at its upper bound. So we cannot widen the bounds of p + // upon entry to blocks 2 and 3. + + bool IsDerefAtUpperBound = + PredEB->TermCondDerefExpr && BoundsOfVInStmtIn != Top && + Lex.CompareExprSemantically(PredEB->TermCondDerefExpr, + BoundsOfVInStmtIn->getUpperExpr()); + + if (!IsDerefAtUpperBound) { + PrunedOutSet[V] = BoundsOfVInStmtIn; + continue; + } + + // If we are here then it means that the terminating condition of the + // pred block dereferences V at its current upper bound. + + // If we are in a block that is a case of a switch-case. + if (IsSwitchCase) { + // We cannot widen the bounds in the following scenarios: + + // 1. The case label tests for null. For example: + + // _Nt_array_ptr p : bounds(p, p + 1); + // switch(*(p + 1)) { + // case '\0': // cannot widen here. + // } + + if (IsCaseLabelNull) + PrunedOutSet[V] = BoundsOfVInStmtIn; + + // 2. We are in a default case and there is no other case that tests + // for null. For example: + + // switch(*(p + 1)) { + // default: can widen here. + // case '\0': cannot widen here. + // case 'a': can widen here. + // } + + // switch(*(p + 1)) { + // default: cannot widen here. + // case 'a': can widen here. + // case 'b': can widen here. + // } + + else if (isa(CurrBlock->getLabel()) && + !BWUtil.ExistsNullCaseLabel(PredBlock)) + PrunedOutSet[V] = BoundsOfVInStmtIn; + } + } + + return PrunedOutSet; +} + +bool BoundsWideningAnalysis::ComputeOutSet(ElevatedCFGBlock *EB) { + auto OrigOut = EB->Out; + + auto Diff = BWUtil.Difference(EB->In, EB->Kill); + EB->Out = BWUtil.Union(Diff, EB->Gen); + + // Return true if the Out set has changed, false otherwise. + return !BWUtil.IsEqual(EB->Out, OrigOut); +} + +void BoundsWideningAnalysis::InitBlockInOutSets(FunctionDecl *FD, + ElevatedCFGBlock *EB) { + // Initialize the In and Out sets for the entry block. + if (EB->Block == &Cfg->getEntry()) { + EB->In = BoundsMapTy(); + EB->Out = BoundsMapTy(); + + // If the function has parameters that generate dataflow facts then add + // those to the In and Out sets of the entry block. + BoundsMapTy VarsAndBounds; + for (const ParmVarDecl *PD : FD->parameters()) { + // Bounds declarations on a function parameter can generate a dataflow + // fact. + // For example: void foo(_Nt_array_ptr p : bounds(p, p + 1)) {} + GetVarsAndBoundsInDecl(EB, PD, VarsAndBounds); + + // Additionally, a where clause on a function parameter can generate a + // dataflow fact. + // For example: void foo(int x _Where p : bounds(p, p + 1)) {} + GetVarsAndBoundsInWhereClause(EB, PD->getWhereClause(), VarsAndBounds); + } + + for (auto VarBoundsPair : VarsAndBounds) { + const VarDecl *V = VarBoundsPair.first; + RangeBoundsExpr *R = VarBoundsPair.second; + + EB->In[V] = R; + EB->Out[V] = R; + } + + } else { + // Initialize the In and Out sets for the rest of the blocks to Top. + for (const VarDecl *V : AllNullTermPtrsInFunc) { + EB->In[V] = Top; + EB->Out[V] = Top; + } + } +} + +void BoundsWideningAnalysis::AddSuccsToWorkList(const CFGBlock *CurrBlock, + WorkListTy &WorkList) { + if (!CurrBlock) + return; + + for (const CFGBlock *SuccBlock : CurrBlock->succs()) { + if (!BWUtil.SkipBlock(SuccBlock)) + WorkList.append(BlockMap[SuccBlock]); + } +} + +BoundsMapTy BoundsWideningAnalysis::GetStmtOut(const CFGBlock *B, + const Stmt *CurrStmt) const { + // Note: This method can be called from outside the BoundsWideningAnalysis + // class to get the widened bounds after a statement. + if (!B) + return BoundsMapTy(); + + auto BlockIt = BlockMap.find(B); + if (BlockIt == BlockMap.end()) + return BoundsMapTy(); + + ElevatedCFGBlock *EB = BlockIt->second; + + if (CurrStmt) { + auto Diff = BWUtil.Difference(EB->In, EB->UnionKill[CurrStmt]); + return BWUtil.Union(Diff, EB->UnionGen[CurrStmt]); + } + return EB->In; +} + +BoundsMapTy BoundsWideningAnalysis::GetStmtIn(const CFGBlock *B, + const Stmt *CurrStmt) const { + // Note: This method can be called from outside the BoundsWideningAnalysis + // class to get the widened bounds before a statement. + if (!B) + return BoundsMapTy(); + + auto BlockIt = BlockMap.find(B); + if (BlockIt == BlockMap.end()) + return BoundsMapTy(); + + ElevatedCFGBlock *EB = BlockIt->second; + + // StmtIn of a statement is equal to the StmtOut of its previous statement. + return GetStmtOut(B, EB->PrevStmtMap[CurrStmt]); +} + +BoundsMapTy BoundsWideningAnalysis::GetBoundsWidenedAndNotKilled( + const CFGBlock *B, const Stmt *CurrStmt) const { + + auto BlockIt = BlockMap.find(B); + if (BlockIt == BlockMap.end()) + return BoundsMapTy(); + + ElevatedCFGBlock *EB = BlockIt->second; + + BoundsMapTy StmtInOfCurrStmt = GetStmtIn(B, CurrStmt); + return BWUtil.Difference(StmtInOfCurrStmt, EB->StmtKill[CurrStmt]); +} + +void BoundsWideningAnalysis::InitNullTermPtrsInFunc(FunctionDecl *FD) { + // Initialize the list of variables that are pointers to null-terminated + // arrays to the null-terminated arrays that are passed as parameters to the + // function. + for (const ParmVarDecl *PD : FD->parameters()) { + if (BWUtil.IsNtArrayType(PD)) + AllNullTermPtrsInFunc.insert(PD); + } +} + +void BoundsWideningAnalysis::UpdateNullTermPtrsInFunc(ElevatedCFGBlock *EB, + const Stmt *CurrStmt) { + // Add variables occurring in StmtGen for the current statement to the list + // of variables that are pointers to null-terminated arrays. + for (auto VarBoundsPair : EB->StmtGen[CurrStmt]) + AllNullTermPtrsInFunc.insert(VarBoundsPair.first); +} + +void BoundsWideningAnalysis::FillStmtGenKillSets(ElevatedCFGBlock *EB, + const Stmt *CurrStmt, + BoundsMapTy &VarsAndBounds) { + for (auto VarBoundsPair : VarsAndBounds) { + const VarDecl *V = VarBoundsPair.first; + RangeBoundsExpr *R = VarBoundsPair.second; + + EB->StmtGen[CurrStmt][V] = R; + EB->StmtKill[CurrStmt].insert(V); + } +} + +void BoundsWideningAnalysis::GetVarsAndBoundsInDecl( + ElevatedCFGBlock *EB, const VarDecl *V, BoundsMapTy &VarsAndBounds) { + + if (BWUtil.IsNtArrayType(V)) { + BoundsExpr *NormalizedBounds = SemaRef.NormalizeBounds(V); + VarsAndBounds[V] = dyn_cast_or_null(NormalizedBounds); + } +} + +void BoundsWideningAnalysis::GetVarsAndBoundsInWhereClause( + ElevatedCFGBlock *EB, WhereClause *WC, BoundsMapTy &VarsAndBounds) { + + if (!WC) + return; + + for (const auto *Fact : WC->getFacts()) { + auto *F = dyn_cast(Fact); + if (!F) + continue; + + VarDecl *V = F->getVarDecl(); + if (BWUtil.IsNtArrayType(V)) { + BoundsExpr *NormalizedBounds = SemaRef.NormalizeBounds(F); + VarsAndBounds[V] = dyn_cast_or_null(NormalizedBounds); + } + } +} + +void BoundsWideningAnalysis::GetVarsAndBoundsInPtrDeref( + ElevatedCFGBlock *EB, BoundsMapTy &VarsAndBounds) { + + // Get the terminating condition of the block. + // The CFG for a compound condition like "if (e1 && e2 && e3)" is as follows: + + // B1->B2 (edge condition e1) + // B2->B3 (edge condition e2) + // B3->B4 (edge condition e3) + + // So we see that each condition becomes an edge in the CFG. + // getTerminatorStmt() would give us the entire condition "e1 && e2 && e3" + // while getLastCondition() would give us e1 for the first edge e2 for the + // second and so on. + const Expr *TermCond = EB->Block->getLastCondition(); + if (!TermCond) + return; + + // If the terminating condition is a dereference expression get that + // expression. For example, from a terminating condition like "if (*(p + + // 1))", extract the expression "p + 1". + Expr *DerefExpr = BWUtil.GetDerefExpr(TermCond); + if (!DerefExpr) + return; + + // Store the dereference condition for the block. We will use it later in the + // In set calculation. + EB->TermCondDerefExpr = DerefExpr; + + // Get the variable in the expression that is a pointers to a null-terminated + // array. For example: On a dereference expression like "*(p + i + j + 1)" + // GetNullTermPtrInExpr() will return p if p is a pointer to a null-terminated + // array. + // Note: We assume that a dereference expression can only contain at most one + // pointer to a null-terminated array. + const VarDecl *NullTermPtrInExpr = BWUtil.GetNullTermPtrInExpr(DerefExpr); + if (!NullTermPtrInExpr) + return; + + // Get all variables that are pointers to null-terminated arrays and in whose + // upper bounds expressions the variable NullTermPtrInExpr occurs. For + // example: + + // _Nt_array_ptr p : bounds(p, p); + // _Nt_array_ptr q : bounds(q, p + i); + // _Nt_array_ptr r : bounds(r, p + 2); + // _Nt_array_ptr s : bounds(p, s); + + // On a dereference expression like "*(p + i + j + 1)" + // GetNullTermPtrsWithVarsInUpperBounds() will return {p, q, r} because p + // occurs in the upper bounds expressions of p, q and r. + + VarSetTy Vars; + Vars.insert(NullTermPtrInExpr); + + VarSetTy NullTermPtrsWithVarsInUpperBounds; + BWUtil.GetNullTermPtrsWithVarsInUpperBounds( + Vars, NullTermPtrsWithVarsInUpperBounds); + + // Now, the bounds of all variables in NullTermPtrsWithVarsInUpperBounds can + // potentially be widened to bounds(lower, DerefExpr + 1). + + for (const VarDecl *V : NullTermPtrsWithVarsInUpperBounds) { + BoundsExpr *NormalizedBounds = SemaRef.NormalizeBounds(V); + RangeBoundsExpr *R = dyn_cast_or_null(NormalizedBounds); + + // DerefExpr potentially widens V. So we need to add "V:bounds(lower, + // DerefExpr + 1)" to the Gen set. + + // TODO: Here, we always consider the lower bound from the declared bounds + // of V. But this may be incorrect in case a where clause redeclares the + // lower bound of V. This needs to be handled. + + RangeBoundsExpr *WidenedBounds = + new (Ctx) RangeBoundsExpr(R->getLowerExpr(), + BWUtil.AddOffsetToExpr(DerefExpr, 1), + SourceLocation(), SourceLocation()); + VarsAndBounds[V] = WidenedBounds; + } +} + +void BoundsWideningAnalysis::GetVarsAndBoundsForModifiedVars( + ElevatedCFGBlock *EB, const Stmt *CurrStmt, BoundsMapTy &VarsAndBounds) { + + // Get variables modified by CurrStmt. + VarSetTy ModifiedVars; + BWUtil.GetModifiedVars(CurrStmt, ModifiedVars); + + // Get the set of variables that are pointers to null-terminated arrays and + // in whose lower and upper bounds expressions the modified variables occur. + VarSetTy NullTermPtrsWithVarsInBounds; + BWUtil.GetNullTermPtrsWithVarsInLowerBounds(ModifiedVars, + NullTermPtrsWithVarsInBounds); + BWUtil.GetNullTermPtrsWithVarsInUpperBounds(ModifiedVars, + NullTermPtrsWithVarsInBounds); + + // For each null-terminated array we need to reset the bounds to its declared + // bounds. + for (const VarDecl *V : NullTermPtrsWithVarsInBounds) { + BoundsExpr *NormalizedBounds = SemaRef.NormalizeBounds(V); + VarsAndBounds[V] = dyn_cast_or_null(NormalizedBounds); + } +} + +void BoundsWideningAnalysis::DumpWidenedBounds(FunctionDecl *FD) { + 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 << ", "; + } + } + + bool IsBlockEmpty = true; + for (CFGElement Elem : *CurrBlock) { + if (Elem.getKind() != CFGElement::Statement) + continue; + + const Stmt *CurrStmt = Elem.castAs().getStmt(); + if (!CurrStmt) + continue; + + BoundsMapTy WidenedBounds = GetStmtIn(CurrBlock, CurrStmt); + + std::vector Vars; + for (auto VarBoundsPair : WidenedBounds) + Vars.push_back(VarBoundsPair.first); + + llvm::sort(Vars.begin(), Vars.end(), + [](const VarDecl *A, const VarDecl *B) { + return A->getQualifiedNameAsString().compare( + B->getQualifiedNameAsString()) < 0; + }); + + std::string Str; + llvm::raw_string_ostream SS(Str); + CurrStmt->printPretty(SS, nullptr, Ctx.getPrintingPolicy()); + + // Print the current statement. + OS << "\n Widened bounds before stmt: " << SS.str(); + if (SS.str().back() != '\n') + OS << "\n"; + + IsBlockEmpty = false; + + if (Vars.size() == 0) + OS << " \n"; + + for (const VarDecl *V : Vars) { + RangeBoundsExpr *Bounds = WidenedBounds[V]; + if (Bounds == Top) { + // If this is the only variable and its bounds are Top, we need to + // print . + if (Vars.size() == 1) + OS << " \n"; + continue; + } + + Expr *Lower = Bounds->getLowerExpr(); + Expr *Upper = Bounds->getUpperExpr(); + + OS << " " << V->getQualifiedNameAsString() << ": bounds("; + Lower->printPretty(OS, nullptr, Ctx.getPrintingPolicy()); + OS << ", "; + Upper->printPretty(OS, nullptr, Ctx.getPrintingPolicy()); + OS << ")\n"; + } + } + + if (IsBlockEmpty) + OS << "\n"; + } +} + +OrderedBlocksTy BoundsWideningAnalysis::GetOrderedBlocks() const { + // We order the CFG blocks based on block ID. Block IDs decrease from entry + // to exit. So we sort in the reverse order. + OrderedBlocksTy OrderedBlocks; + for (auto BlockEBPair : BlockMap) { + const CFGBlock *B = BlockEBPair.first; + OrderedBlocks.push_back(B); + } + + llvm::sort(OrderedBlocks.begin(), OrderedBlocks.end(), + [] (const CFGBlock *A, const CFGBlock *B) { + return A->getBlockID() > B->getBlockID(); + }); + return OrderedBlocks; +} +// end of methods for the BoundsWideningAnalysis class. + +//===---------------------------------------------------------------------===// +// Implementation of the methods in the BoundsWideningUtil class. This class +// contains helper methods that are used by the BoundsWideningAnalysis class to +// perform the dataflow analysis. +//===---------------------------------------------------------------------===// + +bool BoundsWideningUtil::IsSubRange(RangeBoundsExpr *B1, + RangeBoundsExpr *B2) const { + // If B2 is a subrange of B1, then + // B2.Lower >= B1.Lower and B2.Upper <= B1.Upper + + // Examples: + // B1 = bounds(p, p + 5) and B2 = bounds(p + 1, p + 3) ==> True + // B1 = bounds(p, p + 5) and B2 = bounds(p, p + 5) ==> True + // B1 = bounds(p, p + 5) and B2 = bounds(p, p + 2) ==> True + // B1 = bounds(p, p + 5) and B2 = bounds(p + 4, p + 5) ==> True + // B1 = bounds(p, p + 5) and B2 = bounds(p, p) ==> True + // B1 = bounds(p, p + 5) and B2 = bounds(p + 5, p + 5) ==> True + // B1 = bounds(p, p + 5) and B2 = bounds(p, p + 6) ==> False + // B1 = bounds(p, p + 5) and B2 = bounds(p - 1, p + 1) ==> False + // B1 = bounds(p, p + 5) and B2 = bounds(p + 6, p + 10) ==> False + // B1 = bounds(p + 5, p + 6) and B2 = bounds(p, p + 5) ==> False + // B1 = bounds(p, p + 5) and B2 = bounds(p, p + x) ==> False + // B1 = bounds(p, p + x) and B2 = bounds(p, p + x + y) ==> False + + // To determine if B2 is a subrange of B1 we check if: + // B2.Lower - B1.Lower >= 0 and B1.Upper - B2.Upper >= 0 + + Expr *Lower1 = B1->getLowerExpr(); + Expr *Upper1 = B1->getUpperExpr(); + + Expr *Lower2 = B2->getLowerExpr(); + Expr *Upper2 = B2->getUpperExpr(); + + llvm::APSInt Zero(Ctx.getTargetInfo().getIntWidth(), 0); + llvm::APSInt Offset; + + // Lex.GetExprIntDiff returns false if the two input expressions are not + // comparable. This may happen if either of the expressions contains a + // variable that is not present in the other. + if (!Lex.GetExprIntDiff(Lower2, Lower1, Offset)) + return false; + + if (llvm::APSInt::compareValues(Offset, Zero) < 0) + return false; + + if (!Lex.GetExprIntDiff(Upper1, Upper2, Offset)) + return false; + + return llvm::APSInt::compareValues(Offset, Zero) >= 0; +} + +bool BoundsWideningUtil::ExistsNullCaseLabel(const CFGBlock *CurrBlock) const { + for (const CFGBlock *SuccBlock : CurrBlock->succs()) { + if (!SkipBlock(SuccBlock) && CaseLabelTestsForNull(SuccBlock)) + return true; + } + return false; +} + +bool BoundsWideningUtil::IsSwitchCaseBlock(const CFGBlock *CurrBlock) const { + const Stmt *BlockLabel = CurrBlock->getLabel(); + return BlockLabel && + (isa(BlockLabel) || + isa(BlockLabel)); +} + +bool BoundsWideningUtil::CaseLabelTestsForNull( + const CFGBlock *CurrBlock) const { + + const Stmt *BlockLabel = CurrBlock->getLabel(); + assert(BlockLabel && "invalid switch case"); + + if (isa(BlockLabel)) + return false; + + const auto *CS = dyn_cast(BlockLabel); + + // We mimic how clang (in SemaStmt.cpp) gets the value of a switch case. It + // invokes EvaluateKnownConstInt and we do the same here. SemaStmt has + // already extended/truncated the case value to fit the integer range and + // EvaluateKnownConstInt gives us that value. + const Expr *LHS = CS->getLHS(); + if (!LHS) + return true; + + llvm::APSInt LHSVal = LHS->EvaluateKnownConstInt(Ctx); + llvm::APSInt LHSZero (LHSVal.getBitWidth(), LHSVal.isUnsigned()); + if (llvm::APSInt::compareValues(LHSVal, LHSZero) == 0) + return true; + + // If the case statement is not of the form "case LHS ... RHS" (a GNU + // extension) then we are done. We only needed to check the LHS value which + // is the case label for the current case. We return false because we have + // determined that the current case label is non-null. + if (!CS->caseStmtIsGNURange()) + return false; + + // If we reach are here it means we are in a range-base case statement of the + // form "case LHS ... RHS" (a GNU extension). + const Expr *RHS = CS->getRHS(); + if (!RHS) + return true; + + llvm::APSInt RHSVal = RHS->EvaluateKnownConstInt(Ctx); + llvm::APSInt RHSZero (RHSVal.getBitWidth(), RHSVal.isUnsigned()); + if (llvm::APSInt::compareValues(RHSVal, RHSZero) == 0) + return true; + + // Return true if 0 is contained within the range [LHS, RHS]. + return (LHSVal <= LHSZero && RHSZero <= RHSVal) || + (LHSVal >= LHSZero && RHSZero >= RHSVal); +} + +bool BoundsWideningUtil::IsFallthroughEdge(const CFGBlock *PredBlock, + const CFGBlock *CurrBlock) const { + // A fallthrough edge between two blocks is always a true edge. If PredBlock + // has only one successor and CurrBlock is that successor then it means the + // edge between PredBlock and CurrBlock is a fallthrough edge. + return PredBlock->succ_size() == 1 && + CurrBlock == *(PredBlock->succs().begin()); +} + +bool BoundsWideningUtil::IsTrueEdge(const CFGBlock *PredBlock, + const CFGBlock *CurrBlock) const { + // Return false if PredBlock does not have any successors. + if (PredBlock->succ_empty()) + return false; + + // A fallthrough edge is always a true edge. + if (IsFallthroughEdge(PredBlock, CurrBlock)) + return true; + + // Get the last successor in the list of successors of PredBlock. + const CFGBlock *LastSucc = *(PredBlock->succs().end() - 1); + + // If PredBlock has multiple successors, then a successor on a false edge + // would always be last in the list of successors of PredBlock. + if (CurrBlock == LastSucc) + return PredBlock->succ_size() == 1; + + // Check if CurrBlock is a successor of PredBlock. If CurrBlock is not a + // successor of PredBlock then there is no edge between PredBlock and + // CurrBlock. So return false. + for (const CFGBlock *SuccBlock : PredBlock->succs()) { + if (CurrBlock == SuccBlock) + return true; + } + + return false; +} + +void BoundsWideningUtil::GetModifiedVars(const Stmt *CurrStmt, + VarSetTy &ModifiedVars) const { + // Get all variables modified by CurrStmt or statements nested in CurrStmt. + if (!CurrStmt) + return; + + Expr *E = nullptr; + + // If the variable is modified using a unary operator, like ++I or I++. + if (const auto *UO = dyn_cast(CurrStmt)) { + if (UO->isIncrementDecrementOp()) { + assert(UO->getSubExpr() && "invalid UnaryOperator expression"); + E = IgnoreCasts(UO->getSubExpr()); + } + + // Else if the variable is being assigned to, like I = ... + } else if (const auto *BO = dyn_cast(CurrStmt)) { + if (BO->isAssignmentOp()) + E = IgnoreCasts(BO->getLHS()); + } + + if (const auto *D = dyn_cast_or_null(E)) + if (const auto *V = dyn_cast_or_null(D->getDecl())) + ModifiedVars.insert(V); + + for (const Stmt *NestedStmt : CurrStmt->children()) + GetModifiedVars(NestedStmt, ModifiedVars); +} + +void BoundsWideningUtil::GetNullTermPtrsWithVarsInLowerBounds( + VarSetTy &Vars, VarSetTy &NullTermPtrsWithVarsInLowerBounds) const { + + // Get the set of variables that are pointers to null-terminated arrays and + // in whose lower bounds expressions the variables in Vars occur. + + for (const VarDecl *V : Vars) { + auto VarPtrIt = BoundsVarsLower.find(V); + if (VarPtrIt != BoundsVarsLower.end()) { + for (const VarDecl *Ptr : VarPtrIt->second) + if (!Ptr->isInvalidDecl() && IsNtArrayType(Ptr)) + NullTermPtrsWithVarsInLowerBounds.insert(Ptr); + } + } +} + +void BoundsWideningUtil::GetNullTermPtrsWithVarsInUpperBounds( + VarSetTy &Vars, VarSetTy &NullTermPtrsWithVarsInUpperBounds) const { + + // Get the set of variables that are pointers to null-terminated arrays and + // in whose upper bounds expressions the variables in Vars occur. + + for (const VarDecl *V : Vars) { + auto VarPtrIt = BoundsVarsUpper.find(V); + if (VarPtrIt != BoundsVarsUpper.end()) { + for (const VarDecl *Ptr : VarPtrIt->second) + if (!Ptr->isInvalidDecl() && IsNtArrayType(Ptr)) + NullTermPtrsWithVarsInUpperBounds.insert(Ptr); + } + } +} + +Expr *BoundsWideningUtil::AddOffsetToExpr(Expr *E, unsigned Offset) const { + // Returns the expression E + Offset. + // Note: This function only returns the expression E + Offset and does not + // actually evaluate the expression. So if E does not overflow then E + + // Offset does not overflow here. However, E + Offset may later overflow when + // the preorder AST performs constant folding, for example in case E is e + + // MAX_INT and Offset is 1. + + const llvm::APInt + APIntOff(Ctx.getTargetInfo().getPointerWidth(0), Offset); + + IntegerLiteral *WidenedOffset = + ExprCreatorUtil::CreateIntegerLiteral(Ctx, APIntOff); + + return ExprCreatorUtil::CreateBinaryOperator(SemaRef, E, WidenedOffset, + BinaryOperatorKind::BO_Add); +} + +Expr *BoundsWideningUtil::GetDerefExpr(const Expr *TermCond) const { + if (!TermCond) + return nullptr; + + Expr *E = const_cast(TermCond); + + // According to C11 standard section 6.5.13, the logical AND Operator shall + // yield 1 if both of its operands compare unequal to 0; otherwise, it yields + // 0. The result has type int. An IntegralCast is generated for "if (e1 && + // e2)" Here we strip off the IntegralCast. + if (auto *CE = dyn_cast(E)) { + if (CE->getCastKind() == CastKind::CK_IntegralCast) + E = CE->getSubExpr(); + } + + if (auto *CE = dyn_cast(E)) + if (CE->getCastKind() == CastKind::CK_LValueToRValue) + E = CE->getSubExpr(); + + E = IgnoreCasts(E); + + // A dereference expression can contain an array subscript or a pointer + // dereference. + + // If a dereference expression is of the form "*(p + i)". + if (auto *UO = dyn_cast(E)) { + if (UO->getOpcode() == UO_Deref) + return IgnoreCasts(UO->getSubExpr()); + + // Else if a dereference expression is an array access. An array access can + // be written A[i] or i[A] (both are equivalent). getBase() and getIdx() + // always present the normalized view: A[i]. In this case getBase() returns + // "A" and getIdx() returns "i". + } else if (auto *AE = dyn_cast(E)) { + return ExprCreatorUtil::CreateBinaryOperator(SemaRef, AE->getBase(), + AE->getIdx(), + BinaryOperatorKind::BO_Add); + } + return nullptr; +} + +const VarDecl *BoundsWideningUtil::GetNullTermPtrInExpr(Expr *E) const { + // Get the variable in an expression that is a pointer to a null-terminated + // array. + // Note: We assume that an expression can only contain at most one pointer to + // a null-terminated array. + + if (!E) + return nullptr; + + if (auto *CE = dyn_cast(E)) + if (CE->getCastKind() == CastKind::CK_LValueToRValue) + E = CE->getSubExpr(); + + E = IgnoreCasts(E); + + // Get the pointer to a null-terminated array in an expression like *e. + if (const auto *UO = dyn_cast(E)) { + return GetNullTermPtrInExpr(UO->getSubExpr()); + + // Get the pointer to a null-terminated array in an expression like e1 + e2. + } else if (const auto *BO = dyn_cast(E)) { + const VarDecl *V = GetNullTermPtrInExpr(BO->getLHS()); + if (V) + return V; + return GetNullTermPtrInExpr(BO->getRHS()); + } + + // If the variable is a pointer to a null-terminated array return it. + if (const auto *D = dyn_cast(E)) { + if (const auto *V = dyn_cast(D->getDecl())) + if (!V->isInvalidDecl() && IsNtArrayType(V)) + return V; + } + return nullptr; +} + +Expr *BoundsWideningUtil::IgnoreCasts(const Expr *E) const { + return Lex.IgnoreValuePreservingOperations(Ctx, const_cast(E)); +} + +bool BoundsWideningUtil::SkipBlock(const CFGBlock *B) const { + return !B || B == &Cfg->getExit(); +} + +bool BoundsWideningUtil::IsNtArrayType(const VarDecl *V) const { + return V && (V->getType()->isCheckedPointerNtArrayType() || + V->getType()->isNtCheckedArrayType()); +} + +// Common templated set operation functions. +template +T BoundsWideningUtil::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 BoundsWideningUtil::Union(T &A, T &B) const { + auto CopyA = A; + for (auto Item : B) + CopyA.insert(Item); + + return CopyA; +} + +template +T BoundsWideningUtil::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 BoundsWideningUtil::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<> +BoundsMapTy BoundsWideningUtil::Difference( + BoundsMapTy &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<> +BoundsMapTy BoundsWideningUtil::Intersect( + BoundsMapTy &A, BoundsMapTy &B) const { + + if (!A.size() || !B.size()) + return BoundsMapTy(); + + 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; + } + + RangeBoundsExpr *BoundsA = VarBoundsIt->second; + RangeBoundsExpr *BoundsB = VarBoundsPair.second; + + // Currently, CopyA[V] is BoundsA. Set CopyA[V] to BoundsB if: + // 1. BoundsA is Top, or + // 2. BoundsB is not Top and BoundsB is a subrange of BoundsA. + if (BoundsA == BoundsWideningAnalysis::Top) + CopyA[V] = BoundsB; + else if (BoundsB != BoundsWideningAnalysis::Top && + IsSubRange(BoundsA, BoundsB)) + CopyA[V] = BoundsB; + } + return CopyA; +} + +template<> +BoundsMapTy BoundsWideningUtil::Union( + BoundsMapTy &A, BoundsMapTy &B) const { + + auto CopyA = A; + for (auto VarBoundsPair : B) + CopyA[VarBoundsPair.first] = VarBoundsPair.second; + + return CopyA; +} + +template<> +bool BoundsWideningUtil::IsEqual( + BoundsMapTy &A, BoundsMapTy &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; + + RangeBoundsExpr *BoundsA = VarBoundsIt->second; + RangeBoundsExpr *BoundsB = VarBoundsPair.second; + + if (BoundsA == BoundsWideningAnalysis::Top || + BoundsB == BoundsWideningAnalysis::Top) + return BoundsA == BoundsB; + + if (!Lex.CompareExprSemantically(BoundsA->getUpperExpr(), + BoundsB->getUpperExpr())) + return false; + } + return true; +} +// end of methods for the BoundsWideningUtil class. + } // end namespace clang diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index e8067ad55fe4..9970e079e5bd 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -23,7 +23,6 @@ clang_tablegen(OpenCLBuiltins.inc -gen-clang-opencl-builtins add_clang_library(clangSema AnalysisBasedWarnings.cpp AvailableFactsAnalysis.cpp - BoundsAnalysis.cpp BoundsUtils.cpp BoundsWideningAnalysis.cpp CheckedCAlias.cpp diff --git a/clang/lib/Sema/CheckedCAnalysesPrepass.cpp b/clang/lib/Sema/CheckedCAnalysesPrepass.cpp index cd0b59327a13..31478a2fdda6 100644 --- a/clang/lib/Sema/CheckedCAnalysesPrepass.cpp +++ b/clang/lib/Sema/CheckedCAnalysesPrepass.cpp @@ -50,6 +50,18 @@ class PrepassHelper : public RecursiveASTVisitor { // }; FieldDecl *FieldWithBounds = nullptr; + // InBoundsExprLower indicates that we are currently processing the lower + // bounds expression of a BoundsExpr that has been expanded to a + // RangeBoundsExpr. This flag is used to determine whether a variable + // occurs in the lower bounds expression of a VarWithBounds. + bool InBoundsExprLower = false; + + // InBoundsExprUpper indicates that we are currently processing the upper + // bounds expression of a BoundsExpr that has been expanded to a + // RangeBoundsExpr. This flag is used to determine whether a variable + // occurs in the upper bounds expression of a VarWithBounds. + bool InBoundsExprUpper = false; + // ProcessedRecords keeps tracks of the record declarations whose fields // have been traversed by the FillBoundsSiblingFields method. This avoids // unnecessary duplicate traversals of record fields. @@ -70,16 +82,17 @@ class PrepassHelper : public RecursiveASTVisitor { return T->getAsRecordDecl(); } - // AddBoundsSiblingField adds FieldWithBounds to the set of fields in - // whose declared bounds F occurs. - void AddBoundsSiblingField(const FieldDecl *F) { - auto It = Info.BoundsSiblingFields.find(F); - if (It != Info.BoundsSiblingFields.end()) - It->second.insert(FieldWithBounds); + // AddPtrWithBounds adds PtrWithBounds to the set of variables or fields in + // whose bounds VarOrField occurs. + template + void AddPtrWithBounds(T &Map, const U *PtrWithBounds, const U *VarOrField) { + auto It = Map.find(VarOrField); + if (It != Map.end()) + It->second.insert(PtrWithBounds); else { - FieldSetTy Fields; - Fields.insert(FieldWithBounds); - Info.BoundsSiblingFields[F] = Fields; + typename T::mapped_type Ptrs; + Ptrs.insert(PtrWithBounds); + Map[VarOrField] = Ptrs; } } @@ -115,7 +128,7 @@ class PrepassHelper : public RecursiveASTVisitor { // bounds. For example, if a field p has declared bounds count(1), // then p occurs in the declared bounds of p. if (isa(FieldBounds)) - AddBoundsSiblingField(Field); + AddPtrWithBounds(Info.BoundsSiblingFields, FieldWithBounds, Field); // Traverse the declared bounds to visit the DeclRefExprs that // explicitly occur in the declared bounds. These DeclRefExprs @@ -148,6 +161,17 @@ class PrepassHelper : public RecursiveASTVisitor { TraverseStmt(B); VarWithBounds = nullptr; } + + // Else if V does not have a bounds expression but V is a checked array + // or a checked null-terminated array then V occurs in its upper and + // lower bounds. So we add V to the set of variables that occur in its + // lower and upper bounds. For example: + // void f(_Nt_array_ptr p); + // char p _Checked[] = "ab"; + } else if (V->getType()->isCheckedPointerArrayType() || + V->getType()->isCheckedArrayType()) { + AddPtrWithBounds(Info.BoundsVarsLower, V, V); + AddPtrWithBounds(Info.BoundsVarsUpper, V, V); } // Process any where clause attached to this VarDecl. @@ -165,7 +189,7 @@ class PrepassHelper : public RecursiveASTVisitor { if (FieldWithBounds) { const FieldDecl *F = dyn_cast_or_null(E->getDecl()); if (F && !F->isInvalidDecl()) - AddBoundsSiblingField(F); + AddPtrWithBounds(Info.BoundsSiblingFields, FieldWithBounds, F); return true; } @@ -180,17 +204,13 @@ class PrepassHelper : public RecursiveASTVisitor { Info.VarUses[V] = E; } - // We add VarWithBounds to the set of all variables in whose bounds - // expressions V occurs. if (VarWithBounds) { - auto It = Info.BoundsVars.find(V); - if (It != Info.BoundsVars.end()) - It->second.insert(VarWithBounds); - else { - VarSetTy Vars; - Vars.insert(VarWithBounds); - Info.BoundsVars[V] = Vars; - } + // We add VarWithBounds to the set of all variables in whose lower and + // upper bounds expressions V occurs. + if (InBoundsExprLower) + AddPtrWithBounds(Info.BoundsVarsLower, VarWithBounds, V); + else if (InBoundsExprUpper) + AddPtrWithBounds(Info.BoundsVarsUpper, VarWithBounds, V); } return true; @@ -210,19 +230,35 @@ class PrepassHelper : public RecursiveASTVisitor { return true; } + bool VisitBoundsExpr(BoundsExpr *B) { + if (!VarWithBounds) + return true; + + if (RangeBoundsExpr *R = dyn_cast_or_null(B)) { + InBoundsExprLower = true; + TraverseStmt(R->getLowerExpr()); + InBoundsExprLower = false; + + InBoundsExprUpper = true; + TraverseStmt(R->getUpperExpr()); + InBoundsExprUpper = false; + } + + return true; + } + bool ProcessWhereClause(WhereClause *WC) { if (!WC) return true; for (WhereClauseFact *Fact : WC->getFacts()) { - if (BoundsDeclFact *BDF = dyn_cast(Fact)) { - VarDecl *V = BDF->getVarDecl(); - BoundsExpr *B = BDF->getBoundsExpr(); - - VarDecl *OrigVarWithBounds = VarWithBounds; - VarWithBounds = V; - TraverseStmt(B); - VarWithBounds = OrigVarWithBounds; + if (BoundsDeclFact *F = dyn_cast(Fact)) { + if (BoundsExpr *NormalizedBounds = SemaRef.NormalizeBounds(F)) { + VarDecl *OrigVarWithBounds = VarWithBounds; + VarWithBounds = F->getVarDecl(); + TraverseStmt(NormalizedBounds); + VarWithBounds = OrigVarWithBounds; + } } } @@ -242,7 +278,10 @@ class PrepassHelper : public RecursiveASTVisitor { } void DumpBoundsVars(FunctionDecl *FD) { - PrintDeclMap(FD, "BoundsVars", Info.BoundsVars); + PrintDeclMap(FD, "BoundsVars Lower", + Info.BoundsVarsLower); + PrintDeclMap(FD, "BoundsVars Upper", + Info.BoundsVarsUpper); } void DumpBoundsSiblingFields(FunctionDecl *FD) { @@ -252,8 +291,8 @@ class PrepassHelper : public RecursiveASTVisitor { // Print a map from a key of type T to a set of elements of type T, // where T should inherit from NamedDecl. - // This method can be used to print the BoundsVars and - // BoundsSiblingFields maps. + // This method can be used to print the BoundsVarsLower, BoundsVarsUpper + // and BoundsSiblingFields maps. template void PrintDeclMap(FunctionDecl *FD, const char *Message, llvm::DenseMap> Map) { diff --git a/clang/lib/Sema/SemaBounds.cpp b/clang/lib/Sema/SemaBounds.cpp index 30c9d99ab932..16ce62eb1ed6 100644 --- a/clang/lib/Sema/SemaBounds.cpp +++ b/clang/lib/Sema/SemaBounds.cpp @@ -45,8 +45,8 @@ #include "clang/AST/ExprUtils.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Sema/AvailableFactsAnalysis.h" -#include "clang/Sema/BoundsAnalysis.h" #include "clang/Sema/BoundsUtils.h" +#include "clang/Sema/BoundsWideningAnalysis.h" #include "clang/Sema/CheckedCAnalysesPrepass.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallPtrSet.h" @@ -426,6 +426,7 @@ namespace { namespace { using EqualExprTy = SmallVector; + using DeclSetTy = llvm::DenseSet; // EqualExprsContainsExpr returns true if the set Exprs contains an // expression that is equivalent to E. @@ -655,10 +656,10 @@ namespace { ASTContext &Context; std::pair &Facts; - // Having a BoundsAnalysis object here allows us to easily invoke methods - // for bounds-widening and get back the bounds-widening info needed for - // bounds inference/checking. - BoundsAnalysis BoundsAnalyzer; + // 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. + BoundsWideningAnalysis BoundsWideningAnalyzer; // Having an AbstractSetManager object here allows us to create // AbstractSets for lvalue expressions while checking statements. @@ -2631,7 +2632,9 @@ namespace { ReturnBounds(ReturnBounds), Context(SemaRef.Context), Facts(Facts), - BoundsAnalyzer(BoundsAnalysis(SemaRef, Cfg)), + BoundsWideningAnalyzer(BoundsWideningAnalysis(SemaRef, Cfg, + Info.BoundsVarsLower, + Info.BoundsVarsUpper)), AbstractSetMgr(AbstractSetManager(SemaRef, Info.VarUses)), BoundsSiblingFields(Info.BoundsSiblingFields), IncludeNullTerminator(false) {} @@ -2646,12 +2649,14 @@ namespace { ReturnBounds(nullptr), Context(SemaRef.Context), Facts(Facts), - BoundsAnalyzer(BoundsAnalysis(SemaRef, nullptr)), + BoundsWideningAnalyzer(BoundsWideningAnalysis(SemaRef, nullptr, + Info.BoundsVarsLower, + Info.BoundsVarsUpper)), AbstractSetMgr(AbstractSetManager(SemaRef, Info.VarUses)), BoundsSiblingFields(Info.BoundsSiblingFields), IncludeNullTerminator(false) {} - void IdentifyChecked(Stmt *S, StmtSet &MemoryCheckedStmts, StmtSet &BoundsCheckedStmts, CheckedScopeSpecifier CSS) { + void IdentifyChecked(Stmt *S, StmtSetTy &MemoryCheckedStmts, StmtSetTy &BoundsCheckedStmts, CheckedScopeSpecifier CSS) { if (!S) return; @@ -2672,7 +2677,7 @@ namespace { } // Add any subexpressions of S that occur in TopLevelElems to NestedExprs. - void MarkNested(const Stmt *S, StmtSet &NestedExprs, StmtSet &TopLevelElems) { + void MarkNested(const Stmt *S, StmtSetTy &NestedExprs, StmtSetTy &TopLevelElems) { auto Begin = S->child_begin(), End = S->child_end(); for (auto I = Begin; I != End; ++I) { const Stmt *Child = *I; @@ -2710,9 +2715,9 @@ namespace { // // For now, we want to skip B1.1, B2.1, and B3.1 because they will be processed // as part of B4.1. - void FindNestedElements(StmtSet &NestedStmts) { + void FindNestedElements(StmtSetTy &NestedStmts) { // Create the set of top-level CFG elements. - StmtSet TopLevelElems; + StmtSetTy TopLevelElems; for (const CFGBlock *Block : *Cfg) { for (CFGElement Elem : *Block) { if (Elem.getKind() == CFGElement::Statement) { @@ -2736,34 +2741,33 @@ namespace { } } - void UpdateWidenedBounds(BoundsAnalysis &BA, const CFGBlock *Block, - CheckingState &State) { - for (const auto item : BA.GetWidenedBounds(Block)) { - const VarDecl *V = item.first; - BoundsExpr *Bounds = item.second; - - // BoundsAnalysis currently uses VarDecls as keys in the widened - // bounds data structure, so we create an AbstractSet for each - // VarDecl in the widened bounds. TODO: use AbstractSets as keys - // in BoundsAnalysis (checkedc-clang issue #1015). - const AbstractSet *A = AbstractSetMgr.GetOrCreateAbstractSet(V); - auto I = State.ObservedBounds.find(A); - if (I != State.ObservedBounds.end()) - I->second = Bounds; - } - } + void UpdateWidenedBounds(BoundsWideningAnalysis &BA, const CFGBlock *Block, + Stmt *CurrStmt, CheckingState &State) { + // Get the bounds widened before the current statement. + BoundsMapTy WidenedBounds = BA.GetStmtIn(Block, CurrStmt); + + // BoundsWideningAnalysis currently uses VarDecls as keys in the widened + // bounds data structure, so we get the AbstractSet for each VarDecl in + // the widened bounds. + // TODO: use AbstractSets as keys in BoundsWideningAnalysis + // (checkedc-clang issue #1015). + + // Update the bounds of each variable in ObservedBounds to the bounds + // computed by the bounds widening analysis. + // Note: Bounds widening analysis resets killed bounds of a variable to + // its declared bounds. So we do not need to explicitly reset killed + // bounds here. + for (const auto VarBoundsPair : WidenedBounds) { + const VarDecl *V = VarBoundsPair.first; + BoundsExpr *Bounds = VarBoundsPair.second; + if (!Bounds) + continue; - void ResetKilledBounds(BoundsAnalysis &BA, const CFGBlock *Block, - const Stmt *St, CheckingState &State) { - for (const VarDecl *V : BA.GetKilledBounds(Block, St)) { - // BoundsAnalysis currently uses VarDecls as keys in the killed - // bounds data structure, so we create an AbstractSet for each - // VarDecl in the killed bounds. TODO: use AbstractSets as keys - // in BoundsAnalysis (checkedc-clang issue #1015). const AbstractSet *A = AbstractSetMgr.GetOrCreateAbstractSet(V); + auto I = State.ObservedBounds.find(A); if (I != State.ObservedBounds.end()) - I->second = S.NormalizeBounds(V); + I->second = Bounds; } } @@ -2834,17 +2838,16 @@ namespace { llvm::DenseMap BlockStates; BlockStates[Cfg->getEntry().getBlockID()] = ParamsState; - StmtSet NestedElements; + StmtSetTy NestedElements; FindNestedElements(NestedElements); - StmtSet MemoryCheckedStmts; - StmtSet BoundsCheckedStmts; + StmtSetTy MemoryCheckedStmts; + StmtSetTy BoundsCheckedStmts; IdentifyChecked(Body, MemoryCheckedStmts, BoundsCheckedStmts, CheckedScopeSpecifier::CSS_Unchecked); // Run the bounds widening analysis on this function. - BoundsAnalysis &BA = getBoundsAnalyzer(); - BA.WidenBounds(FD, NestedElements); + BoundsWideningAnalyzer.WidenBounds(FD, NestedElements); if (S.getLangOpts().DumpWidenedBounds) - BA.DumpWidenedBounds(FD); + BoundsWideningAnalyzer.DumpWidenedBounds(FD); PostOrderCFGView POView = PostOrderCFGView(Cfg); ResetFacts(); @@ -2852,9 +2855,6 @@ namespace { AFA.GetFacts(Facts); CheckingState BlockState = GetIncomingBlockState(Block, BlockStates); - // Update the observed bounds with the widened bounds calculated above. - UpdateWidenedBounds(BA, Block, BlockState); - for (CFGElement Elem : *Block) { if (Elem.getKind() == CFGElement::Statement) { CFGStmt CS = Elem.castAs(); @@ -2867,6 +2867,10 @@ namespace { if (NestedElements.find(S) != NestedElements.end()) continue; + // Update the observed bounds with the widened bounds computed + // above. + UpdateWidenedBounds(BoundsWideningAnalyzer, Block, S, BlockState); + CheckedScopeSpecifier CSS = CheckedScopeSpecifier::CSS_Unchecked; const Stmt *Statement = S; if (DeclStmt *DS = dyn_cast(S)) @@ -2908,13 +2912,6 @@ namespace { // declared bounds, the observed bounds for each AbstractSet should // be reset to their observed bounds from before checking S. BlockState.ObservedBounds = InitialObservedBounds; - - // If the widened bounds of any variables are killed by statement - // S, reset their observed bounds to their declared bounds. - // Resetting the widened bounds killed by S should be the last - // thing done as part of traversing S. The widened bounds of each - // variable should be in effect until the very end of traversing S. - ResetKilledBounds(BA, Block, S, BlockState); } else if (Elem.getKind() == CFGElement::LifetimeEnds) { // Every variable going out of scope is indicated by a LifetimeEnds @@ -4306,10 +4303,6 @@ namespace { return BoundsUtil::CreateBoundsAlwaysUnknown(S); } - public: - - BoundsAnalysis &getBoundsAnalyzer() { return BoundsAnalyzer; } - private: // Sets the bounds expressions based on whether e is an lvalue or an // rvalue expression. @@ -4356,9 +4349,8 @@ namespace { EquivExprs.push_back({Target, Src}); } - BoundsAnalysis &BA = getBoundsAnalyzer(); - DeclSetTy BoundsWidenedAndNotKilled = - BA.GetBoundsWidenedAndNotKilled(Block, S); + BoundsMapTy BoundsWidenedAndNotKilled = + BoundsWideningAnalyzer.GetBoundsWidenedAndNotKilled(Block, S); for (auto const &Pair : State.ObservedBounds) { const AbstractSet *A = Pair.first; @@ -4376,8 +4368,8 @@ namespace { // 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 - // variables whose bounds are widened in this block and not killed - // by statement S. + // variables whose bounds are widened in this block before statement + // S and not killed by statement S. bool DiagnoseObservedBounds = true; if (const VarDecl *Var = dyn_cast(V)) DiagnoseObservedBounds = BoundsWidenedAndNotKilled.find(Var) == diff --git a/clang/test/CheckedC/inferred-bounds/bounds-context.c b/clang/test/CheckedC/inferred-bounds/bounds-context.c index 63d227bab412..5e57d5243aa8 100644 --- a/clang/test/CheckedC/inferred-bounds/bounds-context.c +++ b/clang/test/CheckedC/inferred-bounds/bounds-context.c @@ -2077,7 +2077,7 @@ void killed_widened_bounds2(nt_array_ptr p : count(0), int other) { // CHECK-NEXT: } // Bounds of p are currently widened by 1 - // Observed bounds context: { p => bounds(p, (p + 0) + 1) } + // Observed bounds context: { p => bounds(p, p + 1) } p[1]; // CHECK: Statement S: // CHECK-NEXT: ImplicitCastExpr {{.*}} @@ -2087,10 +2087,8 @@ void killed_widened_bounds2(nt_array_ptr p : count(0), int other) { // CHECK-NEXT: ImplicitCastExpr {{.*}} // CHECK-NEXT: DeclRefExpr {{.*}} 'p' // CHECK-NEXT: BinaryOperator {{.*}} '+' - // CHECK-NEXT: BinaryOperator {{.*}} '+' - // CHECK-NEXT: ImplicitCastExpr {{.*}} - // CHECK-NEXT: DeclRefExpr {{.*}} 'p' - // CHECK-NEXT: IntegerLiteral {{.*}} 0 + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'p' // CHECK-NEXT: IntegerLiteral {{.*}} 1 // CHECK-NEXT: ImplicitCastExpr {{.*}} // CHECK-NEXT: DeclRefExpr {{.*}} 'p' @@ -2104,10 +2102,8 @@ void killed_widened_bounds2(nt_array_ptr p : count(0), int other) { // CHECK-NEXT: ImplicitCastExpr {{.*}} // CHECK-NEXT: DeclRefExpr {{.*}} 'p' // CHECK-NEXT: BinaryOperator {{.*}} '+' - // CHECK-NEXT: BinaryOperator {{.*}} '+' - // CHECK-NEXT: ImplicitCastExpr {{.*}} - // CHECK-NEXT: DeclRefExpr {{.*}} 'p' - // CHECK-NEXT: IntegerLiteral {{.*}} 0 + // CHECK-NEXT: ImplicitCastExpr {{.*}} + // CHECK-NEXT: DeclRefExpr {{.*}} 'p' // CHECK-NEXT: IntegerLiteral {{.*}} 1 // CHECK-NEXT: } diff --git a/clang/test/CheckedC/inferred-bounds/bounds-vars.c b/clang/test/CheckedC/inferred-bounds/bounds-vars.c index bf078fa6c675..f816cf59733d 100644 --- a/clang/test/CheckedC/inferred-bounds/bounds-vars.c +++ b/clang/test/CheckedC/inferred-bounds/bounds-vars.c @@ -3,8 +3,6 @@ // // RUN: %clang_cc1 -fdump-boundsvars -verify -verify-ignore-unexpected=note -verify-ignore-unexpected=warning %s | FileCheck %s -// expected-no-diagnostics - void f1(_Array_ptr param1 : bounds(param1, param1 + 1), _Nt_array_ptr param2 : bounds(param2, param2 + 1), int x, int y) { @@ -30,13 +28,25 @@ void f1(_Array_ptr param1 : bounds(param1, param1 + 1), int w; _Nt_array_ptr a : bounds(a, a + 1) = "a"; - w = 1 _Where a : bounds(a, a + 1); - x = 2 _Where a : count(x + y + w); + w = 1 _Where a : bounds(a, a + 1); // expected-error {{it is not possible to prove that the inferred bounds of 'param2' imply the declared bounds of 'param2' after statement}} + x = 2 _Where a : count(x + y + w); // expected-error {{inferred bounds for 'q' are unknown after assignment}} y = 3 _Where param2 : bounds(param1, param1 + w); } // CHECK-LABEL: In function: f1 -// CHECK: BoundsVars: +// CHECK: BoundsVars Lower: +// CHECK: a: { a } +// CHECK: m: { m } +// CHECK: p: { p } +// CHECK: p: { p q } +// CHECK: param1: { param1 param2 } +// CHECK: param2: { param2 } +// CHECK: q: { q } +// CHECK: q: { m q } +// CHECK: x: { p } + +// CHECK-LABEL: In function: f1 +// CHECK: BoundsVars Upper: // CHECK: a: { a } // CHECK: m: { m } // CHECK: p: { p } @@ -46,7 +56,7 @@ void f1(_Array_ptr param1 : bounds(param1, param1 + 1), // CHECK: q: { q } // CHECK: q: { m q } // CHECK: w: { a param2 } -// CHECK: x: { a p param1 q } +// CHECK: x: { a param1 q } // CHECK: y: { a p param1 param2 } // CHECK: z: { m p q } } diff --git a/clang/test/CheckedC/inferred-bounds/widened-bounds-check.c b/clang/test/CheckedC/inferred-bounds/widened-bounds-check.c deleted file mode 100644 index 2a3d1b6e3841..000000000000 --- a/clang/test/CheckedC/inferred-bounds/widened-bounds-check.c +++ /dev/null @@ -1,20 +0,0 @@ -// Tests for using bounds widening to control "out-of-bounds access" errors. -// -// RUN: %clang_cc1 -verify -verify-ignore-unexpected=note -fcheckedc-extension %s - -#include - -void f1() { - _Nt_array_ptr p : bounds(p, p) = ""; - - if (*p) - if (*(p + 1)) - if (*(p + 3)) // expected-error {{out-of-bounds memory access}} - {} - - if (*p) { - p++; // expected-warning {{cannot prove declared bounds for 'p' are valid after increment}} - if (*(p + 1)) // expected-error {{out-of-bounds memory access}} - {} - } -} diff --git a/clang/test/CheckedC/inferred-bounds/widened-bounds-multiple-derefs.c b/clang/test/CheckedC/inferred-bounds/widened-bounds-multiple-derefs.c index ae82f6bc20c5..60809a04c78f 100644 --- a/clang/test/CheckedC/inferred-bounds/widened-bounds-multiple-derefs.c +++ b/clang/test/CheckedC/inferred-bounds/widened-bounds-multiple-derefs.c @@ -1,412 +1,751 @@ -// Tests for datafow analysis for bounds widening in case of multiple dereferences of _Nt_array_ptr's. +// Tests for datafow analysis for bounds widening in case of conditionals with +// multiple dereferences of null-terminated arrays. // -// RUN: %clang_cc1 -fdump-widened-bounds -verify -verify-ignore-unexpected=note -verify-ignore-unexpected=warning %s | FileCheck %s +// RUN: %clang_cc1 -fdump-widened-bounds -verify \ +// RUN: -verify-ignore-unexpected=note -verify-ignore-unexpected=warning %s \ +// RUN: | FileCheck %s + +int a; void f1() { _Nt_array_ptr p : count(0) = "a"; - if (*p && *(p + 1)) {} + if (*p && *(p + 1) && *(p + 2)) { + a = 1; + } -// CHECK: In function: f1 -// CHECK: [B4] -// CHECK: 2: *p -// CHECK: [B3] -// CHECK: 1: *(p + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 -} +// CHECK: Function: f1 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 -void f2() { - _Nt_array_ptr p : count(0) = "ab"; - - if (*p && *(p + 1) && *(p + 2)) - {} - -// CHECK: In function: f2 -// CHECK: [B5] -// CHECK: 2: *p -// CHECK: [B4] -// CHECK: 1: *(p + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: *(p + 2) -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 3 +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, Succ: B0 } -void f3() { +void f2() { _Nt_array_ptr p : count(0) = "a"; int a; if (*p && *(p + 1)) { - p = "a"; - if (a) {} - } - -// CHECK: In function: f3 -// CHECK: [B5] -// CHECK: 3: *p -// CHECK: [B4] -// CHECK: 1: *(p + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: p = "a" -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK-NOT: upper_bound(p) + p = "b"; + a = 1; + } + +// CHECK: Function: f2 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: int a; +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: p = "b" +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 } -void f4(_Nt_array_ptr p : count(0)) { - if (p[0] && p[1]) {} +void f3(_Nt_array_ptr p : count(0)) { + if ((((0[p]))) && (((p[1]))) && ((p[2]))) { + a = 1; + } -// CHECK: In function: f4 -// CHECK: [B10] -// CHECK: 1: p[0] -// CHECK: [B9] -// CHECK: 1: p[1] -// CHECK: upper_bound(p) = 1 -// CHECK: [B8] -// CHECK: upper_bound(p) = 2 +// CHECK: Function: f3 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 - _Nt_array_ptr q : count(0) = "a"; - if (0[q] && 1[q]) {} -// CHECK: [B7] -// CHECK: 2: 0[q] -// CHECK: [B6] -// CHECK: 1: 1[q] -// CHECK: upper_bound(q) = 1 -// CHECK: [B5] -// CHECK: upper_bound(q) = 2 - - if ((((q[0]))) && (((q[1])))) {} -// CHECK: [B4] -// CHECK: 1: (((q[0]))) -// CHECK: [B3] -// CHECK: 1: (((q[1]))) -// CHECK: upper_bound(q) = 1 -// CHECk: [B1] -// CHECK: upper_bound(q) = 2 +// CHECK: Block: B4, Pred: B5, Succ: B3, B0 +// CHECK: Widened bounds before stmt: (((0[p]))) +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B0 +// CHECK: Widened bounds before stmt: (((p[1]))) +// CHECK: p: bounds(p, p + 0 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1, B0 +// CHECK: Widened bounds before stmt: ((p[2])) +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B1, Pred: B2, Succ: B0 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 2 + 1) } -void f5() { +void f4() { char p _Nt_checked[] : count(0) = "abc"; - if (p[0] && p[1] && p[2]) - {} - -// CHECK: In function: f5 -// CHECK: [B5] -// CHECK: 2: p[0] -// CHECK: [B4] -// CHECK: 1: p[1] -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: p[2] -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 3 + if (p[0] && p[1] && p[2]) { + a = 1; + } + +// CHECK: Function: f4 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: char p_Nt_checked[] : count(0) = "abc"; +// CHECK: + +// CHECK: Widened bounds before stmt: p[0] +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: p[1] +// CHECK: p: bounds(p, p + 0 + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: p[2] +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, Succ: B0 } -// It seems it is not correct! -void f6(int i) { + +void f5(int i) { char p _Nt_checked[] : bounds(p + i, p) = "abc"; if (p[0] && p[1]) { i = 0; // expected-error {{inferred bounds for 'p' are unknown after assignment}} - if (p[2]) {} - } - -// CHECK: In function: f6 -// CHECK: [B5] -// CHECK: 2: p[0] -// CHECK: [B4] -// CHECK: 1: p[1] -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: i = 0 -// CHECK: 2: p[2] -// CHECK: upper_bound(p) = 2 -// CHECK: [B1] -// CHECK-NOT: upper_bound(p) + if (p[2]) { + a = 1; + } + a = 2; + } + a = 3; + +// CHECK: Function: f5 +// CHECK: Block: B7 (Entry), Pred: Succ: B6 + +// CHECK: Block: B6, Pred: B7, Succ: B5, B1 +// CHECK: Widened bounds before stmt: char p_Nt_checked[] : bounds(p + i, p) = "abc"; +// CHECK: + +// CHECK: Widened bounds before stmt: p[0] +// CHECK: p: bounds(p + i, p) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: p[1] +// CHECK: p: bounds(p + i, p + 0 + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B2 +// CHECK: Widened bounds before stmt: i = 0 +// CHECK: p: bounds(p + i, p + 1 + 1) + +// CHECK: Widened bounds before stmt: p[2] +// CHECK: p: bounds(p + i, p) + +// CHECK: Block: B3, Pred: B4, Succ: B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p + i, p) + +// CHECK: Block: B2, Pred: B3, B4, Succ: B1 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p + i, p) + +// CHECK: Block: B1, Pred: B2, B5, B6, Succ: B0 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p + i, p) } -void f7(char p _Nt_checked[] : count(0)) { - if (p[0] && p[1]) {} - -// CHECK: In function: f7 -// CHECK: [B10] -// CHECK: 1: p[0] -// CHECK: [B9] -// CHECK: 1: p[1] -// CHECK: upper_bound(p) = 1 -// CHECK: [B8] -// CHECK: upper_bound(p) = 2 - - char q _Nt_checked[] : count(0) = "a"; - if (0[q] && 1[q]) {} -// CHECK: [B7] -// CHECK: 2: 0[q] -// CHECK: [B6] -// CHECK: 1: 1[q] -// CHECK: upper_bound(q) = 1 -// CHECK: [B5] -// CHECK: upper_bound(q) = 2 - - if ((((q[0]))) && (((q[1])))) {} -// CHECK: [B4] -// CHECK: 1: (((q[0]))) -// CHECK: [B3] -// CHECK: 1: (((q[1]))) -// CHECK: upper_bound(q) = 1 -// CHECK: [B2] -// CHECK: upper_bound(q) = 2 + +void f6() { + _Nt_array_ptr p : count(2) = "abc"; + + if (*p && *(p + 1) && *(p + 2) && *(p + 3)) { + a = 1; + } + +// CHECK: Function: f6 +// CHECK: Block: B7 (Entry), Pred: Succ: B6 + +// CHECK: Block: B6, Pred: B7, Succ: B5, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(2) = "abc"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 2) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 2) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 2) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + 3) +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 3 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, B6, Succ: B0 +} + +void f7() { + _Nt_array_ptr p : count(0) = "abc"; + + if (*p && *(p + 2) && *(p + 3) && *(p + 1)) { // expected-error {{out-of-bounds memory access}} expected-error {{out-of-bounds memory access}} + a = 1; + } + +// CHECK: Function: f7 +// CHECK: Block: B7 (Entry), Pred: Succ: B6 + +// CHECK: Block: B6, Pred: B7, Succ: B5, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "abc"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(p + 3) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, B6, Succ: B0 } void f8() { - _Nt_array_ptr p : count(2) = "abc"; + _Nt_array_ptr p : count(0) = "abc"; + + if (*p && *(p + 2) && *(p + 1) && *(p + 3) && *(p + 2) && *(p + 3)) { // expected-error {{out-of-bounds memory access}} + a = 1; + } + +// CHECK: Function: f8 +// CHECK: Block: B9 (Entry), Pred: Succ: B8 + +// CHECK: Block: B8, Pred: B9, Succ: B7, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "abc"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B7, Pred: B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 1) - if (*p && *(p + 1) && *(p + 2) && *(p + 3)) - {} - -// CHECK: In function: f8 -// CHECK: [B6] -// CHECK: 2: *p -// CHECK: [B5] -// CHECK: 1: *(p + 1) -// CHECK-NOT: upper_bound(p) -// CHECK: [B4] -// CHECK: 1: *(p + 2) -// CHECK-NOT: upper_bound(p) -// CHECK: [B3] -// CHECK: 1: *(p + 3) -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 +// CHECK: Block: B6, Pred: B7, Succ: B5, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: *(p + 3) +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + 3) +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 3 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, B6, B7, B8, Succ: B0 } void f9(int i) { _Nt_array_ptr p : bounds(p, p + i) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after initialization}} - if (*p && *(p + i) && *(p + i + 1)) {} - -// CHECK: In function: f9 -// CHECK: [B5] -// CHECK: 2: *p -// CHECK: [B4] -// CHECK: 1: *(p + i) -// CHECK-NOT: upper_bound(p) -// CHECK: [B3] -// CHECK: 1: *(p + i + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 + if (*p && *(p + i) && *(p + i + 1)) { + a = 1; + } + +// CHECK: Function: f9 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + i) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(p + i) +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + i + 1) +// CHECK: p: bounds(p, p + i + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + i + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, Succ: B0 } void f10(int i) { _Nt_array_ptr p : bounds(p, 1 + p + i + 5) = "a"; - if (*(i + p + 1 + 2 + 3) && *(3 + p + i + 4) && *(p + i + 9)) {} - -// CHECK: In function: f10 -// CHECK: [B5] -// CHECK: 2: *(i + p + 1 + 2 + 3) -// CHECK: [B4] -// CHECK: 1: *(3 + p + i + 4) -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: *(p + i + 9) -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 + if (*(i + p + 1 + 2 + 3) && *(3 + p + i + 4) && *(p + i + 9)) { + a = 1; + } + +// CHECK: Function: f10 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, 1 + p + i + 5) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(i + p + 1 + 2 + 3) +// CHECK: p: bounds(p, 1 + p + i + 5) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(3 + p + i + 4) +// CHECK: p: bounds(p, i + p + 1 + 2 + 3 + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + i + 9) +// CHECK: p: bounds(p, 3 + p + i + 4 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, 3 + p + i + 4 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, Succ: B0 } -void f11(int i, int j) { +void f11_1(int i, int j) { _Nt_array_ptr p : bounds(p + i, p + j) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after initialization}} if (*(p + j) && *(p + j + 1)) { i = 0; // expected-error {{inferred bounds for 'p' are unknown after assignment}} - if (*(p + j + 2)) {} - } - -// CHECK: In function: f11 -// CHECK: [B9] -// CHECK: 2: *(p + j) -// CHECK: [B8] -// CHECK: 1: *(p + j + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B7] -// CHECK: 1: i = 0 -// CHECK: 2: *(p + j + 2) -// CHECK: upper_bound(p) = 2 -// CHECK: [B6] -// CHECK-NOT: upper_bound(p) + if (*(p + j + 2)) { + a = 1; + } + } + +// CHECK: Function: f11_1 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p + i, p + j) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(p + j) +// CHECK: p: bounds(p + i, p + j) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(p + j + 1) +// CHECK: p: bounds(p + i, p + j + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: i = 0 +// CHECK: p: bounds(p + i, p + j + 1 + 1) + +// CHECK: Widened bounds before stmt: *(p + j + 2) +// CHECK: p: bounds(p + i, p + j) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p + i, p + j) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, Succ: B0 +} + +void f11_2(int i, int j) { + _Nt_array_ptr p : bounds(p + i, p + j) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after initialization}} if (*(p + j) && *(p + j + 1)) { j = 0; // expected-error {{inferred bounds for 'p' are unknown after assignment}} - if (*(p + j + 2)) {} - } - -// CHECK: [B5] -// CHECK: 1: *(p + j) -// CHECK: [B4] -// CHECK: 1: *(p + j + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: j = 0 -// CHECK: 2: *(p + j + 2) -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK-NOT: upper_bound(p) + if (*(p + j + 2)) { + a = 1; + } + } + +// CHECK: Function: f11_2 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p + i, p + j) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(p + j) +// CHECK: p: bounds(p + i, p + j) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(p + j + 1) +// CHECK: p: bounds(p + i, p + j + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: j = 0 +// CHECK: p: bounds(p + i, p + j + 1 + 1) + +// CHECK: Widened bounds before stmt: *(p + j + 2) +// CHECK: p: bounds(p + i, p + j) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p + i, p + j) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, Succ: B0 } void f12(int i, int j) { _Nt_array_ptr p : bounds(p, p + i + j) = "a"; - if (*(((p + i + j))) && *((p) + (i) + (j) + (1)) && *((p + i + j) + 2)) {} - -// CHECK: In function: f12 -// CHECK: [B5] -// CHECK: 2: *(((p + i + j))) -// CHECK: [B4] -// CHECK: 1: *((p) + (i) + (j) + (1)) -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: *((p + i + j) + 2) -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 3 + if (*(((p + i + j))) && *((p) + (1) + (i) + (0) + (j)) && *(1 + (p + i + j) + 1)) { + a = 1; + } + +// CHECK: Function: f12 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + i + j) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(((p + i + j))) +// CHECK: p: bounds(p, p + i + j) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *((p) + (1) + (i) + (0) + (j)) +// CHECK: p: bounds(p, p + i + j + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(1 + (p + i + j) + 1) +// CHECK: p: bounds(p, (p) + (1) + (i) + (0) + (j) + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, 1 + (p + i + j) + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, Succ: B0 } void f13() { char p _Nt_checked[] : count(1) = "a"; - if (p[0] && 1[p] && p[2] && 3[p]) - {} - -// CHECK: In function: f13 -// CHECK: [B6] -// CHECK: 2: p[0] -// CHECK: [B5] -// CHECK: 1: 1[p] -// CHECK-NOT: upper_bound(p) -// CHECK: [B4] -// CHECK: 1: p[2] -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: 3[p] -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 3 + if (p[0] && 1[p] && p[2] && 3[p]) { + a = 1; + } + +// CHECK: Function: f13 +// CHECK: Block: B7 (Entry), Pred: Succ: B6 + +// CHECK: Block: B6, Pred: B7, Succ: B5, B1 +// CHECK: Widened bounds before stmt: char p_Nt_checked[] : count(1) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: p[0] +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: 1[p] +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: p[2] +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: 3[p] +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 3 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, B6, Succ: B0 } void f14(int i) { char p _Nt_checked[] : bounds(p, p + i) = "a"; - if ((1 + i)[p] && p[i] && (1 + i)[p]) {} - -// CHECK: In function: f14 -// CHECK: [B5] -// CHECK: 2: (1 + i)[p] -// CHECK: [B4] -// CHECK: 1: p[i] -// CHECK-NOT: upper_bound(p) -// CHECK: [B3] -// CHECK: 1: (1 + i)[p] -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 + if ((1 + i)[p] && p[i] && (2 + i + 1 - 1 + -1)[p]) { + a = 1; + } + +// CHECK: Function: f14 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: char p_Nt_checked[] : bounds(p, p + i) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: (1 + i)[p] +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: p[i] +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: (2 + i + 1 - 1 + -1)[p] +// CHECK: p: bounds(p, p + i + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + (2 + i + 1 - 1 + -1) + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, Succ: B0 } -void f15(int i) { +void f15_1(int i) { _Nt_array_ptr p : bounds(p, p - i) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after initialization}} - if (*(p - i) && *(p - i + 1)) {} + if (*(p - i) && *(p - i + 1)) { + a = 1; + } + +// CHECK: Function: f15_1 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 -// CHECK: In function: f15 -// CHECK: [B14] -// CHECK: 2: *(p - i) -// CHECK: [B13] -// CHECK: 1: *(p - i + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B12] -// CHECK: upper_bound(p) = 2 +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p - i) = "a"; +// CHECK: +// CHECK: Widened bounds before stmt: *(p - i) +// CHECK: p: bounds(p, p - i) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p - i + 1) +// CHECK: p: bounds(p, p - i + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p - i + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 +} + +void f15_2(int i) { _Nt_array_ptr q : count(0) = "a"; - if (*q && *(q - 1)) - {} + if (*q && *(q - 1)) { // expected-error {{out-of-bounds memory access}} + a = 1; + } + +// CHECK: Function: f15_2 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr q : count(0) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *q +// CHECK: q: bounds(q, q + 0) -// CHECK: [B11] -// CHECK: 2: *q -// CHECK: [B10] -// CHECK: 1: *(q - 1) -// CHECK: upper_bound(q) = 1 -// CHECK: [B9] -// CHECK: upper_bound(q) = 1 +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(q - 1) +// CHECK: q: bounds(q, q + 1) +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: q: bounds(q, q + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 +} + +void f15_3() { _Nt_array_ptr r : bounds(r, r + +1) = "a"; - if (*(r + +1) && *(r + +2)) - {} + if (*(r + +1) && *(r + +2)) { + a = 1; + } + +// CHECK: Function: f15_3 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 -// CHECK: [B8] -// CHECK: 2: *(r + +1) -// CHECK: [B7] -// CHECK: 1: *(r + +2) -// CHECK: upper_bound(r) = 1 -// CHECK: [B6] -// CHECK: upper_bound(r) = 2 +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr r : bounds(r, r + +1) = "a"; +// CHECK: +// CHECK: Widened bounds before stmt: *(r + +1) +// CHECK: r: bounds(r, r + +1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(r + +2) +// CHECK: r: bounds(r, r + +1 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: r: bounds(r, r + +2 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 +} + +void f15_4() { _Nt_array_ptr s : bounds(s, s + -1) = "a"; - if (*(s + -1) && *(s) && *(s + +1)) // expected-error {{out-of-bounds memory access}} - {} - -// CHECK: [B5] -// CHECK: 2: *(s + -1) -// CHECK: [B4] -// CHECK: 1: *(s) -// CHECK: upper_bound(s) = 1 -// CHECK: [B3] -// CHECK: 1: *(s + +1) -// CHECK: upper_bound(s) = 2 -// CHECK: [B2] -// CHECK: upper_bound(s) = 3 + if (*(s + -1) && *(s) && *(s + +1)) { // expected-error {{out-of-bounds memory access}} + a = 1; + } + +// CHECK: Function: f15_4 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr s : bounds(s, s + -1) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(s + -1) +// CHECK: s: bounds(s, s + -1) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(s) +// CHECK: s: bounds(s, s + -1 + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(s + +1) +// CHECK: s: bounds(s, (s) + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: s: bounds(s, s + +1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, Succ: B0 } void f16(_Nt_array_ptr p : bounds(p, p)) { _Nt_array_ptr q : bounds(p, p) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'q' imply the declared bounds of 'q' after initialization}} _Nt_array_ptr r : bounds(p, p + 1) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'r' imply the declared bounds of 'r' after initialization}} - if (*(p) && *(p + 1)) - {} - -// CHECK: In function: f16 -// CHECK: [B4] -// CHECK: 3: *(p) -// CHECK: [B3] -// CHECK: 1: *(p + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: upper_bound(q) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 -// CHECK: upper_bound(q) = 2 -// CHECK: upper_bound(r) = 1 + if (*(p) && *(p + 1)) { + a = 1; + } + + p = "b"; // expected-error {{inferred bounds for 'q' are unknown after assignment}} expected-error {{inferred bounds for 'r' are unknown after assignment}} + a = 2; + +// CHECK: Function: f16 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr q : bounds(p, p) = "a"; +// CHECK: p: bounds(p, p) + +// CHECK: Widened bounds before stmt: _Nt_array_ptr r : bounds(p, p + 1) = "a"; +// CHECK: p: bounds(p, p) +// CHECK: q: bounds(p, p) + +// CHECK: Widened bounds before stmt: *(p) +// CHECK: p: bounds(p, p) +// CHECK: q: bounds(p, p) +// CHECK: r: bounds(p, p + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, (p) + 1) +// CHECK: q: bounds(p, (p) + 1) +// CHECK: r: bounds(p, p + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1 + 1) +// CHECK: q: bounds(p, p + 1 + 1) +// CHECK: r: bounds(p, p + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 +// CHECK: Widened bounds before stmt: p = "b" +// CHECK: p: bounds(p, p) +// CHECK: q: bounds(p, p) +// CHECK: r: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p) +// CHECK: q: bounds(p, p) +// CHECK: r: bounds(p, p + 1) } void f17(char p _Nt_checked[] : count(1)) { _Nt_array_ptr q : bounds(p, p + 1) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'q' imply the declared bounds of 'q' after initialization}} _Nt_array_ptr r : bounds(p, p) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'r' imply the declared bounds of 'r' after initialization}} - if (*(p) && *(p + 1)) - {} - -// CHECK: In function: f17 -// CHECK: [B4] -// CHECK: 3: *(p) -// CHECK: [B3] -// CHECK: 1: *(p + 1) -// CHECK: upper_bound(r) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 1 -// CHECK: upper_bound(q) = 1 -// CHECK: upper_bound(r) = 2 + if (*(p) && *(p + 1)) { + a = 1; + } + + p = "b"; // expected-error {{inferred bounds for 'q' are unknown after assignment}} expected-error {{inferred bounds for 'r' are unknown after assignment}} + a = 2; + +// CHECK: Function: f17 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr q : bounds(p, p + 1) = "a"; +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: _Nt_array_ptr r : bounds(p, p) = "a"; +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p) +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(p, p + 1) +// CHECK: r: bounds(p, p) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(p, p + 1) +// CHECK: r: bounds(p, (p) + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1 + 1) +// CHECK: q: bounds(p, p + 1 + 1) +// CHECK: r: bounds(p, p + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 +// CHECK: Widened bounds before stmt: p = "b" +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(p, p + 1) +// CHECK: r: bounds(p, p) + +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(p, p + 1) +// CHECK: r: bounds(p, p) } void f18() { @@ -415,192 +754,528 @@ void f18() { char r _Nt_checked[] : count(0) = "ab"; char s _Nt_checked[] : count(1) = "ab"; - if (p[0] && p[1]) - {} - -// CHECK: In function: f18 -// CHECK: [B14] -// CHECK: 5: p[0] -// CHECK: [B13] -// CHECK: 1: p[1] -// CHECK: [B12] -// CHECK: upper_bound(p) = 1 - - if (q[0] && q[1] && q[2]) - {} - -// CHECK: [B11] -// CHECK: 1: q[0] -// CHECK: [B10] -// CHECK: 1: q[1] -// CHECK: [B9] -// CHECK: 1: q[2] -// CHECK: [B8] -// CHECK: upper_bound(q) = 1 - - if (r[0] && r[1]) - {} - -// CHECK: [B7] -// CHECK: 1: r[0] -// CHECK: [B6] -// CHECK: 1: r[1] -// CHECK: upper_bound(r) = 1 -// CHECK: [B5] -// CHECK: upper_bound(r) = 2 - - if (s[0] && s[1]) - {} - -// CHECK: [B4] -// CHECK: 1: s[0] -// CHECK: [B3] -// CHECK: 1: s[1] -// CHECK: [B2] -// CHECK: upper_bound(s) = 1 -} +// CHECK: Function: f18 +// CHECK: Block: B15 (Entry), Pred: Succ: B14 -void f19() { - _Nt_array_ptr p : count(0) = "a"; +// CHECK: Block: B14, Pred: B15, Succ: B13, B11 +// CHECK: Widened bounds before stmt: char p_Nt_checked[] = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: char q_Nt_checked[] = "ab"; +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: char r_Nt_checked[] : count(0) = "ab"; +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) + +// CHECK: Widened bounds before stmt: char s_Nt_checked[] : count(1) = "ab"; +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) - if (*p && *(p + 1) && *(p + 3) && *(p + 2)) - {} - -// CHECK: In function: f19 -// CHECK: [B6] -// CHECK: 2: *p -// CHECK: [B5] -// CHECK: 1: *(p + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B4] -// CHECK: 1: *(p + 3) -// CHECK: upper_bound(p) = 2 -// CHECK: [B3] -// CHECK: 1: *(p + 2) -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 3 + if (p[0] && p[1]) { + a = 1; + } + +// CHECK: Widened bounds before stmt: p[0] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B13, Pred: B14, Succ: B12, B11 +// CHECK: Widened bounds before stmt: p[1] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B12, Pred: B13, Succ: B11 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1 + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + + if (q[0] && q[1] && q[2]) { + a = 2; + } + +// CHECK: Block: B11, Pred: B12, B13, B14, Succ: B10, B7 +// CHECK: Widened bounds before stmt: q[0] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B10, Pred: B11, Succ: B9, B7 +// CHECK: Widened bounds before stmt: q[1] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B9, Pred: B10, Succ: B8, B7 +// CHECK: Widened bounds before stmt: q[2] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B8, Pred: B9, Succ: B7 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2 + 1) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + + if (r[0] && r[1]) { + a = 3; + } + +// CHECK: Block: B7, Pred: B8, B9, B10, B11, Succ: B6, B4 +// CHECK: Widened bounds before stmt: r[0] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B6, Pred: B7, Succ: B5, B4 +// CHECK: Widened bounds before stmt: r[1] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0 + 1) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B4 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 1 + 1) +// CHECK: s: bounds(s, s + 1) + + if (s[0] && s[1]) { + a = 4; + } + +// CHECK: Block: B4, Pred: B5, B6, B7, Succ: B3, B1 +// CHECK: Widened bounds before stmt: s[0] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: s[1] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 } -void f20() { +void f19() { _Nt_array_ptr p : bounds(p, ((((((p + 1))))))) = "a"; - if (*(((p + 1))) && *(((p + 2)))) {} + if (*(((p + 1))) && *(((p + 2)))) { + a = 1; + } + +// CHECK: Function: f19 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, ((((((p + 1))))))) = "a"; +// CHECK: -// CHECK: In function: f20 -// CHECK: [B4] -// CHECK: 2: *(((p + 1))) -// CHECK: [B3] -// CHECK: 1: *(((p + 2))) -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 +// CHECK: Widened bounds before stmt: *(((p + 1))) +// CHECK: p: bounds(p, ((((((p + 1))))))) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(((p + 2))) +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 } -void f21(_Nt_array_ptr p : count(i), int i, int flag) { +void f20(_Nt_array_ptr p : count(i), int i) { if (*(p + i) && *(p + i + 1)) { - flag ? i++ : i; // expected-error {{inferred bounds for 'p' are unknown after increment}} - - if (*(p + i + 2)) - {} - } - -// CHECK: In function: f21 -// CHECK: [B7] -// CHECK: 1: *(p + i) -// CHECK: [B6] -// CHECK: 1: *(p + i + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B5] -// CHECK: 1: flag -// CHECK: upper_bound(p) = 2 -// CHECK: [B4] -// CHECK: 1: i -// CHECK: upper_bound(p) = 2 -// CHECK: [B3] -// CHECK: 1: i++ -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: 2: *(p + i + 2) -// CHECK: upper_bound(p) = 2 -// CHECK: [B1] -// CHECK-NOT: upper_bound(p) -// + a == 1 ? i++ : a++; // expected-error {{inferred bounds for 'p' are unknown after increment}} + + if (*(p + i + 2)) { + a = 3; + } + } + +// CHECK: Function: f20 +// CHECK: Block: B8 (Entry), Pred: Succ: B7 + +// CHECK: Block: B7, Pred: B8, Succ: B6, B0 +// CHECK: Widened bounds before stmt: *(p + i) +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B6, Pred: B7, Succ: B5, B0 +// CHECK: Widened bounds before stmt: *(p + i + 1) +// CHECK: p: bounds(p, p + i + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B3, B4 +// CHECK: Widened bounds before stmt: a == 1 +// CHECK: p: bounds(p, p + i + 1 + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B2 +// CHECK: Widened bounds before stmt: a++ +// CHECK: p: bounds(p, p + i + 1 + 1) + +// CHECK: Block: B3, Pred: B5, Succ: B2 +// CHECK: Widened bounds before stmt: i++ +// CHECK: p: bounds(p, p + i + 1 + 1) + +// CHECK: Block: B2, Pred: B3, B4, Succ: B1, B0 +// CHECK: Widened bounds before stmt: a == 1 ? i++ : a++ +// CHECK: p: bounds(p, p + i + 1 + 1) + +// CHECK: Widened bounds before stmt: *(p + i + 2) +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B1, Pred: B2, Succ: B0 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + i) +} + +void f21() { + _Nt_array_ptr p : count(0) = ""; + + if (((*p && *(p + 1)) && *(p + 2)) && (*(p + 3) && *(p + 4))) { + a = 1; + } + +// CHECK: Function: f21 +// CHECK: Block: B8 (Entry), Pred: Succ: B7 + +// CHECK: Block: B7, Pred: B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B7, Succ: B5, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(p + 3) +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + 4) +// CHECK: p: bounds(p, p + 3 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 4 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, B6, B7, Succ: B0 } void f22() { _Nt_array_ptr p : count(0) = ""; + _Nt_array_ptr q : count(0) = ""; + + if (((*p && *(p + 1))) && *(p + 2) && (*q && *(q + 1)) && (*(p + 3) && *(p + 4))) { + a = 1; + } + +// CHECK: Function: f22 +// CHECK: Block: B10 (Entry), Pred: Succ: B9 + +// CHECK: Block: B9, Pred: B10, Succ: B8, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: _Nt_array_ptr q : count(0) = ""; +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0) + +// CHECK: Block: B8, Pred: B9, Succ: B7, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 0) - if (((*p && *(p + 1)) && *(p + 2)) && (*(p + 3) && *(p + 4))) - {} - -// CHECK: In function: f22 -// CHECK: [B7] -// CHECK: 2: *p -// CHECK: [B6] -// CHECK: 1: *(p + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B5] -// CHECK: 1: *(p + 2) -// CHECK: upper_bound(p) = 2 -// CHECK: [B4] -// CHECK: 1: *(p + 3) -// CHECK: upper_bound(p) = 3 -// CHECK: [B3] -// CHECK: 1: *(p + 4) -// CHECK: upper_bound(p) = 4 -// CHECK: [B2] -// CHECK: upper_bound(p) = 5 - } +// CHECK: Block: B7, Pred: B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 1 + 1) +// CHECK: q: bounds(q, q + 0) + +// CHECK: Block: B6, Pred: B7, Succ: B5, B1 +// CHECK: Widened bounds before stmt: *q +// CHECK: p: bounds(p, p + 2 + 1) +// CHECK: q: bounds(q, q + 0) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: *(q + 1) +// CHECK: p: bounds(p, p + 2 + 1) +// CHECK: q: bounds(q, q + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(p + 3) +// CHECK: p: bounds(p, p + 2 + 1) +// CHECK: q: bounds(q, q + 1 + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + 4) +// CHECK: p: bounds(p, p + 3 + 1) +// CHECK: q: bounds(q, q + 1 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 4 + 1) +// CHECK: q: bounds(q, q + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, B6, B7, B8, B9, Succ: B0 +} void f23() { _Nt_array_ptr p : count(0) = ""; - _Nt_array_ptr q : count(0) = ""; + _Nt_array_ptr q : count(1) = "a"; + _Nt_array_ptr r : count(2) = "ab"; - if (((*p && *(p + 1))) && *(p + 2) && (*q && *(q + 1)) && (*(p + 3) && *(p + 4))) - {} - -// CHECK: In function: f23 -// CHECK: [B9] -// CHECK: 3: *p -// CHECK: [B8] -// CHECK: 1: *(p + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B7] -// CHECK: 1: *(p + 2) -// CHECK: upper_bound(p) = 2 -// CHECK: [B6] -// CHECK: 1: *q -// CHECK: upper_bound(p) = 3 -// CHECK: [B5] -// CHECK: 1: *(q + 1) -// CHECK: upper_bound(p) = 3 -// CHECK: upper_bound(q) = 1 -// CHECK: [B4] -// CHECK: 1: *(p + 3) -// CHECK: upper_bound(p) = 3 -// CHECK: upper_bound(q) = 2 -// CHECK: [B3] -// CHECK: 1: *(p + 4) -// CHECK: upper_bound(p) = 4 -// CHECK: upper_bound(q) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 5 -// CHECK: upper_bound(q) = 2 + if (*(r + 2) && *(p) && *(1 + q) && *(1 + p) && *(r + 3) && *(q + 2)) { + a = 1; + } + +// CHECK: Function: f23 +// CHECK: Block: B9 (Entry), Pred: Succ: B8 + +// CHECK: Block: B8, Pred: B9, Succ: B7, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: _Nt_array_ptr q : count(1) = "a"; +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: _Nt_array_ptr r : count(2) = "ab"; +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 1) + +// CHECK: Widened bounds before stmt: *(r + 2) +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 1) +// CHECK: r: bounds(r, r + 2) + +// CHECK: Block: B7, Pred: B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: *(p) +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 1) +// CHECK: r: bounds(r, r + 2 + 1) + +// CHECK: Block: B6, Pred: B7, Succ: B5, B1 +// CHECK: Widened bounds before stmt: *(1 + q) +// CHECK: p: bounds(p, (p) + 1) +// CHECK: q: bounds(q, q + 1) +// CHECK: r: bounds(r, r + 2 + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: *(1 + p) +// CHECK: p: bounds(p, (p) + 1) +// CHECK: q: bounds(q, 1 + q + 1) +// CHECK: r: bounds(r, r + 2 + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(r + 3) +// CHECK: p: bounds(p, 1 + p + 1) +// CHECK: q: bounds(q, 1 + q + 1) +// CHECK: r: bounds(r, r + 2 + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(q + 2) +// CHECK: p: bounds(p, 1 + p + 1) +// CHECK: q: bounds(q, 1 + q + 1) +// CHECK: r: bounds(r, r + 3 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, 1 + p + 1) +// CHECK: q: bounds(q, q + 2 + 1) +// CHECK: r: bounds(r, r + 3 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, B6, B7, B8, Succ: B0 } void f24() { _Nt_array_ptr p : count(0) = "a"; - if (*p || *(p + 1)) // expected-error {{out-of-bounds memory access}} -{} - -// CHECK: In function: f24 -// CHECK: [B4] -// CHECK: 2: *p -// CHECK: [B3] -// CHECK: 1: *(p + 1) -// CHECK-NOT: upper_bound(p) -// CHECK: [B2] -// CHECK-NOT: upper_bound(p) + if (*p || *(p + 1)) { // expected-error {{out-of-bounds memory access}} + a = 1; + } + +// CHECK: Function: f24 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 + +// CHECK: Block: B4, Pred: B5, Succ: B2, B3 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B2, Pred: B3, B4, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 +} + +void f25() { + _Nt_array_ptr p : count(0) = "a"; + + if ((*p || *(p + 1)) && *p && (*(p + 1) || *p) && *(p + 1)) { // expected-error {{out-of-bounds memory access}} + a = 1; + } + +// CHECK: Function: f25 +// CHECK: Block: B9 (Entry), Pred: Succ: B8 + +// CHECK: Block: B8, Pred: B9, Succ: B6, B7 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B7, Pred: B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B7, B8, Succ: B5, B1 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B5, Pred: B6, Succ: B3, B4 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B3, Pred: B4, B5, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B6, B7, Succ: B0 +} + +void f26() { + _Nt_array_ptr p : count(0) = "a"; + + if (*p && a > 0) { + a = 1; + } + +// CHECK: Function: f26 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: a > 0 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 +} + +void f27() { + _Nt_array_ptr p : count(0) = "a"; + + while (*p && *(p + 1) && *(p + 2)) { + a = 1; + } + + for (int i = 0; *p && *(p + 1); ++i) { + a = 2; + } + +// CHECK: Function: f27 +// CHECK: Block: B13 (Entry), Pred: Succ: B12 + +// CHECK: Block: B12, Pred: B13, Succ: B11 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "a"; +// CHECK: + +// CHECK: Block: B11, Pred: B7, B12, Succ: B10, B6 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B10, Pred: B11, Succ: B9, B6 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B9, Pred: B10, Succ: B8, B6 +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B8, Pred: B9, Succ: B7 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B7, Pred: B8, Succ: B11 + +// CHECK: Block: B6, Pred: B9, B10, B11, Succ: B5 +// CHECK: Widened bounds before stmt: int i = 0; +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B5, Pred: B2, B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B5 +// CHECK: Widened bounds before stmt: ++i +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B1, Pred: B4, B5, Succ: B0 } diff --git a/clang/test/CheckedC/inferred-bounds/widened-bounds-semantic-compare.c b/clang/test/CheckedC/inferred-bounds/widened-bounds-semantic-compare.c index 473f51706dca..f1b9fd80c954 100644 --- a/clang/test/CheckedC/inferred-bounds/widened-bounds-semantic-compare.c +++ b/clang/test/CheckedC/inferred-bounds/widened-bounds-semantic-compare.c @@ -1,203 +1,431 @@ -// Tests for bounds widening of _Nt_array_ptr's using function to semantically -// compare two expressions. +// Tests for bounds widening of null-terminated arrays using the preorder AST +// to semantically compare two expressions. // -// RUN: %clang_cc1 -fdump-widened-bounds -verify -verify-ignore-unexpected=note -verify-ignore-unexpected=warning %s | FileCheck %s +// RUN: %clang_cc1 -fdump-widened-bounds -verify \ +// RUN: -verify-ignore-unexpected=note -verify-ignore-unexpected=warning %s \ +// RUN: | FileCheck %s + +int a; void f1(int i) { _Nt_array_ptr p : bounds(p, p + i) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after initialization}} - if (*(i + p)) {} + if (*(i + p)) { + a = 1; + } + +// CHECK: Function: f1 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + i) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(i + p) +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, i + p + 1) -// CHECK: In function: f1 -// CHECK: 2: *(i + p) -// CHECK: upper_bound(p) = 1 +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } void f2(int i, int j) { _Nt_array_ptr p : bounds(p, p + (i + j)) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after initialization}} - if (*(p + (j + i))) {} + if (*(p + (j + i))) { + a = 1; + } + +// CHECK: Function: f2 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + (i + j)) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(p + (j + i)) +// CHECK: p: bounds(p, p + (i + j)) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + (j + i) + 1) -// CHECK: In function: f2 -// CHECK: 2: *(p + (j + i)) -// CHECK: upper_bound(p) = 1 +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } void f3(int i, int j) { _Nt_array_ptr p : bounds(p, p + (i * j)) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after initialization}} - if (*(p + (j * i))) {} + if (*(p + (j * i))) { + a = 1; + } -// CHECK: In function: f3 -// CHECK: 2: *(p + (j * i)) -// CHECK: upper_bound(p) = 1 +// CHECK: Function: f3 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + (i * j)) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(p + (j * i)) +// CHECK: p: bounds(p, p + (i * j)) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + (j * i) + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } void f4(int i, int j, int k, int m, int n) { _Nt_array_ptr p : bounds(p, p + i + j + k + m + n) = "a"; - if (*(n + m + k + j + i + p)) {} + if (*(n + m + k + j + i + p)) { + a = 1; + } + +// CHECK: Function: f4 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 -// CHECK: In function: f4 -// CHECK: 2: *(n + m + k + j + i + p) -// CHECK: upper_bound(p) = 1 +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + i + j + k + m + n) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(n + m + k + j + i + p) +// CHECK: p: bounds(p, p + i + j + k + m + n) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, n + m + k + j + i + p + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } void f5(int i, int j, int k, int m, int n) { _Nt_array_ptr p : bounds(p, (p + i) + (j + k) + (m + n)) = "a"; - if (*((n + m + k) + (j + i + p))) {} + if (*((n + m + k) + (j + i + p))) { + a = 1; + } + +// CHECK: Function: f5 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 -// CHECK: In function: f5 -// CHECK: 2: *((n + m + k) + (j + i + p)) -// CHECK: upper_bound(p) = 1 +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, (p + i) + (j + k) + (m + n)) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *((n + m + k) + (j + i + p)) +// CHECK: p: bounds(p, (p + i) + (j + k) + (m + n)) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, (n + m + k) + (j + i + p) + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } void f6(int i, int j) { _Nt_array_ptr p : bounds(p, p + i + j + i + j) = "a"; - if (*(j + j + p + i + i)) {} + if (*(j + j + p + i + i)) { + a = 1; + } + +// CHECK: Function: f6 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + i + j + i + j) = "a"; +// CHECK: -// CHECK: In function: f6 -// CHECK: 2: *(j + j + p + i + i) -// CHECK: upper_bound(p) = 1 +// CHECK: Widened bounds before stmt: *(j + j + p + i + i) +// CHECK: p: bounds(p, p + i + j + i + j) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, j + j + p + i + i + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } void f7(int i, int j) { _Nt_array_ptr p : bounds(p, p + i * j) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after initialization}} - if (*(p + i + j)) {} + if (*(p + i + j)) { + a = 1; + } + +// CHECK: Function: f7 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + i * j) = "a"; +// CHECK: -// CHECK: In function: f7 -// CHECK: 2: *(p + i + j) -// CHECK-NOT: upper_bound(p) +// CHECK: Widened bounds before stmt: *(p + i + j) +// CHECK: p: bounds(p, p + i * j) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + i * j) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } void f8(int i, int j) { _Nt_array_ptr p : bounds(p, p + i + j) = "a"; - if (*(p + i + i)) {} + if (*(p + i + i)) { + a = 1; + } + +// CHECK: Function: f8 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + i + j) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(p + i + i) +// CHECK: p: bounds(p, p + i + j) -// CHECK: In function: f8 -// CHECK: 2: *(p + i + i) -// CHECK-NOT: upper_bound(p) +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + i + j) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } void f9(int i, int j, int k) { _Nt_array_ptr p : bounds(p, (p + i) + (j * k)) = "a"; - if (*((p + i) + (j * k))) {} + if (*((p + i) + (j * k))) { + a = 1; + } + +// CHECK: Function: f9 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, (p + i) + (j * k)) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *((p + i) + (j * k)) +// CHECK: p: bounds(p, (p + i) + (j * k)) -// CHECK: In function: f9 -// CHECK: 2: *((p + i) + (j * k)) -// CHECK: upper_bound(p) = 1 +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, (p + i) + (j * k) + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } void f10(int i, int j, int k) { _Nt_array_ptr p : bounds(p, (p + i) + (j * k)) = "a"; - if (*((p + i) + (j + k))) {} + if (*((p + i) + (j + k))) { + a = 1; + } + +// CHECK: Function: f10 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, (p + i) + (j * k)) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *((p + i) + (j + k)) +// CHECK: p: bounds(p, (p + i) + (j * k)) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, (p + i) + (j * k)) -// CHECK: In function: f10 -// CHECK: 2: *((p + i) + (j + k)) -// CHECK-NOT: upper_bound(p) +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } void f11(int i, int j) { _Nt_array_ptr p : bounds(p, p + i + j) = "a"; - if (*(j + i + p)) - if (*(i + j + p - 1 - -4 + -2)) - {} + if (*(j + i + p)) { + a = 1; + if (*(i + j + p - 1 - -4 + -2)) { + a = 2; + } + } + +// CHECK: Function: f11 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + i + j) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(j + i + p) +// CHECK: p: bounds(p, p + i + j) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, j + i + p + 1) -// CHECK: In function: f11 -// CHECK: [B4] -// CHECK: 1: _Nt_array_ptr p : bounds(p, p + i + j) = "a"; -// CHECK: 2: *(j + i + p) -// CHECK: [B3] -// CHECK: 1: *(i + j + p - 1 - -4 + -2) -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 +// CHECK: Widened bounds before stmt: *(i + j + p - 1 - -4 + -2) +// CHECK: p: bounds(p, j + i + p + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, i + j + p - 1 - -4 + -2 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 } void f12() { _Nt_array_ptr p : bounds(p, p) = "a"; - if (*(p + 0)) - if (*(p + 1 - 1 + -1 - +1 - -3)) - {} + if (*(p + 0)) { + a = 1; + if (*(p + 1 - 1 + -1 - +1 - -3)) { + a = 2; + } + } + +// CHECK: Function: f12 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(p + 0) +// CHECK: p: bounds(p, p) -// CHECK: In function: f12 -// CHECK: [B4] -// CHECK: 1: _Nt_array_ptr p : bounds(p, p) = "a"; -// CHECK: 2: *(p + 0) -// CHECK: [B3] -// CHECK: 1: *(p + 1 - 1 + -1 - +1 - -3) -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0 + 1) + +// CHECK: Widened bounds before stmt: *(p + 1 - 1 + -1 - +1 - -3) +// CHECK: p: bounds(p, p + 0 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1 - 1 + -1 - +1 - -3 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 } void f13(int i, int j) { _Nt_array_ptr p : bounds(p, p + (i * j * 2 + 2 + 1)) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after initialization}} - if (*(p + (i * j * 2 + 3))) - if (*(p + (i * j * 2 + 1 + 1 + 1) + 1)) - {} + if (*(p + (i * j * 2 + 3))) { + a = 1; + if (*(p + (i * j * 2 + 1 + 1 + 1) + 1)) { + a = 2; + } + } + +// CHECK: Function: f13 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 -// CHECK: In function: f13 -// CHECK: [B4] -// CHECK: 1: _Nt_array_ptr p : bounds(p, p + (i * j * 2 + 2 + 1)) = "a"; -// CHECK: 2: *(p + (i * j * 2 + 3)) -// CHECK: [B3] -// CHECK: 1: *(p + (i * j * 2 + 1 + 1 + 1) + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + (i * j * 2 + 2 + 1)) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(p + (i * j * 2 + 3)) +// CHECK: p: bounds(p, p + (i * j * 2 + 2 + 1)) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + (i * j * 2 + 3) + 1) + +// CHECK: Widened bounds before stmt: *(p + (i * j * 2 + 1 + 1 + 1) + 1) +// CHECK: p: bounds(p, p + (i * j * 2 + 3) + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + (i * j * 2 + 1 + 1 + 1) + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 } void f14(int i) { _Nt_array_ptr p : bounds(p, p + (i * 1)) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after initialization}} - if (*(p + (i * 2))) - {} + if (*(p + (i * 2))) { + a = 1; + } + +// CHECK: Function: f14 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + (i * 1)) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(p + (i * 2)) +// CHECK: p: bounds(p, p + (i * 1)) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + (i * 1)) -// CHECK: In function: f14 -// CHECK: [B2] -// CHECK-NOT: upper_bound(p) +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } void f15(_Nt_array_ptr p : count(6)) { - if (*(p + (1 * (2 * 3)))) - {} + if (*(p + (1 * (2 * 3)))) { + a = 1; + } -// CHECK: In function: f15 -// [B2] -// 1: _Nt_array_ptr p : count(6) = "a"; -// 2: *(p + (1 * (2 * 3))) -// [B1] -// upper_bound(p) = 1 +// CHECK: Function: f15 +// CHECK: Block: B3 (Entry), Pred: Succ: B2 + +// CHECK: Block: B2, Pred: B3, Succ: B1, B0 +// CHECK: Widened bounds before stmt: *(p + (1 * (2 * 3))) +// CHECK: p: bounds(p, p + 6) + +// CHECK: Block: B1, Pred: B2, Succ: B0 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + (1 * (2 * 3)) + 1) } void f16(int i, int j) { _Nt_array_ptr p : bounds(p, p + i + j + 8) = "a"; - if (*(p + (i + 3 * 2 + (j + 2)))) {} + if (*(p + (i + 3 * 2 + (j + 2)))) { + a = 1; + } + +// CHECK: Function: f16 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + i + j + 8) = "a"; +// CHECK: -// CHECK: In function: f16 -// [B2] -// 1: _Nt_array_ptr p : bounds(p, p + i + j + 8) = "a"; -// 2: *(p + (i + 3 * 2 + (j + 2))) -// [B1] -// upper_bound(p) = 1 +// CHECK: Widened bounds before stmt: *(p + (i + 3 * 2 + (j + 2))) +// CHECK: p: bounds(p, p + i + j + 8) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + (i + 3 * 2 + (j + 2)) + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } void f17(_Nt_array_ptr p : bounds(p - 10, p - 3)) { - if (p[-(1 + 2)]) {} + if (p[-(1 + 2)]) { + a = 1; + } + +// CHECK: Function: f17 +// CHECK: Block: B3 (Entry), Pred: Succ: B2 + +// CHECK: Block: B2, Pred: B3, Succ: B1, B0 +// CHECK: Widened bounds before stmt: p[-(1 + 2)] +// CHECK: p: bounds(p - 10, p - 3) -// CHECK: In function: f17 -// [B2] -// 1: p[-(1 + 2)] -// [B1] -// upper_bounds(p) = 1 +// CHECK: Block: B1, Pred: B2, Succ: B0 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p - 10, p + -(1 + 2) + 1) } diff --git a/clang/test/CheckedC/inferred-bounds/widened-bounds-strings-examples.c b/clang/test/CheckedC/inferred-bounds/widened-bounds-strings-examples.c deleted file mode 100644 index 6228efe111b7..000000000000 --- a/clang/test/CheckedC/inferred-bounds/widened-bounds-strings-examples.c +++ /dev/null @@ -1,94 +0,0 @@ -// Tests for bounds widening for K&R string related functions. -// -// RUN: %clang_cc1 -fdump-widened-bounds -verify -verify-ignore-unexpected=note -verify-ignore-unexpected=warning %s | FileCheck %s - -// Return length of p (adapted from p. 39, K&R 2nd Edition). -// p implicitly has count(0). -int my_strlen(_Nt_array_ptr p) { - int i = 0; - // Create a temporary whose count of elements - // can change. - _Nt_array_ptr s : count(i) = p; - // s[i] implies that the count can increase - // by 1. - while (s[i]) - ++i; // expected-error {{inferred bounds for 's' are unknown after increment}} - return i; - -// CHECK: In function: my_strlen -// CHECK: [B4] -// CHECK: 1: ++i -// CHECK: upper_bound(s) = 1 -} - -// Delete all c from p (adapted from p. 47, K&R 2nd Edition) -// p implicltly has count(0). -void squeeze(_Nt_array_ptr p, char c) { - int i = 0, j = 0; - // Create a temporary whose count of elements can - // change. - _Nt_array_ptr s : count(i) = p; - for ( ; s[i]; i++) { // expected-error {{inferred bounds for 's' are unknown after increment}} - // We will widen the bounds of s so that we - // can assign to s[j] when j == i. - _Nt_array_ptr tmp : count(i + 1) = s; - if (tmp[i] != c) - tmp[j++] = tmp[i]; - } - // if i==j, this writes a 0 at the upper bound. Writing a 0 at the upper bound - // is allowed for pointers to null-terminated arrays. It is not allowed for - // regular arrays. - s[j] = 0; - -// CHECK: In function: squeeze -// CHECK: [B5] -// CHECK: 1: _Nt_array_ptr tmp : count(i + 1) = s; -// CHECK: 2: tmp[i] != c -// CHECK: upper_bound(s) = 1 - -// CHECK: [B4] -// CHECK: 1: tmp[j++] = tmp[i] -// CHECK: upper_bound(s) = 1 - -// CHECK: [B2] -// CHECK: 1: i++ -// CHECK: upper_bound(s) = 1 -} - -// Reverse a string in place (p. 62, K&R 2nd Edition). -// p implicitly has count(0). -void reverse(_Nt_array_ptr p) { - int len = 0; - // Calculate the length of the string. - _Nt_array_ptr s : count(len) = p; - for (; s[len]; len++); // expected-error {{inferred bounds for 's' are unknown after increment}} - - // Now that we know the length, use s just like we would use an array_ptr. - for (int i = 0, j = len - 1; i < j; i++, j--) { - int c = s[i]; - s[i] = s[j]; - s[j] = c; - } - -// CHECK: In function: reverse -// CHECK: [B6] -// CHECK: 1: len++ -// CHECK: upper_bound(s) = 1 -} - -// Return < 0 if s < t, 0 if s == t, > 0 if s > t. -// Adapted from p.106, K&R 2nd Edition. -// s and t implicitly have count(0). -int my_strcmp(_Nt_array_ptr s, _Nt_array_ptr t) { - // Reading *s and *t is allowed for count(0) - for (; *s == *t; s++, t++) // Incrementing s, t allowed because *s, *t != `\0` - if (*s) - return 0; - return *s - *t; - -// CHECK: In function: my_strcmp -// CHECK: [B3] -// CHECK: 1: 0 -// CHECK: 2: return [B3.1]; -// CHECK: upper_bound(s) = 1 -} diff --git a/clang/test/CheckedC/inferred-bounds/widened-bounds-where-clauses.c b/clang/test/CheckedC/inferred-bounds/widened-bounds-where-clauses.c new file mode 100644 index 000000000000..c2b24556faa3 --- /dev/null +++ b/clang/test/CheckedC/inferred-bounds/widened-bounds-where-clauses.c @@ -0,0 +1,414 @@ +// Tests for bounds widening of null-terminated arrays in presence of where +// clauses. +// +// RUN: %clang_cc1 -fdump-widened-bounds -verify \ +// RUN: -verify-ignore-unexpected=note -verify-ignore-unexpected=warning %s \ +// RUN: | FileCheck %s + +int a; + +void f1(_Nt_array_ptr p : bounds(p, p + 1)) { + if (*(p + 1)) { + a = 1; + } + + int x = 1 _Where p : bounds(p, p + x); + + if (*(p + 1)) { + a = 2; + } + + if (*(p + x)) { + a = 3; + if (*(p + x + 1)) { + a = 4; + if (*(p + x + 2)) { + a = 5; + } + } + } + +// CHECK: Function: f1 +// CHECK: Block: B10 (Entry), Pred: Succ: B9 + +// CHECK: Block: B9, Pred: B10, Succ: B8, B7 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B8, Pred: B9, Succ: B7 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B7, Pred: B8, B9, Succ: B6, B5 +// CHECK: Widened bounds before stmt: int x = 1; +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + x) + +// CHECK: Block: B6, Pred: B7, Succ: B5 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + x) + +// CHECK: Block: B5, Pred: B6, B7, Succ: B4, B1 +// CHECK: Widened bounds before stmt: *(p + x) +// CHECK: p: bounds(p, p + x) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + x + 1) + +// CHECK: Widened bounds before stmt: *(p + x + 1) +// CHECK: p: bounds(p, p + x + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + x + 1 + 1) + +// CHECK: Widened bounds before stmt: *(p + x + 2) +// CHECK: p: bounds(p, p + x + 1 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + x + 2 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, Succ: B0 +} + +void f2() { + _Nt_array_ptr p : bounds(p, p + 1) = "a"; + int x = 1 _Where p : count(x); + + if (*(p + 1)) { + a = 1; + } else if (*(p + x)) { + a = 2; + } + +// CHECK: Function: f2 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B6, Succ: B4, B3 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + 1) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: int x = 1; +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + x) + +// CHECK: Block: B4, Pred: B5, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + x) + +// CHECK: Block: B3, Pred: B5, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + x) +// CHECK: p: bounds(p, p + x) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + x + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 +} + +void f3(_Nt_array_ptr p : bounds(p, p + 1)) { + int x = 1 _Where p : bounds(p, p + x); + + if (*(p + x)) { + x = 0; // expected-error {{inferred bounds for 'p' are unknown after assignment}} + if (*(p + x + 1)) { + a = 1; + } + } + +// CHECK: Function: f3 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: int x = 1; +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p + x) +// CHECK: p: bounds(p, p + x) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: x = 0 +// CHECK: p: bounds(p, p + x + 1) + +// CHECK: Widened bounds before stmt: *(p + x + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 +} + +void f4(_Nt_array_ptr p : bounds(p, p + i), int i) { + if (*(p + i)) { + i = 0; // expected-error {{inferred bounds for 'p' are unknown after assignment}} + if (*(p + i + 1)) { + a = 1 _Where p : bounds(p, p + i); + if (*(p + i)) { + a = 2; + } + } + } + +// CHECK: Function: f4 +// CHECK: Block: B7 (Entry), Pred: Succ: B6 + +// CHECK: Block: B6, Pred: B7, Succ: B5, B2 +// CHECK: Widened bounds before stmt: *(p + i) +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B2 +// CHECK: Widened bounds before stmt: i = 0 +// CHECK: p: bounds(p, p + i + 1) + +// CHECK: Widened bounds before stmt: *(p + i + 1) +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + i) + +// CHECK: Widened bounds before stmt: *(p + i) +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B3, Pred: B4, Succ: B2 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + i + 1) + + if (*(p + i)) { + a = 3; + } + +// CHECK: Block: B2, Pred: B3, B4, B5, B6, Succ: B1, B0 +// CHECK: Widened bounds before stmt: *(p + i) +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B1, Pred: B2, Succ: B0 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + i + 1) +} + +void f5() { + _Nt_array_ptr p : bounds(p, p + 1) = "a"; + _Nt_array_ptr q : bounds(p, p + 1) = p; + + if (*(p + 1)) { + a = 1; + } + +// CHECK: Function: f5 +// CHECK: Block: B8 (Entry), Pred: Succ: B7 + +// CHECK: Block: B7, Pred: B8, Succ: B6, B5 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + 1) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: _Nt_array_ptr q : bounds(p, p + 1) = p; +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(p, p + 1) + +// CHECK: Block: B6, Pred: B7, Succ: B5 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1 + 1) +// CHECK: q: bounds(p, p + 1 + 1) + + a = 2 _Where q : bounds(q, q + 1); + + if (*(p + 1)) { + a = 3; + } + +// CHECK: Block: B5, Pred: B6, B7, Succ: B4, B3 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(p, p + 1) +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 1 + 1) +// CHECK: q: bounds(q, q + 1) + + if (*(q + 1)) { + a = 4; + } + +// CHECK: Block: B3, Pred: B4, B5, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(q + 1) +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(p, q + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 +} + +void f6() { + _Nt_array_ptr p = "a"; + _Nt_array_ptr q = "b"; + _Nt_array_ptr r = "c"; + + int x = 1 _Where p : bounds(p, p + x + 1) _And q : count(x) _And r : bounds(p, p + x); + + if (*(p + x) && *(p + x + 1) && *(q + x) && *(q + x + 1)) { // expected-error {{it is not possible to prove that the inferred bounds of 'r' imply the declared bounds of 'r' after statement}} + a = 1; + } + +// CHECK: Function: f6 +// CHECK: Block: B7 (Entry), Pred: Succ: B6 + +// CHECK: Block: B6, Pred: B7, Succ: B5, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: _Nt_array_ptr q : count(0) = "b"; +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: _Nt_array_ptr r : count(0) = "c"; +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0) + +// CHECK: Widened bounds before stmt: int x = 1; +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0) +// CHECK: r: bounds(r, r + 0) + +// CHECK: Widened bounds before stmt: *(p + x) +// CHECK: p: bounds(p, p + x + 1) +// CHECK: q: bounds(q, q + x) +// CHECK: r: bounds(p, p + x) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: *(p + x + 1) +// CHECK: p: bounds(p, p + x + 1) +// CHECK: q: bounds(q, q + x) +// CHECK: r: bounds(r, p + x + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(q + x) +// CHECK: p: bounds(p, p + x + 1 + 1) +// CHECK: q: bounds(q, q + x) +// CHECK: r: bounds(r, p + x + 1 + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(q + x + 1) +// CHECK: p: bounds(p, p + x + 1 + 1) +// CHECK: q: bounds(q, q + x + 1) +// CHECK: r: bounds(r, p + x + 1 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + x + 1 + 1) +// CHECK: q: bounds(q, q + x + 1 + 1) +// CHECK: r: bounds(r, p + x + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, B6, Succ: B0 +} + +void f7() { + _Nt_array_ptr p = "a"; + + int x = 1 _Where x > 0 _And p : count(x) _And 1 == 1 _And p : count(x + 10); + + if (*(p + x)) { + a = 1; + } + + if (*(p + x + 10)) { + a = 2; + } + +// CHECK: Function: f7 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B6, Succ: B4, B3 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: int x = 1; +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: *(p + x) +// CHECK: p: bounds(p, p + x + 10) + +// CHECK: Block: B4, Pred: B5, Succ: B3 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + x + 10) + +// CHECK: Block: B3, Pred: B4, B5, Succ: B2, B1 +// CHECK: Widened bounds before stmt: *(p + x + 10) +// CHECK: p: bounds(p, p + x + 10) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + x + 10 + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 +} + +void f8() { + _Nt_array_ptr p = "a"; + + while (*p) { + p = "b" _Where p : count(0); + } + +// CHECK: Function: f8 +// CHECK: Block: B10 (Entry), Pred: Succ: B9 + +// CHECK: Block: B9, Pred: B10, Succ: B8 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "a"; +// CHECK: + +// CHECK: Block: B8, Pred: B6, B9, Succ: B7, B5 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B7, Pred: B8, Succ: B6 +// CHECK: Widened bounds before stmt: p = "b" +// CHECK: p: bounds(p, p + 1) + + int x = 1 _Where p : bounds(p, p + x); + + while (*(p + x)) { + x = 0; // expected-error {{inferred bounds for 'p' are unknown after assignment}} + a = 1 _Where p : bounds(p, p + x); + } + +// CHECK: Block: B6, Pred: B7, Succ: B8 + +// CHECK: Block: B5, Pred: B8, Succ: B4 +// CHECK: Widened bounds before stmt: int x = 1; +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B4, Pred: B2, B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: *(p + x) +// CHECK: p: bounds(p, p + x) + +// CHECK: Block: B3, Pred: B4, Succ: B2 +// CHECK: Widened bounds before stmt: x = 0 +// CHECK: p: bounds(p, p + x + 1) + +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B2, Pred: B3, Succ: B4 + +// CHECK: Block: B1, Pred: B4, Succ: B0 +} diff --git a/clang/test/CheckedC/inferred-bounds/widened-bounds.c b/clang/test/CheckedC/inferred-bounds/widened-bounds.c index a1d7286f65ec..914b8056956a 100644 --- a/clang/test/CheckedC/inferred-bounds/widened-bounds.c +++ b/clang/test/CheckedC/inferred-bounds/widened-bounds.c @@ -1,104 +1,259 @@ -// Tests for datafow analysis for bounds widening of _Nt_array_ptr's. +// Tests for datafow analysis for bounds widening of null-terminated arrays. // -// RUN: %clang_cc1 -fdump-widened-bounds -verify -verify-ignore-unexpected=note -verify-ignore-unexpected=warning %s | FileCheck %s +// RUN: %clang_cc1 -fdump-widened-bounds -verify \ +// RUN: -verify-ignore-unexpected=note -verify-ignore-unexpected=warning %s \ +// RUN: | FileCheck %s #include #include +int a; + void f1() { _Nt_array_ptr p : count(0) = "a"; - if (*p) {} + if (*p) { + a = 1; + } + a = 2; + +// CHECK: Function: f1 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 -// CHECK: In function: f1 -// CHECK: [B3] -// CHECK: 2: *p -// CHECK: [B2] -// CHECK: upper_bound(p) = 1 +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 0) } void f2() { _Nt_array_ptr p : count(0) = "ab"; - if (*p) - if (*(p + 1)) - if (*(p + 2)) - {} - -// CHECK: In function: f2 -// CHECK: [B5] -// CHECK: 2: *p -// CHECK: [B4] -// CHECK: 1: *(p + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: *(p + 2) -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 3 + if (*p) { + a = 1; + if (*(p + 1)) { + a = 2; + if (*(p + 2)) { + a = 3; + } + a = 4; + } + a = 5; + } + a = 6; + +// CHECK: Function: f2 +// CHECK: Block: B8 (Entry), Pred: Succ: B7 + +// CHECK: Block: B7, Pred: B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "ab"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B7, Succ: B5, B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B3 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B3, Pred: B4, B5, Succ: B2 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B2, Pred: B3, B6, Succ: B1 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B1, Pred: B2, B7, Succ: B0 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 0) } void f3() { _Nt_array_ptr p : count(0) = "a"; - int a; if (*p) { p = "a"; - if (a) {} + if (a) { + a = 1; + } + a = 2; } + a = 3; + +// CHECK: Function: f3 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B2 +// CHECK: Widened bounds before stmt: p = "a" +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: a +// CHECK: p: bounds(p, p + 0) -// CHECK: In function: f3 -// CHECK: [B4] -// CHECK: 3: *p -// CHECK: [B3] -// CHECK: 1: p = "a" -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK-NOT: upper_bound(p) = 1 +// CHECK: Block: B3, Pred: B4, Succ: B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B2, Pred: B3, B4, Succ: B1 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B1, Pred: B2, B5, Succ: B0 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 0) } void f4(_Nt_array_ptr p : count(0)) { - if (p[0]) {} - -// CHECK: In function: f4 -// CHECK: [B7] -// CHECK: 1: p[0] -// CHECK: [B6] -// CHECK: upper_bound(p) = 1 + if (p[0]) { + a = 1; + } + a = 2; _Nt_array_ptr q : count(0) = "a"; - if (0[q]) {} -// CHECK: [B5] -// CHECK: 2: 0[q] -// CHECK: [B4] -// CHECK: upper_bound(q) = 1 - - if ((((q[0])))) {} -// CHECK: [B3] -// CHECK: 1: (((q[0]))) -// CHECK: [B2] -// CHECK: upper_bound(q) = 1 + if (0[q]) { + a = 3; + } + a = 4; + + if ((((q[0])))) { + a = 5; + } + a = 6; + +// CHECK: Function: f4 +// CHECK: Block: B8 (Entry), Pred: Succ: B7 + +// CHECK: Block: B7, Pred: B8, Succ: B6, B5 +// CHECK: Widened bounds before stmt: p[0] +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B7, Succ: B5 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0 + 1) + +// CHECK: Block: B5, Pred: B6, B7, Succ: B4, B3 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: _Nt_array_ptr q : count(0) = "a"; +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: 0[q] +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0) + +// CHECK: Block: B4, Pred: B5, Succ: B3 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0 + 1) + +// CHECK: Block: B3, Pred: B4, B5, Succ: B2, B1 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0) + +// CHECK: Widened bounds before stmt: (((q[0]))) +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0 + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0) } void f5() { char p _Nt_checked[] : count(0) = "abc"; - if (p[0]) - if (p[1]) - if (p[2]) - {} - -// CHECK: In function: f5 -// CHECK: [B5] -// CHECK: 2: p[0] -// CHECK: [B4] -// CHECK: 1: p[1] -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: p[2] -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 3 + if (p[0]) { + a = 1; + if (p[1]) { + a = 2; + if (p[2]) { + a = 3; + } + a = 4; + } + a = 5; + } + a = 6; + +// CHECK: Function: f5 +// CHECK: Block: B8 (Entry), Pred: Succ: B7 + +// CHECK: Block: B7, Pred: B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: char p_Nt_checked[] : count(0) = "abc"; +// CHECK: + +// CHECK: Widened bounds before stmt: p[0] +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B7, Succ: B5, B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0 + 1) + +// CHECK: Widened bounds before stmt: p[1] +// CHECK: p: bounds(p, p + 0 + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B3 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Widened bounds before stmt: p[2] +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B3, Pred: B4, B5, Succ: B2 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B2, Pred: B3, B6, Succ: B1 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 0 + 1) + +// CHECK: Block: B1, Pred: B2, B7, Succ: B0 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 0) } void f6(int i) { @@ -106,106 +261,287 @@ void f6(int i) { if (p[0]) { i = 0; // expected-error {{inferred bounds for 'p' are unknown after assignment}} - if (p[1]) {} - } - -// CHECK: In function: f6 -// CHECK: [B4] -// CHECK: 2: p[0] -// CHECK: [B3] -// CHECK: 1: i = 0 -// CHECK: 2: p[1] -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK-NOT: upper_bound(p) + if (p[1]) { + a = 1; + } + a = 2; + } + a = 3; + +// CHECK: Function: f6 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: char p_Nt_checked[] : bounds(p + i, p) = "abc"; +// CHECK: + +// CHECK: Widened bounds before stmt: p[0] +// CHECK: p: bounds(p + i, p) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B2 +// CHECK: Widened bounds before stmt: i = 0 +// CHECK: p: bounds(p + i, p + 0 + 1) + +// CHECK: Widened bounds before stmt: p[1] +// CHECK: p: bounds(p + i, p) + +// CHECK: Block: B3, Pred: B4, Succ: B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p + i, p) + +// CHECK: Block: B2, Pred: B3, B4, Succ: B1 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p + i, p) } void f7(char p _Nt_checked[] : count(0)) { - if (p[0]) {} - -// CHECK: In function: f7 -// CHECK: [B7] -// CHECK: 1: p[0] -// CHECK: [B6] -// CHECK: upper_bound(p) = 1 + if (p[0]) { + a = 1; + } + a = 2; char q _Nt_checked[] : count(0) = "a"; - if (0[q]) {} -// CHECK: [B5] -// CHECK: 2: 0[q] -// CHECK: [B4] -// CHECK: upper_bound(q) = 1 - - if ((((q[0])))) {} -// CHECK: [B3] -// CHECK: 1: (((q[0]))) -// CHECK: [B2] -// CHECK: upper_bound(q) = 1 + if (0[q]) { + a = 3; + } + a = 4; + + if ((((q[0])))) { + a = 5; + } + a = 6; + +// CHECK: Function: f7 +// CHECK: Block: B8 (Entry), Pred: Succ: B7 + +// CHECK: Block: B7, Pred: B8, Succ: B6, B5 +// CHECK: Widened bounds before stmt: p[0] +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B7, Succ: B5 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0 + 1) + +// CHECK: Block: B5, Pred: B6, B7, Succ: B4, B3 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: char q_Nt_checked[] : count(0) = "a"; +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: 0[q] +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0) + +// CHECK: Block: B4, Pred: B5, Succ: B3 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0 + 1) + +// CHECK: Block: B3, Pred: B4, B5, Succ: B2, B1 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0) + +// CHECK: Widened bounds before stmt: (((q[0]))) +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0 + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 0) +// CHECK: q: bounds(q, q + 0) } void f8() { _Nt_array_ptr p : count(2) = "abc"; - if (*p) - if (*(p + 1)) - if (*(p + 2)) - if (*(p + 3)) - {} - -// CHECK: In function: f8 -// CHECK: [B6] -// CHECK: 2: *p -// CHECK: [B5] -// CHECK: 1: *(p + 1) -// CHECK-NOT: upper_bound(p) -// CHECK: [B4] -// CHECK: 1: *(p + 2) -// CHECK-NOT: upper_bound(p) -// CHECK: [B3] -// CHECK: 1: *(p + 3) -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 + if (*p) { + a = 1; + if (*(p + 1)) { + a = 2; + if (*(p + 2)) { + a = 3; + if (*(p + 3)) { + a = 4; + } + a = 5; + } + a = 6; + } + a = 7; + } + a = 8; + +// CHECK: Function: f8 +// CHECK: Block: B10 (Entry), Pred: Succ: B9 + +// CHECK: Block: B9, Pred: B10, Succ: B8, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(2) = "abc"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 2) + +// CHECK: Block: B8, Pred: B9, Succ: B7, B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 2) + +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 2) + +// CHECK: Block: B7, Pred: B8, Succ: B6, B3 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 2) + +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 2) + +// CHECK: Block: B6, Pred: B7, Succ: B5, B4 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Widened bounds before stmt: *(p + 3) +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B4 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 3 + 1) + +// CHECK: Block: B4, Pred: B5, B6, Succ: B3 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B3, Pred: B4, B7, Succ: B2 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 2) + +// CHECK: Block: B2, Pred: B3, B8, Succ: B1 +// CHECK: Widened bounds before stmt: a = 7 +// CHECK: p: bounds(p, p + 2) + +// CHECK: Block: B1, Pred: B2, B9, Succ: B0 +// CHECK: Widened bounds before stmt: a = 8 +// CHECK: p: bounds(p, p + 2) } void f9(int i) { -_Nt_array_ptr p : bounds(p, p + i) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after initialization}} - - if (*p) - if (*(p + i)) - if (*(p + i + 1)) {} - -// CHECK: In function: f9 -// CHECK: [B5] -// CHECK: 2: *p -// CHECK: [B4] -// CHECK: 1: *(p + i) -// CHECK-NOT: upper_bound(p) -// CHECK: [B3] -// CHECK: 1: *(p + i + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 + _Nt_array_ptr p : bounds(p, p + i) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after initialization}} + + if (*p) { + a = 1; + if (*(p + i)) { + a = 2; + if (*(p + i + 1)) { + a = 3; + } + a = 4; + } + a = 5; + } + a = 6; + +// CHECK: Function: f9 +// CHECK: Block: B8 (Entry), Pred: Succ: B7 + +// CHECK: Block: B7, Pred: B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + i) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B6, Pred: B7, Succ: B5, B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + i) + +// CHECK: Widened bounds before stmt: *(p + i) +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B3 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + i + 1) + +// CHECK: Widened bounds before stmt: *(p + i + 1) +// CHECK: p: bounds(p, p + i + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + i + 1 + 1) + +// CHECK: Block: B3, Pred: B4, B5, Succ: B2 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + i + 1) + +// CHECK: Block: B2, Pred: B3, B6, Succ: B1 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B1, Pred: B2, B7, Succ: B0 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + i) } void f10(int i) { _Nt_array_ptr p : bounds(p, 1 + p + i + 5) = "a"; - if (*(i + p + 1 + 2 + 3)) - if (*(3 + p + i + 4)) - if (*(p + i + 9)) {} - -// CHECK: In function: f10 -// CHECK: [B5] -// CHECK: 2: *(i + p + 1 + 2 + 3) -// CHECK: [B4] -// CHECK: 1: *(3 + p + i + 4) -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: *(p + i + 9) -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 + if (*(i + p + 1 + 2 + 3)) { + a = 1; + if (*(3 + p + i + 4)) { + a = 2; + if (*(p + i + 9)) { + a = 3; + } + a = 4; + } + a = 5; + } + a = 6; + +// CHECK: Function: f10 +// CHECK: Block: B8 (Entry), Pred: Succ: B7 + +// CHECK: Block: B7, Pred: B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, 1 + p + i + 5) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(i + p + 1 + 2 + 3) +// CHECK: p: bounds(p, 1 + p + i + 5) + +// CHECK: Block: B6, Pred: B7, Succ: B5, B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, i + p + 1 + 2 + 3 + 1) + +// CHECK: Widened bounds before stmt: *(3 + p + i + 4) +// CHECK: p: bounds(p, i + p + 1 + 2 + 3 + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B3 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, 3 + p + i + 4 + 1) + +// CHECK: Widened bounds before stmt: *(p + i + 9) +// CHECK: p: bounds(p, 3 + p + i + 4 + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, 3 + p + i + 4 + 1) + +// CHECK: Block: B3, Pred: B4, B5, Succ: B2 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, 3 + p + i + 4 + 1) + +// CHECK: Block: B2, Pred: B3, B6, Succ: B1 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, i + p + 1 + 2 + 3 + 1) + +// CHECK: Block: B1, Pred: B2, B7, Succ: B0 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, 1 + p + i + 5) } void f11(int i, int j) { @@ -213,180 +549,458 @@ void f11(int i, int j) { if (*(p + j)) { i = 0; // expected-error {{inferred bounds for 'p' are unknown after assignment}} - if (*(p + j + 1)) {} + if (*(p + j + 1)) { + a = 1; + } + a = 2; } + a = 3; + +// CHECK: Function: f11 +// CHECK: Block: B10 (Entry), Pred: Succ: B9 + +// CHECK: Block: B9, Pred: B10, Succ: B8, B5 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p + i, p + j) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(p + j) +// CHECK: p: bounds(p + i, p + j) + +// CHECK: Block: B8, Pred: B9, Succ: B7, B6 +// CHECK: Widened bounds before stmt: i = 0 +// CHECK: p: bounds(p + i, p + j + 1) -// CHECK: In function: f11 -// CHECK: [B7] -// CHECK: 2: *(p + j) -// CHECK: [B6] -// CHECK: 1: i = 0 -// CHECK: 2: *(p + j + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B5] -// CHECK-NOT: upper_bound(p) +// CHECK: Widened bounds before stmt: *(p + j + 1) +// CHECK: p: bounds(p + i, p + j) + +// CHECK: Block: B7, Pred: B8, Succ: B6 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p + i, p + j) + +// CHECK: Block: B6, Pred: B7, B8, Succ: B5 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p + i, p + j) + +// CHECK: Block: B5, Pred: B6, B9, Succ: B4, B1 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p + i, p + j) if (*(p + j)) { j = 0; // expected-error {{inferred bounds for 'p' are unknown after assignment}} - if (*(p + j + 1)) {} + if (*(p + j + 1)) { + a = 4; + } + a = 5; } + a = 6; + +// CHECK: Widened bounds before stmt: *(p + j) +// CHECK: p: bounds(p + i, p + j) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B2 +// CHECK: Widened bounds before stmt: j = 0 +// CHECK: p: bounds(p + i, p + j + 1) + +// CHECK: Widened bounds before stmt: *(p + j + 1) +// CHECK: p: bounds(p + i, p + j) -// CHECK: [B4] -// CHECK: 1: *(p + j) -// CHECK: [B3] -// CHECK: 1: j = 0 -// CHECK: 2: *(p + j + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK-NOT: upper_bound(p) +// CHECK: Block: B3, Pred: B4, Succ: B2 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p + i, p + j) + +// CHECK: Block: B2, Pred: B3, B4, Succ: B1 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p + i, p + j) + +// CHECK: Block: B1, Pred: B2, B5, Succ: B0 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p + i, p + j) } void f12(int i, int j) { _Nt_array_ptr p : bounds(p, p + i + j) = "a"; - if (*(((p + i + j)))) - if (*((p) + (i) + (j) + (1))) - if (*((p + i + j) + 2)) {} - -// CHECK: In function: f12 -// CHECK: [B5] -// CHECK: 2: *(((p + i + j))) -// CHECK: [B4] -// CHECK: 1: *((p) + (i) + (j) + (1)) -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: *((p + i + j) + 2) -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 3 + if (*(((p + i + j)))) { + a = 1; + if (*((p) + (i) + (j) + (1))) { + a = 2; + if (*((p + i + j) + 2)) { + a = 3; + } + a = 4; + } + a = 5; + } + a = 6; + +// CHECK: Function: f12 +// CHECK: Block: B8 (Entry), Pred: Succ: B7 + +// CHECK: Block: B7, Pred: B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p + i + j) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(((p + i + j))) +// CHECK: p: bounds(p, p + i + j) + +// CHECK: Block: B6, Pred: B7, Succ: B5, B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + i + j + 1) + +// CHECK: Widened bounds before stmt: *((p) + (i) + (j) + (1)) +// CHECK: p: bounds(p, p + i + j + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B3 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, (p) + (i) + (j) + (1) + 1) + +// CHECK: Widened bounds before stmt: *((p + i + j) + 2) +// CHECK: p: bounds(p, (p) + (i) + (j) + (1) + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, (p + i + j) + 2 + 1) + +// CHECK: Block: B3, Pred: B4, B5, Succ: B2 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, (p) + (i) + (j) + (1) + 1) + +// CHECK: Block: B2, Pred: B3, B6, Succ: B1 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + i + j + 1) + +// CHECK: Block: B1, Pred: B2, B7, Succ: B0 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + i + j) } void f13() { char p _Nt_checked[] : count(1) = "a"; - if (p[0]) - if (1[p]) - if (p[2]) - if (3[p]) - {} - -// CHECK: In function: f13 -// CHECK: [B6] -// CHECK: 2: p[0] -// CHECK: [B5] -// CHECK: 1: 1[p] -// CHECK-NOT: upper_bound(p) -// CHECK: [B4] -// CHECK: 1: p[2] -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: 3[p] -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 3 + if (p[0]) { + a = 1; + if (1[p]) { + a = 2; + if (p[2]) { + a = 3; + if (3[p]) { + a = 4; + } + a = 5; + } + a = 6; + } + a = 7; + } + a = 8; + +// CHECK: Function: f13 +// CHECK: Block: B10 (Entry), Pred: Succ: B9 + +// CHECK: Block: B9, Pred: B10, Succ: B8, B1 +// CHECK: Widened bounds before stmt: char p_Nt_checked[] : count(1) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: p[0] +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B8, Pred: B9, Succ: B7, B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: 1[p] +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B7, Pred: B8, Succ: B6, B3 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Widened bounds before stmt: p[2] +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B6, Pred: B7, Succ: B5, B4 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Widened bounds before stmt: 3[p] +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B4 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 3 + 1) + +// CHECK: Block: B4, Pred: B5, B6, Succ: B3 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B3, Pred: B4, B7, Succ: B2 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B2, Pred: B3, B8, Succ: B1 +// CHECK: Widened bounds before stmt: a = 7 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B1, Pred: B2, B9, Succ: B0 +// CHECK: Widened bounds before stmt: a = 8 +// CHECK: p: bounds(p, p + 1) } void f14(int i) { char p _Nt_checked[] : bounds(p, p + i) = "a"; - if ((1 + i)[p]) - if (p[i]) - if ((1 + i)[p]) {} - -// CHECK: In function: f14 -// CHECK: [B5] -// CHECK: 2: (1 + i)[p] -// CHECK: [B4] -// CHECK: 1: p[i] -// CHECK-NOT: upper_bound(p) -// CHECK: [B3] -// CHECK: 1: (1 + i)[p] -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 + if ((1 + i)[p]) { + a = 1; + if (p[i]) { + a = 2; + if ((1 + i)[p]) { + a = 3; + } + a = 4; + } + a = 5; + } + a = 6; + +// CHECK: Function: f14 +// CHECK: Block: B8 (Entry), Pred: Succ: B7 + +// CHECK: Block: B7, Pred: B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: char p_Nt_checked[] : bounds(p, p + i) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: (1 + i)[p] +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B6, Pred: B7, Succ: B5, B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + i) + +// CHECK: Widened bounds before stmt: p[i] +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B3 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + i + 1) + +// CHECK: Widened bounds before stmt: (1 + i)[p] +// CHECK: p: bounds(p, p + i + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + (1 + i) + 1) + +// CHECK: Block: B3, Pred: B4, B5, Succ: B2 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + i + 1) + +// CHECK: Block: B2, Pred: B3, B6, Succ: B1 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + i) + +// CHECK: Block: B1, Pred: B2, B7, Succ: B0 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + i) } void f15(int i) { _Nt_array_ptr p : bounds(p, p - i) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'p' imply the declared bounds of 'p' after initialization}} - if (*(p - i)) {} -// CHECK: In function: f15 -// CHECK: [B10] -// CHECK: 2: *(p - i) -// CHECK: [B9] -// CHECK: upper_bound(p) = 1 + if (*(p - i)) { + a = 1; + } + +// CHECK: Function: f15 +// CHECK: Block: B11 (Entry), Pred: Succ: B10 + +// CHECK: Block: B10, Pred: B11, Succ: B9, B8 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, p - i) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *(p - i) +// CHECK: p: bounds(p, p - i) + +// CHECK: Block: B9, Pred: B10, Succ: B8 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p - i + 1) _Nt_array_ptr q : count(0) = "a"; - if (*q) - if (*(q - 1)) - {} - -// CHECK: [B8] -// CHECK: 2: *q -// CHECK: [B7] -// CHECK: 1: *(q - 1) -// CHECK: upper_bound(q) = 1 -// CHECK: [B6] -// CHECK: upper_bound(q) = 1 + if (*q) { + a = 2; + if (*(q - 1)) { // expected-error {{out-of-bounds memory access}} + a = 3; + } + } + +// CHECK: Block: B8, Pred: B9, B10, Succ: B7, B5 +// CHECK: Widened bounds before stmt: _Nt_array_ptr q : count(0) = "a"; +// CHECK: p: bounds(p, p - i) + +// CHECK: Widened bounds before stmt: *q +// CHECK: p: bounds(p, p - i) +// CHECK: q: bounds(q, q + 0) + +// CHECK: Block: B7, Pred: B8, Succ: B6, B5 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p - i) +// CHECK: q: bounds(q, q + 1) + +// CHECK: Widened bounds before stmt: *(q - 1) +// CHECK: p: bounds(p, p - i) +// CHECK: q: bounds(q, q + 1) + +// CHECK: Block: B6, Pred: B7, Succ: B5 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p - i) +// CHECK: q: bounds(q, q + 1) _Nt_array_ptr r : bounds(r, r + +1) = "a"; - if (*(r + +1)) - {} + if (*(r + +1)) { + a = 4; + } + +// CHECK: Block: B5, Pred: B6, B7, B8, Succ: B4, B3 +// CHECK: Widened bounds before stmt: _Nt_array_ptr r : bounds(r, r + +1) = "a"; +// CHECK: p: bounds(p, p - i) +// CHECK: q: bounds(q, q + 0) + +// CHECK: Widened bounds before stmt: *(r + +1) +// CHECK: p: bounds(p, p - i) +// CHECK: q: bounds(q, q + 0) +// CHECK: r: bounds(r, r + +1) -// CHECK: [B5] -// CHECK: 2: *(r + +1) -// CHECK: [B4] -// CHECK: upper_bound(r) = 1 +// CHECK: Block: B4, Pred: B5, Succ: B3 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p - i) +// CHECK: q: bounds(q, q + 0) +// CHECK: r: bounds(r, r + +1 + 1) _Nt_array_ptr s : bounds(s, s + -1) = "a"; - if (*(s + -1)) // expected-error {{out-of-bounds memory access}} - {} + if (*(s + -1)) { // expected-error {{out-of-bounds memory access}} + a = 5; + } -// CHECK: [B3] -// CHECK: 2: *(s + -1) -// CHECK: [B2] -// CHECK: upper_bound(s) = 1 +// CHECK: Block: B3, Pred: B4, B5, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr s : bounds(s, s + -1) = "a"; +// CHECK: p: bounds(p, p - i) +// CHECK: q: bounds(q, q + 0) +// CHECK: r: bounds(r, r + +1) + +// CHECK: Widened bounds before stmt: *(s + -1) +// CHECK: p: bounds(p, p - i) +// CHECK: q: bounds(q, q + 0) +// CHECK: r: bounds(r, r + +1) +// CHECK: s: bounds(s, s + -1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p - i) +// CHECK: q: bounds(q, q + 0) +// CHECK: r: bounds(r, r + +1) +// CHECK: s: bounds(s, s + -1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } void f16(_Nt_array_ptr p : bounds(p, p)) { _Nt_array_ptr q : bounds(p, p) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'q' imply the declared bounds of 'q' after initialization}} _Nt_array_ptr r : bounds(p, p + 1) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'r' imply the declared bounds of 'r' after initialization}} - if (*(p)) - if (*(p + 1)) - {} - -// CHECK: In function: f16 -// CHECK: [B4] -// CHECK: 3: *(p) -// CHECK: [B3] -// CHECK: 1: *(p + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: upper_bound(q) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 -// CHECK: upper_bound(q) = 2 -// CHECK: upper_bound(r) = 1 + if (*(p)) { + a = 1; + if (*(p + 1)) { + a = 2; + } + a = 3; + } + +// CHECK: Function: f16 +// CHECK: Block: B6 (Entry), Pred: Succ: B5 + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr q : bounds(p, p) = "a"; +// CHECK: p: bounds(p, p) + +// CHECK: Widened bounds before stmt: _Nt_array_ptr r : bounds(p, p + 1) = "a"; +// CHECK: p: bounds(p, p) +// CHECK: q: bounds(p, p) + +// CHECK: Widened bounds before stmt: *(p) +// CHECK: p: bounds(p, p) +// CHECK: q: bounds(p, p) +// CHECK: r: bounds(p, p + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, (p) + 1) +// CHECK: q: bounds(p, (p) + 1) +// CHECK: r: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, (p) + 1) +// CHECK: q: bounds(p, (p) + 1) +// CHECK: r: bounds(p, p + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1 + 1) +// CHECK: q: bounds(p, p + 1 + 1) +// CHECK: r: bounds(p, p + 1 + 1) + +// CHECK: Block: B2, Pred: B3, B4, Succ: B1 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, (p) + 1) +// CHECK: q: bounds(p, (p) + 1) +// CHECK: r: bounds(p, p + 1) + +// CHECK: Block: B1, Pred: B2, B5, Succ: B0 } void f17(char p _Nt_checked[] : count(1)) { _Nt_array_ptr q : bounds(p, p + 1) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'q' imply the declared bounds of 'q' after initialization}} _Nt_array_ptr r : bounds(p, p) = "a"; // expected-error {{it is not possible to prove that the inferred bounds of 'r' imply the declared bounds of 'r' after initialization}} - if (*(p)) - if (*(p + 1)) - {} - -// CHECK: In function: f17 -// CHECK: [B4] -// CHECK: 3: *(p) -// CHECK: [B3] -// CHECK: 1: *(p + 1) -// CHECK: upper_bound(r) = 1 -// CHECK: [B2] -// CHECK: upper_bound(p) = 1 -// CHECK: upper_bound(q) = 1 -// CHECK: upper_bound(r) = 2 + if (*(p)) { + a = 1; + if (*(p + 1)) { + a = 2; + } + } + +// CHECK: Function: f17 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr q : bounds(p, p + 1) = "a"; +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: _Nt_array_ptr r : bounds(p, p) = "a"; +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p) +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(p, p + 1) +// CHECK: r: bounds(p, p) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(p, p + 1) +// CHECK: r: bounds(p, (p) + 1) + +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(p, p + 1) +// CHECK: r: bounds(p, (p) + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1 + 1) +// CHECK: q: bounds(p, p + 1 + 1) +// CHECK: r: bounds(p, p + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 } void f18() { @@ -395,901 +1009,1684 @@ void f18() { char r _Nt_checked[] : count(0) = "ab"; char s _Nt_checked[] : count(1) = "ab"; - if (p[0]) - if (p[1]) - {} - -// CHECK: In function: f18 -// CHECK: [B13] -// CHECK: 5: p[0] -// CHECK: [B12] -// CHECK: 1: p[1] -// CHECK: [B11] -// CHECK: upper_bound(p) = 1 - - if (q[0]) - if (q[1]) - if (q[2]) - {} - -// CHECK: [B10] -// CHECK: 1: q[0] -// CHECK: [B9] -// CHECK: 1: q[1] -// CHECK: [B8] -// CHECK: 1: q[2] -// CHECK: [B7] -// CHECK: upper_bound(q) = 1 - - if (r[0]) - {} - -// CHECK: [B6] -// CHECK: 1: r[0] -// CHECK: [B5] -// CHECK: upper_bound(r) = 1 - - if (s[0]) - if (s[1]) - {} - -// CHECK: [B4] -// CHECK: 1: s[0] -// CHECK: [B3] -// CHECK: 1: s[1] -// CHECK: [B2] -// CHECK: upper_bound(s) = 1 +// CHECK: Function: f18 +// CHECK: Block: B14 (Entry), Pred: Succ: B13 + +// CHECK: Block: B13, Pred: B14, Succ: B12, B10 +// CHECK: Widened bounds before stmt: char p_Nt_checked[] = "a"; + +// CHECK: Widened bounds before stmt: char q_Nt_checked[] = "ab"; +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: char r_Nt_checked[] : count(0) = "ab"; +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) + +// CHECK: Widened bounds before stmt: char s_Nt_checked[] : count(1) = "ab"; +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) + + if (p[0]) { + a = 1; + if (p[1]) { + a = 2; + } + } + +// CHECK: Widened bounds before stmt: p[0] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B12, Pred: B13, Succ: B11, B10 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Widened bounds before stmt: p[1] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B11, Pred: B12, Succ: B10 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1 + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + + if (q[0]) { + a = 3; + if (q[1]) { + a = 4; + if (q[2]) { + a = 5; + } + } + } + +// CHECK: Block: B10, Pred: B11, B12, B13, Succ: B9, B6 +// CHECK: Widened bounds before stmt: q[0] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B9, Pred: B10, Succ: B8, B6 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Widened bounds before stmt: q[1] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B8, Pred: B9, Succ: B7, B6 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Widened bounds before stmt: q[2] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B7, Pred: B8, Succ: B6 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2 + 1) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + + if (r[0]) { + a = 6; + } + +// CHECK: Block: B6, Pred: B7, B8, B9, B10, Succ: B5, B4 +// CHECK: Widened bounds before stmt: r[0] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B4 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0 + 1) +// CHECK: s: bounds(s, s + 1) + + if (s[0]) { + a = 7; + if (s[1]) { + a = 8; + } + } + +// CHECK: Block: B4, Pred: B5, B6, Succ: B3, B1 +// CHECK: Widened bounds before stmt: s[0] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: a = 7 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Widened bounds before stmt: s[1] +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 8 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 2) +// CHECK: r: bounds(r, r + 0) +// CHECK: s: bounds(s, s + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, Succ: B0 } void f19() { _Nt_array_ptr p : count(0) = "a"; - if (*p) - if (*(p + 1)) - if (*(p + 3)) - if (*(p + 2)) - {} - -// CHECK: In function: f19 -// CHECK: [B6] -// CHECK: 2: *p -// CHECK: [B5] -// CHECK: 1: *(p + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B4] -// CHECK: 1: *(p + 3) -// CHECK: upper_bound(p) = 2 -// CHECK: [B3] -// CHECK: 1: *(p + 2) -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 3 + if (*p) { + a = 1; + if (*(p + 1)) { + a = 2; + if (*(p + 3)) { + a = 3; + if (*(p + 2)) { + a = 4; + } + } + } + } + +// CHECK: Function: f19 +// CHECK: Block: B7 (Entry), Pred: Succ: B6 + +// CHECK: Block: B6, Pred: B7, Succ: B5, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B5, Pred: B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Widened bounds before stmt: *(p + 3) +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B1, Pred: B2, B3, B4, B5, B6, Succ: B0 } -void f20() { - // Declared bounds and deref offset are both INT_MAX. Valid widening. - _Nt_array_ptr p : count(INT_MAX) = ""; // expected-error {{declared bounds for 'p' are invalid after initialization}} - if (*(p + INT_MAX)) - {} +void f20_1() { + // Pointer dereferenced at the upper bound. Valid bounds widening. + _Nt_array_ptr p : count(INT_MAX) = ""; // expected-error {{declared bounds for 'p' are invalid after initialization}} + if (*(p + INT_MAX)) { + a = 1; + } + +// CHECK: Function: f20_1 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count({{.*}}) = ""; +// CHECK: -// CHECK: In function: f20 -// CHECK: [B13] -// CHECK: 2: *(p + {{.*}}) -// CHECK: [B12] -// CHECK: upper_bound(p) = 1 +// CHECK: Widened bounds before stmt: *(p + {{.*}}) +// CHECK: p: bounds(p, p + {{.*}}) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + {{.*}} + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 +} - // Declared bounds and deref offset are both INT_MIN. Valid widening. +void f20_2() { + // Pointer dereferenced at the upper bound. Valid bounds widening. _Nt_array_ptr q : count(INT_MIN) = ""; - if (*(q + INT_MIN)) // expected-error {{out-of-bounds memory access}} - {} + if (*(q + INT_MIN)) { // expected-error {{out-of-bounds memory access}} + a = 2; + } + +// CHECK: Function: f20_2 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr q : count({{.*}}) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *(q + {{.*}}) +// CHECK: q: bounds(q, q + {{.*}}) -// CHECK: [B11] -// CHECK: 2: *(q + {{.*}}) -// CHECK: [B10] -// CHECK: upper_bound(q) = 1 +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: q: bounds(q, q + {{.*}} + 1) - // Declared bounds (INT_MIN) and deref offset (INT_MAX - 1). No sequential deref tests. No widening. +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 +} + +// See issue #780 for a test involving INT_MAX that does not work on an Windows +// X86 build. +void f20_3() { _Nt_array_ptr r : count(INT_MIN) = ""; - // TODO: Windows X86 Debug build fails to display the error "out-of-bounds - // memory access". This seems to happen only at *(p + INT_MAX). So for now, I - // have changed the dereference to *(p + INT_MAX - 1) to make this test pass. - // I have filed isue #780. This needs to be investigated and the test need to - // be changed to *(p + INT_MAX). - if (*(r + INT_MAX - 1)) // expected-error {{out-of-bounds memory access}} - {} - -// CHECK: [B9] -// CHECK: 2: *(r + {{.*}}) -// CHECK: [B8] -// CHECK-NOT: upper_bound(r) - - // Declared bounds and deref offset are both (INT_MAX + 1). Integer overflow. No widening. + if (*(r + INT_MAX - 1)) { // expected-error {{out-of-bounds memory access}} + a = 3; + } + +// CHECK: Function: f20_3 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr r : count({{.*}}) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *(r + {{.*}} - 1) +// CHECK: r: bounds(r, r + {{.*}}) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: r: bounds(r, r + {{.*}}) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 +} + +void f20_4() { + // Pointer dereferenced at the upper bound but integer overflow occurs. No + // bounds widening. _Nt_array_ptr s : count(INT_MAX + 1) = ""; - if (*(s + INT_MAX + 1)) // expected-error {{out-of-bounds memory access}} - {} + if (*(s + INT_MAX + 1)) { // expected-error {{out-of-bounds memory access}} + a = 4; + } + +// CHECK: Function: f20_4 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr s : count({{.*}} + 1) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *(s + {{.*}} + 1) +// CHECK: s: bounds(s, s + {{.*}} + 1) -// CHECK: [B7] -// CHECK: 2: *(s + {{.*}}) -// CHECK: [B6] -// CHECK-NOT: upper_bound(s) +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: s: bounds(s, s + {{.*}} + 1) - // Declared bounds and deref offset are both (INT_MIN + 1). Valid widening. +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 +} + +void f20_5() { + // Pointer dereferenced at the upper bound. Valid bounds widening. _Nt_array_ptr t : count(INT_MIN + 1) = ""; - if (*(t + INT_MIN + 1)) // expected-error {{out-of-bounds memory access}} - {} + if (*(t + INT_MIN + 1)) { // expected-error {{out-of-bounds memory access}} + a = 5; + } + +// CHECK: Function: f20_5 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr t : count({{.*}} + 1) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *(t + {{.*}} + 1) +// CHECK: t: bounds(t, t + {{.*}} + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: t: bounds(t, t + {{.*}} + 1 + 1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 +} + +void f20_6() { + // Pointer dereferenced at the upper bound but integer underflow occurs. No + // bounds widening. + _Nt_array_ptr u : count(INT_MIN + -1) = ""; // expected-error {{declared bounds for 'u' are invalid after initialization}} + if (*(u + INT_MIN + -1)) { + a = 6; + } + +// CHECK: Function: f20_6 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr u : count({{.*}} + -1) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *(u + {{.*}} + -1) +// CHECK: u: bounds(u, u + {{.*}} + -1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: u: bounds(u, u + {{.*}} + -1) + +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 +} + +void f21() { + char p _Nt_checked[] : count(0) = "abc"; + + while (p[0]) { + a = 1; + while (p[1]) { + a = 2; + while (p[2]) { + a = 3; + } + } + } + +// CHECK: Function: f21 +// CHECK: Block: B12 (Entry), Pred: Succ: B11 + +// CHECK: Block: B11, Pred: B12, Succ: B10 +// CHECK: Widened bounds before stmt: char p_Nt_checked[] : count(0) = "abc"; +// CHECK: + +// CHECK: Block: B10, Pred: B2, B11, Succ: B9, B1 +// CHECK: Widened bounds before stmt: p[0] +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B9, Pred: B10, Succ: B8 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0 + 1) + +// CHECK: Block: B8, Pred: B3, B9, Succ: B7, B2 +// CHECK: Widened bounds before stmt: p[1] +// CHECK: p: bounds(p, p + 0 + 1) + +// CHECK: Block: B7, Pred: B8, Succ: B6 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B6, Pred: B4, B7, Succ: B5, B3 +// CHECK: Widened bounds before stmt: p[2] +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B4 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B6 + +// CHECK: Block: B3, Pred: B6, Succ: B8 + +// CHECK: Block: B2, Pred: B8, Succ: B10 + +// CHECK: Block: B1, Pred: B10, Succ: B0 +} + +void f22() { + _Nt_array_ptr p : count(0) = "a"; + + if (*p) { + a = 1; + while (*(p + 1)) { + a = 2; + if (*(p + 2)) { + a = 3; + } + } + } + +// CHECK: Function: f22 +// CHECK: Block: B8 (Entry), Pred: Succ: B7 + +// CHECK: Block: B7, Pred: B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = "a"; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B7, Succ: B5 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B5, Pred: B2, B6, Succ: B4, B1 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B4, Pred: B5, Succ: B3, B2 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B2 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B2, Pred: B3, B4, Succ: B5 + +// CHECK: Block: B1, Pred: B5, B7, Succ: B0 +} + +void f23() { + _Nt_array_ptr p : count(0) = ""; + + goto B; + while (*p) { +B: a = 1; + while (*(p + 1)) { // expected-error {{out-of-bounds memory access}} + a = 2; + } + } + +// CHECK: Function: f23 +// CHECK: Block: B16 (Entry), Pred: Succ: B15 + +// CHECK: Block: B15, Pred: B16, Succ: B13 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Block: B14, Pred: B9, Succ: B13, B8 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B13, Pred: B14, B15, Succ: B12 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B12, Pred: B10, B13, Succ: B11, B9 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B11, Pred: B12, Succ: B10 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B10, Pred: B11, Succ: B12 + +// CHECK: Block: B9, Pred: B12, Succ: B14 + + while (*p) { + a = 3; + while (*(p + 1)) { // expected-error {{out-of-bounds memory access}} +C: a = 4; + } + } + goto C; + +// CHECK: Block: B8, Pred: B3, B14, Succ: B7, B2 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B7, Pred: B8, Succ: B6 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B6, Pred: B4, B7, Succ: B5, B3 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B5, Pred: B6, B2, Succ: B4 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B4, Pred: B5, Succ: B6 + +// CHECK: Block: B3, Pred: B6, Succ: B8 + +// CHECK: Block: B2, Pred: B8, Succ: B5 +} + +void f24() { + _Nt_array_ptr p : count(0) = ""; + + while (*p) { + p++; + while (*(p + 1)) { // expected-error {{out-of-bounds memory access}} + a = 1; + } + } + +// CHECK: Function: f24 +// CHECK: Block: B9 (Entry), Pred: Succ: B8 + +// CHECK: Block: B8, Pred: B9, Succ: B7 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Block: B7, Pred: B2, B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B7, Succ: B5 +// CHECK: Widened bounds before stmt: p++ +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B5, Pred: B3, B6, Succ: B4, B2 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B4, Pred: B5, Succ: B3 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B3, Pred: B4, Succ: B5 + +// CHECK: Block: B2, Pred: B5, Succ: B7 + +// CHECK: Block: B1, Pred: B7, Succ: B0 +} + +void f25_1() { + _Nt_array_ptr p : count(0) = ""; + + for (; *p; ) { + a = 1; + for (; *(p + 1); ) { + a = 2; + for (; *(p + 2); ) { + a = 3; + } + a = 4; + } + a = 5; + } + a = 6; + +// CHECK: Function: f25_1 +// CHECK: Block: B21 (Entry), Pred: Succ: B20 + +// CHECK: Block: B20, Pred: B21, Succ: B19 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Block: B19, Pred: B9, B20, Succ: B18, B8 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B18, Pred: B19, Succ: B17 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B17, Pred: B11, B18, Succ: B16, B10 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B16, Pred: B17, Succ: B15 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B15, Pred: B13, B16, Succ: B14, B12 +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B14, Pred: B15, Succ: B13 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B13, Pred: B14, Succ: B15 + +// CHECK: Block: B12, Pred: B15, Succ: B11 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B11, Pred: B12, Succ: B17 + +// CHECK: Block: B10, Pred: B17, Succ: B9 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B9, Pred: B10, Succ: B19 + +// CHECK: Block: B8, Pred: B19, Succ: B7 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 0) + + for (; *p; ) { + p++; + for (; *(p + 1); ) { // expected-error {{out-of-bounds memory access}} + a = 7; + } + } + +// CHECK: Block: B7, Pred: B2, B8, Succ: B6, B1 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B7, Succ: B5 +// CHECK: Widened bounds before stmt: p++ +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B5, Pred: B3, B6, Succ: B4, B2 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B4, Pred: B5, Succ: B3 +// CHECK: Widened bounds before stmt: a = 7 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B3, Pred: B4, Succ: B5 + +// CHECK: Block: B2, Pred: B5, Succ: B7 + +// CHECK: Block: B1, Pred: B7, Succ: B0 +} + +void f25_2() { + _Nt_array_ptr p : count(0) = ""; + + for (; *p; ) { +D: a = 1; + for (; *(p + 1); ) { // expected-error {{out-of-bounds memory access}} + a = 2; + } + } + goto D; + +// CHECK: Function: f25_2 +// CHECK: Block: B10 (Entry), Pred: Succ: B9 + +// CHECK: Block: B9, Pred: B10, Succ: B8 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Block: B8, Pred: B3, B9, Succ: B7, B2 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B7, Pred: B8, B2, Succ: B6 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B4, B7, Succ: B5, B3 +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B5, Pred: B6, Succ: B4 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 0) +} + +void f26() { + _Nt_array_ptr p : bounds(p, ((((((p + 1))))))) = "a"; + + if (*(((p + 1)))) { + a = 1; + } + +// CHECK: Function: f26 +// CHECK: Block: B4 (Entry), Pred: Succ: B3 + +// CHECK: Block: B3, Pred: B4, Succ: B2, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : bounds(p, ((((((p + 1))))))) = "a"; +// CHECK: -// CHECK: [B5] -// CHECK: 2: *(t + {{.*}}) -// CHECK: [B4] -// CHECK: upper_bound(t) = 1 +// CHECK: Widened bounds before stmt: *(((p + 1))) +// CHECK: p: bounds(p, ((((((p + 1))))))) - // Declared bounds and deref offset are both (INT_MIN + -1). Integer underflow. No widening. - _Nt_array_ptr u : count(INT_MIN + -1) = ""; // expected-error {{declared bounds for 'u' are invalid after initialization}} - if (*(u + INT_MIN + -1)) - {} +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1 + 1) -// CHECK: [B3] -// CHECK: 2: *(u + {{.*}}) -// CHECK: [B2] -// CHECK-NOT: upper_bound(u) +// CHECK: Block: B1, Pred: B2, B3, Succ: B0 } -void f21() { - char p _Nt_checked[] : count(0) = "abc"; +void f27(_Nt_array_ptr p : count(i), int i) { + if (*(p + i)) { + a == 1 ? i++ : i; // expected-error {{inferred bounds for 'p' are unknown after increment}} - while (p[0]) - while (p[1]) - while (p[2]) - {} - -// CHECK: In function: f21 -// CHECK: [B7] -// CHECK: 1: p[0] -// CHECK: [B6] -// CHECK: 1: p[1] -// CHECK: upper_bound(p) = 1 -// CHECK: [B5] -// CHECK: 1: p[2] -// CHECK: upper_bound(p) = 2 -// CHECK: [B4] -// CHECK: upper_bound(p) = 3 -// CHECK: [B3] -// CHECK: upper_bound(p) = 2 -// CHECK: [B2] -// CHECK: upper_bound(p) = 1 -} + if (*(p + i + 1)) { + a = 2; + } + } -void f22() { - _Nt_array_ptr p : count(0) = "a"; +// CHECK: Function: f27 +// CHECK: Block: B7 (Entry), Pred: Succ: B6 - if (*p) - while (*(p + 1)) - if (*(p + 2)) - {} - -// CHECK: In function: f22 -// CHECK: [B6] -// CHECK: 2: *p -// CHECK: [B5] -// CHECK: 1: *(p + 1) -// CHECK: upper_bound(p) = 1 -// CHECK: [B4] -// CHECK: 1: *(p + 2) -// CHECK: upper_bound(p) = 2 -// CHECK: [B3] -// CHECK: upper_bound(p) = 3 -// CHECK: [B2] -// CHECK: upper_bound(p) = 2 -} +// CHECK: Block: B6, Pred: B7, Succ: B5, B0 +// CHECK: Widened bounds before stmt: *(p + i) +// CHECK: p: bounds(p, p + i) -void f23() { - _Nt_array_ptr p : count(0) = ""; +// CHECK: Block: B5, Pred: B6, Succ: B3, B4 +// CHECK: Widened bounds before stmt: a == 1 +// CHECK: p: bounds(p, p + i + 1) -// CHECK: In function: f23 +// CHECK: Block: B4, Pred: B5, Succ: B2 +// CHECK: Widened bounds before stmt: i +// CHECK: p: bounds(p, p + i + 1) - goto B; - while (*p) { -B: p; - while (*(p + 1)) { // expected-error {{out-of-bounds memory access}} - p; - } - } +// CHECK: Block: B3, Pred: B5, Succ: B2 +// CHECK: Widened bounds before stmt: i++ +// CHECK: p: bounds(p, p + i + 1) -// CHECK: [B15] -// CHECK: T: goto B; -// CHECK: [B14] -// CHECK: 1: *p -// CHECK: T: while -// CHECK: [B13] -// CHECK: B: -// CHECK: 1: p -// CHECK-NOT: upper_bound(p) -// CHECK: [B12] -// CHECK: 1: *(p + 1) -// CHECK: T: while -// CHECK: [B11] -// CHECK: 1: p -// CHECK-NOT: upper_bound(p) +// CHECK: Block: B2, Pred: B3, B4, Succ: B1, B0 +// CHECK: Widened bounds before stmt: a == 1 ? i++ : i +// CHECK: p: bounds(p, p + i + 1) - while (*p) { - p; - while (*(p + 1)) { -C: p; - } - } - goto C; +// CHECK: Widened bounds before stmt: *(p + i + 1) +// CHECK: p: bounds(p, p + i) -// CHECK: [B8] -// CHECK: 1: *p -// CHECK: T: while -// CHECK: [B7] -// CHECK: 1: p -// CHECK: upper_bound(p) = 1 -// CHECK: [B6] -// CHECK: 1: *(p + 1) -// CHECK: T: while -// CHECK-NOT: upper_bound(p) -// CHECK: [B5] -// CHECK: C: -// CHECK: 1: p -// CHECK-NOT: upper_bound(p) -// CHECK: [B3] -// CHECK-NOT: upper_bound(p) -// CHECK: [B2] -// CHECK: T: goto C; +// CHECK: Block: B1, Pred: B2, Succ: B0 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + i) } -void f24() { +void f28() { _Nt_array_ptr p : count(0) = ""; - while (*p) { - p++; - while (*(p+1)) { // expected-error {{out-of-bounds memory access}} - p; - } + switch (*p) { + default: a = 0; break; + case 'a': a = 1; break; + case 'b': a = 2; break; } -// CHECK: In function: f24 -// CHECK: [B7] -// CHECK: 1: *p -// CHECK: T: while -// CHECK: [B6] -// CHECK: 1: p++ -// CHECK: upper_bound(p) = 1 -// CHECK: [B5] -// CHECK: 1: *(p + 1) -// CHECK: T: while -// CHECK: [B4] -// CHECK: 1: p -// CHECK-NOT: upper_bound(p) -} - -void f25() { - _Nt_array_ptr p : count(0) = ""; - int i; +// CHECK: Function: f28 +// CHECK: Block: B22 (Entry), Pred: Succ: B18 -// CHECK: In function: f25 +// CHECK: Block: B21, Pred: B18, Succ: B14 +// CHECK: Widened bounds before stmt: a = 0 +// CHECK: p: bounds(p, p + 0) - for (; *p; ) { - i = 0; - for (; *(p + 1); ) { - i = 1; - for (; *(p + 2); ) { - i = 2; - } - } - } +// CHECK: Block: B20, Pred: B18, Succ: B14 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) -// CHECK: [B23] -// CHECK: 1: *p -// CHECK: T: for -// CHECK: [B22] -// CHECK: 1: i = 0 -// CHECK: upper_bound(p) = 1 -// CHECK: [B21] -// CHECK: 1: *(p + 1) -// CHECK: T: for -// CHECK: upper_bound(p) = 1 -// CHECK: [B20] -// CHECK: 1: i = 1 -// CHECK: upper_bound(p) = 2 -// CHECK: [B19] -// CHECK: 1: *(p + 2) -// CHECK: T: for -// CHECK: upper_bound(p) = 2 -// CHECK: [B18] -// CHECK: 1: i = 2 -// CHECK: upper_bound(p) = 3 -// CHECK: [B17] -// CHECK: upper_bound(p) = 3 -// CHECK: [B16] -// CHECK: upper_bound(p) = 2 -// CHECK: [B15] -// CHECK: upper_bound(p) = 1 +// CHECK: Block: B19, Pred: B18, Succ: B14 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1) - for (; *p; ) { -D: p; - for (; *(p + 1); ) { // expected-error {{out-of-bounds memory access}} - p; - } + switch (*p) { + case 'a': a = 3; + default: a = 4; + case 'b': a = 5; break; } - goto D; -// CHECK: [B14] -// CHECK: 1: *p -// CHECK: T: for -// CHECK: [B13] -// CHECK: D: -// CHECK: 1: p -// CHECK-NOT: upper_bound(p) -// CHECK: [B12] -// CHECK: 1: *(p + 1) -// CHECK: T: for -// CHECK: [B11] -// CHECK: 1: p -// CHECK-NOT: upper_bound(p) -// CHECK: [B8] -// CHECK: T: goto D; +// CHECK: Block: B18, Pred: B22, Succ: B19, B20, B21 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: - for (; *p; ) { - p++; - for (; *(p + 1); ) { - p; - } - } +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) -// CHECK: [B7] -// CHECK: 1: *p -// CHECK: T: for -// CHECK: [B6] -// CHECK: 1: p++ -// CHECK: upper_bound(p) = 1 -// CHECK: [B5] -// CHECK: 1: *(p + 1) -// CHECK: T: for -// CHECK: [B4] -// CHECK: 1: p -// CHECK-NOT: upper_bound(p) -} +// CHECK: Block: B17, Pred: B14, Succ: B16 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 1) -void f26() { - _Nt_array_ptr p : count(0) = ""; +// CHECK: Block: B16, Pred: B17, B14, Succ: B15 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 0) -// CHECK: In function: f26 +// CHECK: Block: B15, Pred: B14, B16, Succ: B10 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 0) switch (*p) { - default: break; -// CHECK: default: -// CHECK-NOT: upper_bound(p) - case 'a': break; -// CHECK: case 'a': -// CHECK: upper_bound(p) = 1 - case 'b': break; -// CHECK: case 'b': -// CHECK: upper_bound(p) = 1 + case 'a': a = 6; break; + default: a = 7; + case 'b': a = 8; break; } - switch (*p) { - case 'a': -// CHECK: case 'a': -// CHECK: upper_bound(p) = 1 - default: -// CHECK: default: -// CHECK-NOT: upper_bound(p) - case 'b': break; -// CHECK: case 'b': -// CHECK-NOT: upper_bound(p) - } +// CHECK: Block: B14, Pred: B19, B20, B21, Succ: B15, B17, B16 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) - switch (*p) { - case 'a': break; -// CHECK: case 'a': -// CHECK: upper_bound(p) = 1 - default: -// CHECK: default: -// CHECK-NOT: upper_bound(p) - case 'b': break; -// CHECK: case 'b': -// CHECK-NOT: upper_bound(p) - } +// CHECK: Block: B13, Pred: B10, Succ: B6 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B12, Pred: B10, Succ: B11 +// CHECK: Widened bounds before stmt: a = 7 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B11, Pred: B10, B12, Succ: B6 +// CHECK: Widened bounds before stmt: a = 8 +// CHECK: p: bounds(p, p + 0) switch (*p) { - default: break; -// CHECK: default: -// CHECK: upper_bound(p) = 1 - case '\0': break; -// CHECK: case '\x00': -// CHECK-NOT: upper_bound(p) - case 'a': break; -// CHECK: case 'a': -// CHECK: upper_bound(p) = 1 + default: a = 9; break; + case '\0': a = 10; break; + case 'a': a = 11; break; } +// CHECK: Block: B10, Pred: B15, Succ: B11, B13, B12 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B9, Pred: B6, Succ: B2 +// CHECK: Widened bounds before stmt: a = 9 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B8, Pred: B6, Succ: B2 +// CHECK: Widened bounds before stmt: a = 10 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B7, Pred: B6, Succ: B2 +// CHECK: Widened bounds before stmt: a = 11 +// CHECK: p: bounds(p, p + 1) + switch (*p) { - case '\0': -// CHECK: case 'a': -// CHECK-NOT: upper_bound(p) - case 'a': break; -// CHECK: case '\x00': -// CHECK-NOT: upper_bound(p) - default: break; -// CHECK: default: -// CHECK: upper_bound(p) = 1 + case '\0': a = 12; + case 'a': a = 13; break; + default: a = 14; break; } + +// CHECK: Block: B6, Pred: B11, B13, Succ: B7, B8, B9 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B5, Pred: B2, Succ: B4 +// CHECK: Widened bounds before stmt: a = 12 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B4, Pred: B2, B5, Succ: B1 +// CHECK: Widened bounds before stmt: a = 13 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B3, Pred: B2, Succ: B1 +// CHECK: Widened bounds before stmt: a = 14 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B2, Pred: B7, B8, B9, Succ: B4, B5, B3 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B1, Pred: B3, B4, Succ: B0 } -void f27() { +void f29() { _Nt_array_ptr p : count(0) = ""; - const char a = '\0'; - const int b = '0'; - const char c = '1'; - const int d = '2'; - -// CHECK: In function: f27 + const char c1 = '\0'; + const int c2 = '0'; + const char c3 = '1'; + const int c4 = '2'; switch (*p) { - default: break; -// CHECK: default: -// CHECK: upper_bound(p) = 1 + default: a = 1; break; - case a: break; -// CHECK: case a: -// CHECK-NOT: upper_bound(p) + case c1: a = 2; break; - case b: break; -// CHECK: case b: -// CHECK: upper_bound(p) = 1 + case c2: a = 3; break; - case c: break; -// CHECK: case c: -// CHECK: upper_bound(p) = 1 + case c3: a = 4; break; - case d: break; -// CHECK: case d: -// CHECK: upper_bound(p) = 1 + case c4: a = 5; break; } +// CHECK: Function: f29 +// CHECK: Block: B12 (Entry), Pred: Succ: B6 +// CHECK: Block: B11, Pred: B6, Succ: B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B10, Pred: B6, Succ: B2 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B9, Pred: B6, Succ: B2 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B8, Pred: B6, Succ: B2 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B7, Pred: B6, Succ: B2 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 1) + enum {e1, e2}; switch (*p) { - case e1: break; -// CHECK: case e1: -// CHECK-NOT: upper_bound(p) + case e1: a = 6; break; - case e2: break; -// CHECK: case e2: -// CHECK: upper_bound(p) = 1 + case e2: a = 7; break; - default: break; -// CHECK: default: -// CHECK: upper_bound(p) = 1 + default: a = 8; break; } + +// CHECK: Block: B5, Pred: B2, Succ: B1 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B4, Pred: B2, Succ: B1 +// CHECK: Widened bounds before stmt: a = 7 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B3, Pred: B2, Succ: B1 +// CHECK: Widened bounds before stmt: a = 8 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B2, Pred: B7, B8, B9, B10, B11, Succ: B4, B5, B3 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B1, Pred: B3, B4, B5, Succ: B0 } -void f28() { +void f30() { _Nt_array_ptr p : count(0) = ""; - int a; - -// CHECK: In function: f28 switch (*p) { - default: break; -// CHECK: default: -// CHECK: upper_bound(p) = 1 + default: a = 1; break; - case 0: + case 0: a = 2; switch (*p) { - default: break; -// CHECK: default: -// CHECK-NOT: upper_bound(p) - - case 1: break; + default: a = 3; break; + case 1: a = 4; break; } -// CHECK: [B12] -// CHECK: case 1: -// CHECK: upper_bound(p) = 1 -// CHECK: [B11] -// CHECK: case 0: -// CHECK-NOT: upper_bound(p) } +// CHECK: Function: f30 +// CHECK: Block: B15 (Entry), Pred: Succ: B10 + +// CHECK: Block: B14, Pred: B10, Succ: B7 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B13, Pred: B11, Succ: B7 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B12, Pred: B11, Succ: B7 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B11, Pred: B10, Succ: B12, B13 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B10, Pred: B15, Succ: B11, B14 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + switch (*p) { - case 1: + case 1: a = 5; switch (*(p + 1)) { - case 2: break; + case 2: a = 6; break; } -// CHECK: [B9] -// CHECK: case 2: -// CHECK: T: break; -// CHECK: upper_bound(p) = 2 -// CHECK: [B8] -// CHECK: case 1: -// CHECK: 1: *(p + 1) -// CHECK: T: switch -// CHECK: upper_bound(p) = 1 } +// CHECK: Block: B9, Pred: B8, Succ: B2 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B8, Pred: B7, Succ: B9, B2 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B7, Pred: B12, B13, B14, Succ: B8, B2 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + switch (*p) { - case 1: do { a; - } while (a); - } -// CHECK: [B6] -// CHECK: case 1: -// CHECK: upper_bound(p) = 1 -// CHECK: [B5] -// CHECK: upper_bound(p) = 1 -// CHECK: [B4] -// CHECK: 1: a -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: a -// CHECK: T: do ... while -// CHECK: upper_bound(p) = 1 + case 1: a = 7; do { a = 8; + } while (a > 0); + } + +// CHECK: Block: B6, Pred: B2, Succ: B4 +// CHECK: Widened bounds before stmt: a = 7 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B5, Pred: B3, Succ: B4 + +// CHECK: Block: B4, Pred: B5, B6, Succ: B3 +// CHECK: Widened bounds before stmt: a = 8 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B3, Pred: B4, Succ: B5, B1 +// CHECK: Widened bounds before stmt: a > 0 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B2, Pred: B9, B8, B7, Succ: B6, B1 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B1, Pred: B3, B2, Succ: B0 } -void f29() { +void f31() { _Nt_array_ptr p : count(0) = ""; - int i; - -// CHECK: In function: f29 switch (*p) { case 'a': -// CHECK: case 'a': -// CHECK: upper_bound(p) = 1 + a = 1; if (*(p + 1)) { - i = 0; -// CHECK: 1: i = 0 -// CHECK: upper_bound(p) = 2 + a = 2; for (;*(p + 2);) { - i = 1; -// CHECK: 1: i = 1 -// CHECK: upper_bound(p) = 3 + a = 3; while (*(p + 3)) { - i = 2; -// CHECK: 1: i = 2 -// CHECK: upper_bound(p) = 4 + a = 4; } - - i = 3; -// CHECK: 1: i = 3 -// CHECK: upper_bound(p) = 3 + a = 5; } - - i = 4; -// CHECK: 1: i = 4 -// CHECK: upper_bound(p) = 2 + a = 6; } - - i = 5; -// CHECK: 1: i = 5 -// CHECK: upper_bound(p) = 1 + a = 7; break; } -} + a = 8; -void f30() { -// CHECK: In function: f30 +// CHECK: Function: f31 +// CHECK: Block: B14 (Entry), Pred: Succ: B2 + +// CHECK: Block: B13, Pred: B2, Succ: B12, B3 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) +// CHECK: Block: B12, Pred: B13, Succ: B11 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B11, Pred: B5, B12, Succ: B10, B4 +// CHECK: Widened bounds before stmt: *(p + 2) +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B10, Pred: B11, Succ: B9 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B9, Pred: B7, B10, Succ: B8, B6 +// CHECK: Widened bounds before stmt: *(p + 3) +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B8, Pred: B9, Succ: B7 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 3 + 1) + +// CHECK: Block: B7, Pred: B8, Succ: B9 + +// CHECK: Block: B6, Pred: B9, Succ: B5 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 2 + 1) + +// CHECK: Block: B5, Pred: B6, Succ: B11 + +// CHECK: Block: B4, Pred: B11, Succ: B3 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B3, Pred: B4, B13, Succ: B1 +// CHECK: Widened bounds before stmt: a = 7 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B2, Pred: B14, Succ: B13, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B1, Pred: B3, B2, Succ: B0 +// CHECK: Widened bounds before stmt: a = 8 +// CHECK: p: bounds(p, p + 0) +} + +void f32() { _Nt_array_ptr p : count(0) = ""; const int i = -1; const int j = 1; switch (*p) { - case i ... j: break; -// CHECK: case i ... j: -// CHECK-NOT: upper_bound(p) + case i ... j: a = 1; break; } +// CHECK: Function: f32 +// CHECK: Block: B14 (Entry), Pred: Succ: B12 + +// CHECK: Block: B13, Pred: B12, Succ: B8 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0) + switch (*p) { - case 1 ... -1: break; -// CHECK: case 1 ... -1: -// CHECK-NOT: upper_bound(p) + case 1 ... -1: a = 2; break; - case 1 ... 0: break; -// CHECK: case 1 ... 0: -// CHECK-NOT: upper_bound(p) + case 1 ... 0: a = 3; break; - case -2 ... -1: break; -// CHECK: case -2 ... -1: -// CHECK: upper_bound(p) = 1 + case -2 ... -1: a = 4; break; } +// CHECK: Block: B11, Pred: B8, Succ: B6 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B10, Pred: B8, Succ: B6 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B9, Pred: B8, Succ: B6 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 1) + switch (*p) { - case -1 ... 1: break; -// CHECK: case -1 ... 1: -// CHECK-NOT: upper_bound(p) + case -1 ... 1: a = 5; break; } +// CHECK: Block: B7, Pred: B6, Succ: B4 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 0) + switch (*p) { - case 0 ... 1: break; -// CHECK: case 0 ... 1: -// CHECK-NOT: upper_bound(p) + case 0 ... 1: a = 6; break; } +// CHECK: Block: B5, Pred: B4, Succ: B2 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 0) + switch (*p) { - case 1 ... 2: break; -// CHECK: case 1 ... 2: -// CHECK: upper_bound(p) = 1 + case 1 ... 2: a = 7; break; } -} -void f31() { -// CHECK: In function: f31 +// CHECK: Block: B3, Pred: B2, Succ: B1 +// CHECK: Widened bounds before stmt: a = 7 +// CHECK: p: bounds(p, p + 1) +} +void f34() { _Nt_array_ptr p : count(0) = ""; const int i = 'abc'; const char c = 'xyz'; const unsigned u = UINT_MAX; switch (*p) { - case 999999999999999999999999999: break; // expected-error {{integer literal is too large to be represented in any integer type}} -// CHECK: case {{.*}} -// CHECK: upper_bound(p) = 1 + case 999999999999999999999999999: a = 1; break; // expected-error {{integer literal is too large to be represented in any integer type}} + + case 00000000000000000000000000000000000: a = 2; break; + case 00000000000000000000000000000000001: a = 3; break; + case '00000000000000000000000000000000000': a = 4; break; - case 00000000000000000000000000000000000: break; -// CHECK: case 0: -// CHECK-NOT: upper_bound(p) + case i: a = 5; break; + case c: a = 6; break; + case u: a = 7; break; + } - case 00000000000000000000000000000000001: break; -// CHECK: case 1: -// CHECK: upper_bound(p) = 1 +// CHECK: Function: f34 +// CHECK: Block: B25 (Entry), Pred: Succ: B17 +// CHECK: Block: B24, Pred: B17, Succ: B11 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) - case '00000000000000000000000000000000000': break; -// CHECK: case {{.*}} -// CHECK: upper_bound(p) = 1 +// CHECK: Block: B23, Pred: B17, Succ: B11 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 0) - case i: break; -// CHECK: case i: -// CHECK: upper_bound(p) = 1 +// CHECK: Block: B22, Pred: B17, Succ: B11 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 1) - case c: break; -// CHECK: case c: -// CHECK: upper_bound(p) = 1 +// CHECK: Block: B21, Pred: B17, Succ: B11 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 1) - case u: break; -// CHECK: case u: -// CHECK: upper_bound(p) = 1 - } +// CHECK: Block: B20, Pred: B17, Succ: B11 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B19, Pred: B17, Succ: B11 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B18, Pred: B17, Succ: B11 +// CHECK: Widened bounds before stmt: a = 7 +// CHECK: p: bounds(p, p + 1) switch (*p) { - case INT_MAX: break; -// CHECK: case {{.*}} -// CHECK: upper_bound(p) = 1 + case INT_MAX: a = 8; break; + case INT_MIN: a = 9; break; + case INT_MAX + INT_MAX: a = 10; break; + case INT_MAX - INT_MAX: a = 11; break; + case INT_MAX + INT_MIN: a = 12; break; + } - case INT_MIN: break; -// CHECK: case {{.*}} -// CHECK: upper_bound(p) = 1 +// CHECK: Block: B16, Pred: B11, Succ: B8 +// CHECK: Widened bounds before stmt: a = 8 +// CHECK: p: bounds(p, p + 1) - case INT_MAX + INT_MAX: break; -// CHECK: case {{.*}} -// CHECK: upper_bound(p) = 1 +// CHECK: Block: B15, Pred: B11, Succ: B8 +// CHECK: Widened bounds before stmt: a = 9 +// CHECK: p: bounds(p, p + 1) - case INT_MAX - INT_MAX: break; -// CHECK: case {{.*}} -// CHECK-NOT: upper_bound(p) +// CHECK: Block: B14, Pred: B11, Succ: B8 +// CHECK: Widened bounds before stmt: a = 10 +// CHECK: p: bounds(p, p + 1) - case INT_MAX + INT_MIN: break; -// CHECK: case {{.*}} -// CHECK: upper_bound(p) = 1 - } +// CHECK: Block: B13, Pred: B11, Succ: B8 +// CHECK: Widened bounds before stmt: a = 11 +// CHECK: p: bounds(p, p + 0) - switch (*p) { - case INT_MIN - INT_MIN: break; -// CHECK: case {{.*}} -// CHECK-NOT: upper_bound(p) +// CHECK: Block: B12, Pred: B11, Succ: B8 +// CHECK: Widened bounds before stmt: a = 12 +// CHECK: p: bounds(p, p + 1) - case INT_MAX - INT_MIN: break; -// CHECK: case {{.*}} -// CHECK: upper_bound(p) = 1 + switch (*p) { + case INT_MIN - INT_MIN: a = 13; break; + case INT_MAX - INT_MIN: a = 14; break; } +// CHECK: Block: B10, Pred: B8, Succ: B5 +// CHECK: Widened bounds before stmt: a = 13 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B9, Pred: B8, Succ: B5 +// CHECK: Widened bounds before stmt: a = 14 +// CHECK: p: bounds(p, p + 1) + switch (*p) { // Note: This does not widen the bounds as the value of the expression is // computed to 0 and we have the warning: overflow in expression; result is 0 // with type 'int'. - case INT_MIN + INT_MIN: break; -// CHECK: case {{.*}} -// CHECK-NOT: upper_bound(p) + case INT_MIN + INT_MIN: a = 15; break; - case UINT_MAX: break; -// CHECK: case {{.*}} -// CHECK: upper_bound(p) = 1 + case UINT_MAX: a = 16; break; } +// CHECK: Block: B7, Pred: B5, Succ: B2 +// CHECK: Widened bounds before stmt: a = 15 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B5, Succ: B2 +// CHECK: Widened bounds before stmt: a = 16 +// CHECK: p: bounds(p, p + 1) + _Nt_array_ptr q : count(0) = 0; const uint64_t x = 0x0000444400004444LL; switch (*p) { - case ULLONG_MAX: break; -// CHECK: case {{.*}} -// CHECK: upper_bound(p) = 1 - - case x: break; -// CHECK: case x: -// CHECK: upper_bound(p) = 1 + case ULLONG_MAX: a = 17; break; + case x: a = 18; break; } -} -void f32() { -// CHECK: In function: f32 +// CHECK: Block: B4, Pred: B2, Succ: B1 +// CHECK: Widened bounds before stmt: a = 17 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 0) + +// CHECK: Block: B3, Pred: B2, Succ: B1 +// CHECK: Widened bounds before stmt: a = 18 +// CHECK: p: bounds(p, p + 1) +// CHECK: q: bounds(q, q + 0) +} +void f34_1() { _Nt_array_ptr p : count(0) = ""; switch(*p) { default: f1(); break; case 0: { + a = 1; + switch(*p) { default: f2(); break; - case 'a': break; + case 'a': a = 2; break; } break; } } -// CHECK: default: -// CHECK: 1: f1() -// CHECK: upper_bound(p) = 1 -// CHECK: default: -// CHECK: 1: f2() -// CHECK-NOT: upper_bound(p) -// CHECK: case 'a': -// CHECK: upper_bound(p) = 1 -// CHECK: case 0: -// CHECK-NOT: upper_bound(p) +// CHECK: Function: f34_1 +// CHECK: Block: B8 (Entry), Pred: Succ: B2 + +// CHECK: Block: B7, Pred: B2, Succ: B1 +// CHECK: Widened bounds before stmt: f1() +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B6, Pred: B4, Succ: B3 +// CHECK: Widened bounds before stmt: f2() +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B5, Pred: B4, Succ: B3 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B4, Pred: B2, Succ: B5, B6 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B3, Pred: B5, B6, Succ: B1 + +// CHECK: Block: B2, Pred: B8, Succ: B4, B7 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B1, Pred: B3, B7, Succ: B0 +} + +void f34_2() { + _Nt_array_ptr p : count(0) = ""; switch(*p) { default: f1(); break; case 0: { + a = 3; + switch(*p) { default: f2(); break; - case '\0': break; + case '\0': a = 4; break; } break; } } -// CHECK: default: -// CHECK: 1: f1() -// CHECK: upper_bound(p) = 1 -// CHECK: default: -// CHECK: 1: f2() -// CHECK: upper_bound(p) = 1 -// CHECK: case '\x00': -// CHECK-NOT: upper_bound(p) -// CHECK: case 0: -// CHECK-NOT: upper_bound(p) +// CHECK: Function: f34_2 +// CHECK: Block: B8 (Entry), Pred: Succ: B2 + +// CHECK: Block: B7, Pred: B2, Succ: B1 +// CHECK: Widened bounds before stmt: f1() +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B6, Pred: B4, Succ: B3 +// CHECK: Widened bounds before stmt: f2() +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B5, Pred: B4, Succ: B3 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B4, Pred: B2, Succ: B5, B6 +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B3, Pred: B5, B6, Succ: B1 + +// CHECK: Block: B2, Pred: B8, Succ: B4, B7 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B1, Pred: B3, B7, Succ: B0 +} + +void f34_3() { + _Nt_array_ptr p : count(0) = ""; switch(*p) { default: f1(); break; case 'b': { - switch(*(p+1)) { + a = 5; + + switch(*(p + 1)) { default: f2(); break; - case 'a': break; + case 'a': a = 6; break; } break; } } -// CHECK: default: -// CHECK: 1: f1() -// CHECK-NOT: upper_bound(p) -// CHECK: default: -// CHECK: 1: f2() -// CHECK: upper_bound(p) = 1 -// CHECK: case 'a': -// CHECK: upper_bound(p) = 2 -// CHECK: case 'b': -// CHECK: upper_bound(p) = 1 +// CHECK: Function: f34_3 +// CHECK: Block: B8 (Entry), Pred: Succ: B2 + +// CHECK: Block: B7, Pred: B2, Succ: B1 +// CHECK: Widened bounds before stmt: f1() +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B4, Succ: B3 +// CHECK: Widened bounds before stmt: f2() +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B5, Pred: B4, Succ: B3 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B4, Pred: B2, Succ: B5, B6 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B3, Pred: B5, B6, Succ: B1 + +// CHECK: Block: B2, Pred: B8, Succ: B4, B7 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B1, Pred: B3, B7, Succ: B0 +} + +void f34_4() { + _Nt_array_ptr p : count(0) = ""; switch(*p) { default: f1(); break; case 'b': { - switch(*(p+1)) { + a = 7; + + switch(*(p + 1)) { default: f2(); break; - case '\0': break; + case '\0': a = 8; break; } break; } } -// CHECK: default: -// CHECK: 1: f1() -// CHECK-NOT: upper_bound(p) -// CHECK: default: -// CHECK: 1: f2() -// CHECK: upper_bound(p) = 2 -// CHECK: case '\x00': -// CHECK: upper_bound(p) = 1 -// CHECK: case 'b': -// CHECK: upper_bound(p) = 1 +// CHECK: Function: f34_4 +// CHECK: Block: B8 (Entry), Pred: Succ: B2 + +// CHECK: Block: B7, Pred: B2, Succ: B1 +// CHECK: Widened bounds before stmt: f1() +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B4, Succ: B3 +// CHECK: Widened bounds before stmt: f2() +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B5, Pred: B4, Succ: B3 +// CHECK: Widened bounds before stmt: a = 8 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B4, Pred: B2, Succ: B5, B6 +// CHECK: Widened bounds before stmt: a = 7 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B3, Pred: B5, B6, Succ: B1 + +// CHECK: Block: B2, Pred: B8, Succ: B4, B7 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B1, Pred: B3, B7, Succ: B0 +} + +void f34_5() { + _Nt_array_ptr p : count(0) = ""; switch(*p) { default: { - switch(*(p+1)) { + a = 9; + + switch(*(p + 1)) { default: f2(); break; - case '\0': break; + case '\0': a = 10; break; } break; } - case 0: break; - } - -// CHECK: default: -// CHECK: 1: f2() -// CHECK: upper_bound(p) = 2 -// CHECK: case '\x00': -// CHECK: upper_bound(p) = 1 -// CHECK: default: -// CHECK: upper_bound(p) = 1 -// CHECK: case 0: -// CHECK-NOT: upper_bound(p) -} + case 0: a = 11; break; + } -void f33() { - _Nt_array_ptr p : bounds(p, ((((((p + 1))))))) = "a"; +// CHECK: Function: f34_5 +// CHECK: Block: B8 (Entry), Pred: Succ: B2 + +// CHECK: Block: B7, Pred: B5, Succ: B4 +// CHECK: Widened bounds before stmt: f2() +// CHECK: p: bounds(p, p + 1 + 1) + +// CHECK: Block: B6, Pred: B5, Succ: B4 +// CHECK: Widened bounds before stmt: a = 10 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B5, Pred: B2, Succ: B6, B7 +// CHECK: Widened bounds before stmt: a = 9 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: *(p + 1) +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B4, Pred: B6, B7, Succ: B1 - if (*(((p + 1)))) {} +// CHECK: Block: B3, Pred: B2, Succ: B1 +// CHECK: Widened bounds before stmt: a = 11 +// CHECK: p: bounds(p, p + 0) -// CHECK: In function: f33 -// CHECK: [B3] -// CHECK: 2: *(((p + 1))) -// CHECK: [B2] -// CHECK: upper_bound(p) = 1 +// CHECK: Block: B2, Pred: B8, Succ: B3, B5 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B1, Pred: B3, B4, Succ: B0 } -void f34(_Nt_array_ptr p : count(i), int i, int flag) { - if (*(p + i)) { - flag ? i++ : i; // expected-error {{inferred bounds for 'p' are unknown after increment}} - - if (*(p + i + 1)) - {} - } - -// CHECK: In function: f34 -// CHECK: [B6] -// CHECK: 1: *(p + i) -// CHECK: [B5] -// CHECK: 1: flag -// CHECK: upper_bound(p) = 1 -// CHECK: [B4] -// CHECK: 1: i -// CHECK: upper_bound(p) = 1 -// CHECK: [B3] -// CHECK: 1: i++ -// CHECK: upper_bound(p) = 1 -// CHECK: [B2] -// CHECK: 2: *(p + i + 1) -// CHECK: upper_bound(p) = 1 +void f35() { + _Nt_array_ptr p : count(0) = ""; + + if (*p) { + a = 1; +A: a = 2; + a = 3; + } + +// CHECK: Function: f35 +// CHECK: Block: B5 (Entry), Pred: Succ: B4 + +// CHECK: Block: B4, Pred: B5, Succ: B3, B1 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B3, Pred: B4, Succ: B2 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B2, Pred: B3, Succ: B1 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B1, Pred: B2, B4, Succ: B0 } -void f35(void) { - int i, j; - for (;;) { - i = 1; - j = 2; - _Nt_array_ptr p : count(i + j) = 0; - if (p[i + j]) { - return; +void f36() { + _Nt_array_ptr p : count(0) = ""; + + if (a > 0) { + a = 1; +A: a = 2; + a = 3; + + } else if (a == 0) { + a = 4; + + while (a != 0) { + ++a; + + switch(a) { + default: a = 5; break; + case 1: a = 6; break; + } } } -// CHECK: In function: f35 -// CHECK: [B7] -// CHECK: 1: int i; -// CHECK: 2: int j; -// CHECK: [B6] -// CHECK: T: for (; ; ) -// CHECK: [B5] -// CHECK: 1: i = 1 -// CHECK: 2: j = 2 -// CHECK: 3: _Nt_array_ptr p : count(i + j) = 0; -// CHECK: 4: p[i + j] (ImplicitCastExpr, LValueToRValue, char) -// CHECK: T: if [B5.4] -// CHECK: [B4] -// CHECK: 1: return; -// CHECK: upper_bound(p) = 1 + if (*p) { + a = 7; + } + + goto A; + +// CHECK: Function: f36 +// CHECK: Block: B15 (Entry), Pred: Succ: B14 + +// CHECK: Block: B14, Pred: B15, Succ: B13, B11 +// CHECK: Widened bounds before stmt: _Nt_array_ptr p : count(0) = ""; +// CHECK: + +// CHECK: Widened bounds before stmt: a > 0 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B13, Pred: B14, Succ: B12 +// CHECK: Widened bounds before stmt: a = 1 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B12, Pred: B13, B2, Succ: B4 +// CHECK: Widened bounds before stmt: a = 2 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: a = 3 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B11, Pred: B14, Succ: B10, B4 +// CHECK: Widened bounds before stmt: a == 0 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B10, Pred: B11, Succ: B9 +// CHECK: Widened bounds before stmt: a = 4 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B9, Pred: B5, B10, Succ: B6, B4 +// CHECK: Widened bounds before stmt: a != 0 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B8, Pred: B6, Succ: B5 +// CHECK: Widened bounds before stmt: a = 5 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B7, Pred: B6, Succ: B5 +// CHECK: Widened bounds before stmt: a = 6 +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B6, Pred: B9, Succ: B7, B8 +// CHECK: Widened bounds before stmt: ++a +// CHECK: p: bounds(p, p + 0) + +// CHECK: Widened bounds before stmt: a +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B5, Pred: B7, B8, Succ: B9 + +// CHECK: Block: B4, Pred: B9, B11, B12, Succ: B3, B2 +// CHECK: Widened bounds before stmt: *p +// CHECK: p: bounds(p, p + 0) + +// CHECK: Block: B3, Pred: B4, Succ: B2 +// CHECK: Widened bounds before stmt: a = 7 +// CHECK: p: bounds(p, p + 1) + +// CHECK: Block: B2, Pred: B3, B4, Succ: B12 } diff --git a/clang/test/CheckedC/parsing/valid-where-clause.c b/clang/test/CheckedC/parsing/valid-where-clause.c index e516b495858d..b1d6b8713696 100644 --- a/clang/test/CheckedC/parsing/valid-where-clause.c +++ b/clang/test/CheckedC/parsing/valid-where-clause.c @@ -1,4 +1,7 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify \ +// RUN: -verify-ignore-unexpected=note \ +// RUN: -verify-ignore-unexpected=warning \ +// RUN: -verify-ignore-unexpected=error %s // Test valid where clause cases.