diff --git a/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h b/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h new file mode 100644 index 0000000000000000000000000000000000000000..d296706afa9b7f7a6520c73cc1529fbf0dee2a4a --- /dev/null +++ b/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h @@ -0,0 +1,222 @@ +//===- BSCBorrowCheck.h - Borrow Check for Source CFGs -*- BSC --*-------// +// +// 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 BSC borrow check for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_BSCBORROWCHECK_H +#define LLVM_CLANG_ANALYSIS_ANALYSES_BSCBORROWCHECK_H + +#if ENABLE_BSC +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/DenseMap.h" +#include +#include + +namespace clang { +enum BorrowKind : char { + NoBorrow = 0, + Mut, + Immut, +}; + +struct TargetInfo { + VarDecl *TargetVD = nullptr; + std::string TargetFieldPath; + + TargetInfo() {} + TargetInfo(VarDecl *targetVD) : TargetVD(targetVD) {} + TargetInfo(VarDecl *targetVD, std::string targetFieldPath) + : TargetVD(targetVD), TargetFieldPath(targetFieldPath) {} + + bool operator==(const TargetInfo &Other) const { + return TargetVD == Other.TargetVD && + TargetFieldPath == Other.TargetFieldPath; + } +}; + +struct NonLexicalLifetimeRange { + // If a borrow pointer comes from a struct, record the field path, + // otherwise BorrowFieldPath will be void string. + // For example, + // a.b.c = &mut local; // BorrowFieldPath : ".b.c" + // p = &mut local; // BorrowFieldPath : "" + std::string BorrowFieldPath; + + unsigned Begin; + unsigned End; + BorrowKind Kind = BorrowKind::NoBorrow; + + // Record the target(VarDecl and FieldPath) of a borrow during this + // NonLexicalLifetimeRange. For owned and other variables, TargetVD will be + // void. Borrow from a variable, TargetFieldPath is void string; Borrow from a + // member of a struct variable, TargetFieldPath records fields borrowed. For + // example: + // int* borrow p1 = &mut local; // TargetVD: local, TargetFieldPath: "" + // struct S* borrow p2 = &mut s; // TargetVD: s, TargetFieldPath: "" + // int* borrow p3 = &mut s.a; // TargetVD: s, TargetFieldPath: ".a" + // int* borrow p4 = &mut s.b.c; // TargetVD: s, TargetFieldPath: ".b.c" + TargetInfo Target; + + NonLexicalLifetimeRange() {} + + // These constructors are for borrow variables, which have binding targets. + NonLexicalLifetimeRange(unsigned begin, unsigned end, BorrowKind kind, + VarDecl *targetVD) + : Begin(begin), End(end), Kind(kind), Target(TargetInfo(targetVD)) {} + NonLexicalLifetimeRange(unsigned begin, unsigned end, BorrowKind kind, + TargetInfo target) + : Begin(begin), End(end), Kind(kind), Target(target) {} + NonLexicalLifetimeRange(unsigned begin, unsigned end, BorrowKind kind, + TargetInfo target, std::string borrowFieldPath) + : BorrowFieldPath(borrowFieldPath), Begin(begin), End(end), Kind(kind), + Target(target) {} + + // This constructor is for no-borrow variables, which don't have binding + // targets. + NonLexicalLifetimeRange(unsigned begin, unsigned end) + : Begin(begin), End(end) {} + + bool operator==(const NonLexicalLifetimeRange &Other) const { + return Begin == Other.Begin && End == Other.End && Kind == Other.Kind && + Target == Other.Target && BorrowFieldPath == Other.BorrowFieldPath; + } +}; + +// NonLexicalLifetimeOfVar: NLL for a VarDecl in a cfg path. +using NonLexicalLifetimeOfVar = llvm::SmallVector; + +// NonLexicalLifetime: NLL for all variables in a cfg path. +// map: +// var: +// obj_var, owned_var, borrow_var +// lifetime: +// obj_lifetime: range +// owned_lifetime: range +// borrow_lifetime: vector> +using NonLexicalLifetime = llvm::DenseMap; + +struct BorrowInfo { + std::string TargetFieldPath; + VarDecl *BorrowVD; + std::string BorrowFieldPath; + unsigned Begin; + unsigned End; + BorrowKind Kind = BorrowKind::NoBorrow; + + BorrowInfo() {} + BorrowInfo(std::string targetFieldPath, VarDecl *borrowVD, + std::string borrowFieldPath, unsigned begin, unsigned end, + BorrowKind kind) + : TargetFieldPath(targetFieldPath), BorrowVD(borrowVD), + BorrowFieldPath(borrowFieldPath), Begin(begin), End(end), Kind(kind) {} + bool operator==(const BorrowInfo &other) { + return TargetFieldPath == other.TargetFieldPath && + BorrowVD == other.BorrowVD && + BorrowFieldPath == other.BorrowFieldPath && Begin == other.Begin && + End == other.End && Kind == other.Kind; + } +}; + +using BorrowTargetMapInfo = + llvm::DenseMap>; + +enum BorrowCheckDiagKind { + LiveLonger, + AtMostOneMutBorrow, + ReturnLocal, + ModifyAfterBeBorrowed, + ReadAfterBeMutBorrowed, +}; + +struct BorrowCheckDiagInfo { + std::string Name; + BorrowCheckDiagKind Kind; + SourceLocation Loc; + SourceLocation PreLoc; + + BorrowCheckDiagInfo(std::string Name, BorrowCheckDiagKind Kind, + SourceLocation Loc, + SourceLocation PreLoc = SourceLocation()) + : Name(Name), Kind(Kind), Loc(Loc), PreLoc(PreLoc) {} + + bool operator==(const BorrowCheckDiagInfo& other) const { + return Name == other.Name && + Kind == other.Kind && + Loc == other.Loc; + } +}; + +class BorrowCheckDiagReporter { + Sema &S; + std::vector DIV; + +public: + BorrowCheckDiagReporter(Sema &S) : S(S) {} + ~BorrowCheckDiagReporter() { flushDiagnostics(); } + + void addDiagInfo(BorrowCheckDiagInfo &DI) { + for (auto it = DIV.begin(), ei = DIV.end(); + it != ei; ++it) { + if (DI == *it) + return; + } + DIV.push_back(DI); + } + +private: + void flushDiagnostics() { + // Sort the diag info by their SourceLocations. While not strictly + // guaranteed to produce them in line/column order, this will provide + // a stable ordering. + std::sort(DIV.begin(), DIV.end(), [this](const BorrowCheckDiagInfo &a, const BorrowCheckDiagInfo &b){ + return S.getSourceManager().isBeforeInTranslationUnit(a.Loc, b.Loc); + }); + + for (const BorrowCheckDiagInfo & DI: DIV) { + switch (DI.Kind) { + case LiveLonger: + S.Diag(DI.Loc, diag::err_borrow_live_longer_than_target_var) << DI.Name; + break; + case AtMostOneMutBorrow: + S.Diag(DI.Loc, diag::err_at_most_one_mut_borrow) << DI.Name; + S.Diag(DI.PreLoc, diag::note_previous_borrow); + break; + case ReturnLocal: + S.Diag(DI.Loc, diag::err_return_value_borrow_local) << DI.Name; + break; + case ModifyAfterBeBorrowed: + S.Diag(DI.Loc, diag::err_modify_after_be_borrow) << DI.Name; + S.Diag(DI.PreLoc, diag::note_previous_borrow); + break; + case ReadAfterBeMutBorrowed: + S.Diag(DI.Loc, diag::err_read_after_be_mut_borrow) << DI.Name; + S.Diag(DI.PreLoc, diag::note_previous_borrow); + break; + default: + llvm_unreachable("unknown error type"); + break; + } + S.getDiagnostics().increaseBorrowCheckErrors(); + } + } +}; + +void runBorrowCheck(const FunctionDecl &fd, const CFG &cfg, + BorrowCheckDiagReporter &reporter, ASTContext& Ctx); + +} // end namespace clang + +#endif // ENABLE_BSC + +#endif // LLVM_CLANG_ANALYSIS_ANALYSES_BSCBORROWCHECK_H \ No newline at end of file diff --git a/clang/include/clang/Analysis/Analyses/BSCOwnership.h b/clang/include/clang/Analysis/Analyses/BSCOwnership.h index abef07863fec0e9f46b3329ba884ba90d8380b28..42a2e5bdcf8382df630f97e44fb00604d56f2cf1 100644 --- a/clang/include/clang/Analysis/Analyses/BSCOwnership.h +++ b/clang/include/clang/Analysis/Analyses/BSCOwnership.h @@ -127,7 +127,6 @@ class OwnershipDiagReporter { public: OwnershipDiagReporter(Sema &S) : S(S) {} - ~OwnershipDiagReporter() { flushDiagnostics(); } void addDiagInfo(DiagInfo &DI) { for (auto it = DIV.begin(), ei = DIV.end(); @@ -138,7 +137,6 @@ public: DIV.push_back(DI); } -private: void flushDiagnostics() { // Sort the diag info by their SourceLocations. While not strictly // guaranteed to produce them in line/column order, this will provide @@ -182,6 +180,8 @@ private: S.getDiagnostics().increaseOwnershipErrors(); } } + + unsigned getNumErrors() { return DIV.size(); } }; void runOwnershipAnalysis(const FunctionDecl &fd, const CFG &cfg, diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td index 252a24fff977b1c8888ba2dd16edb7716cbf6a08..b09088df53d65b9c0bdb1a49cd6e8ef0eee1a9f7 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -125,4 +125,14 @@ def err_ownership_read_invalid_uninit : Error< "cannot read %0, because it is uninitialized">; def err_ownership_read_invalid_moved : Error< "cannot read %0, because it is moved at line: %1">; - +def err_borrow_live_longer_than_target_var : Error< + "borrow pointer variable '%0' lives longer than target variable">; +def err_at_most_one_mut_borrow : Error< + "There should be at most one mutable borrow targeting to '%0' at the same time">; +def err_return_value_borrow_local : Error< + "Return value cannot be a borrow from local variable '%0'">; +def err_modify_after_be_borrow : Error< + "Can not modify '%0' because be borrowed">; +def err_read_after_be_mut_borrow : Error< + "Can not read '%0' because be mut borrowed">; +def note_previous_borrow : Note<"previous borrow is here">; diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index 07b67f7d7f0da390d7868ca1c801ac6b52e253b2..bf96300ec317bd57ba0eb0efc818b3fbbb2379f3 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -497,6 +497,7 @@ private: /// Number of ownership errors reported #if ENABLE_BSC unsigned NumOwnershipErrors; + unsigned NumBorrowCheckErrors; #endif /// A function pointer that converts an opaque diagnostic @@ -866,6 +867,10 @@ public: void increaseOwnershipErrors() { NumOwnershipErrors++; } + unsigned getNumBorrowCheckErrors() const { return NumBorrowCheckErrors; } + void increaseBorrowCheckErrors() { + NumBorrowCheckErrors++; + } #endif /// Return an ID for a diagnostic with the specified format string and diff --git a/clang/lib/Analysis/BSCBorrowCheck.cpp b/clang/lib/Analysis/BSCBorrowCheck.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ea812ae854245ff6b04c7698cae75f146038cdb8 --- /dev/null +++ b/clang/lib/Analysis/BSCBorrowCheck.cpp @@ -0,0 +1,1298 @@ +//===- BSCBorrowCheck.cpp - Borrow Check for Source CFGs -*- BSC --*-------// +// +// 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 BSC borrow check for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#if ENABLE_BSC + +#include "clang/Analysis/Analyses/BSCBorrowCheck.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/FlowSensitive/DataflowWorklist.h" +#include +#include +#include +#include +using namespace clang; + +using CFGPath = std::vector; +using CFGPathVec = std::vector>; + +class BorrowCheckImpl { +public: + ASTContext& Ctx; + const FunctionDecl& fd; + + NonLexicalLifetime CalculateNLLForPath( + const CFGPath &Path, + const std::map, TargetInfo> &ParamTarget, + unsigned NumElements); + void BorrowCheckForPath(const CFGPath &Path, BorrowCheckDiagReporter &reporter, + const NonLexicalLifetime& NLLForAllVars, unsigned NumElements); + void BuildParamTarget(std::map, TargetInfo>& ParamTarget); + + BorrowCheckImpl(ASTContext& ASTCtx, const FunctionDecl& FD) + : Ctx(ASTCtx), fd(FD) {} +}; + +// If the CFG is like this: +// BB4(Entry) +// | +// BB3 +// / \ +// BB2 BB1 +// \ / +// BB0(Exit) +// The Successors of all BBs in the CFG is: +// { 4:[3], 3:[2, 1], 2:[0], 1:[0] } +// Then we will compute all paths from Entry block to Exit block: +// [4, 3, 2, 0], [4, 3, 1, 0] +// TODO: This class should be modified, +// because it cannot handle the cfg with loops. +class CFGPathFinder { + CFGPathVec CFGPaths; + const CFGBlock* EntryBB; + const CFGBlock* ExitBB; +public: + CFGPathVec FindPathOfCFG(const CFG &cfg) { + EntryBB = &cfg.getEntry(); + ExitBB = &cfg.getExit(); + // Then we use DFS to find all paths from Entry block to Exit block . + FindPath(EntryBB, std::vector{ EntryBB }); + return CFGPaths; + } + +private: + void FindPath(const CFGBlock* current, CFGPath path) { + if (current == ExitBB) + CFGPaths.push_back(path); + + for(const CFGBlock *next : current->succs()) { + if (!next) + continue; + CFGPath newpath1 = path; + if(find(path.begin(), path.end(), next) == path.end()) { + newpath1.push_back(next); + CFGPath newpath2 = newpath1; + FindPath(next, newpath2); + } + } + } +}; + +class NLLCalculator : public StmtVisitor { + ASTContext& Ctx; + DeclContext* DC; + // Record VarDecls when we traverse path in reverse order. + // For borrow and owned variables, + // we should record the location they are used for the last time in path. + // For other local variables, + // we should record the location they leave current scope. + llvm::DenseMap FoundVars; + + // Only record borrow struct VarDecl and FieldPath when we traverse path in + // reverse order. For example: + // struct S { int* borrow p; }; + // void test() { + // int local = 5; + // struct S s = { .p = &mut local }; + // use(s.p); // Record `s` and ".p" + // } + std::map, unsigned> FoundBorrowFields; + + unsigned NumElements; + bool CurrOpIsBorrowUse = false; + +public: + // Record current CFGElement index we are visiting in the path. + unsigned CurElemID; + // Record NLL for all variables in a cfg path. + NonLexicalLifetime NLLForAllVars; + + NLLCalculator(ASTContext& ASTCtx, DeclContext* DeclCtx, unsigned NumElems) + : Ctx(ASTCtx), DC(DeclCtx), NumElements(NumElems), CurElemID(NumElems + 1) {} + + void VisitDeclStmt(DeclStmt *DS); + void VisitBinaryOperator(BinaryOperator *BO); + void VisitDeclRefExpr(DeclRefExpr *DRE); + void VisitReturnStmt(ReturnStmt *RS); + void VisitCallExpr(CallExpr *CE); + void VisitUnaryOperator(UnaryOperator *UO); + void VisitMemberExpr(MemberExpr *ME); + void VisitImplicitCastExpr(ImplicitCastExpr *ICE); + void VisitScopeEnd(VarDecl *VD); + void VisitScopeBegin(VarDecl *VD); + void HandleNLLAfterTraversing( + const std::map, TargetInfo> &ParamTarget); + +private: + void VisitInitForTargets(Expr *InitE, llvm::SmallVector &Targets); + void VisitInitForBorrowFieldTargets(VarDecl* VD, std::string FP, RecordDecl *RD, Expr * InitE); + void VisitFieldInitForBorrowFieldTargets(FieldDecl *FD, VarDecl *VD, Expr *InitE); + void VisitCallExprForBorrowFieldTargets(CallExpr *CE, llvm::SmallVector &Targets); + void VisitMEForFieldPath( + MemberExpr *ME, std::pair &VDAndFP); + void + UpdateNLLWhenTargetFound(TargetInfo OldTarget, + const llvm::SmallVector &NewTargets); +}; + +class BorrowRuleChecker : public StmtVisitor { + BorrowCheckDiagReporter &reporter; + +public: + NonLexicalLifetime NLLForAllVars; + unsigned CurElemID = 0; + enum Operation { None, Read, Write }; + Operation Op = None; + BorrowRuleChecker(BorrowCheckDiagReporter &reporter, const NonLexicalLifetime& NLLForVars) + : reporter(reporter), NLLForAllVars(NLLForVars) {} + + // Record the correspondence of borrowed and borrow variables. + // The key of map is borrowed variable(i.e. targets), the value is variables + // borrow from its key variable. For example, if we have such borrow + // correspondence as follows: + // `p1` borrows from `local1` from index 5 to index 10 in the path. + // `p2` borrows from `local2` from index 3 to index 4 in the path. + // `p3` borrows from `local1` and 'local2' from index 6 to index 8 in the path. + // `p4` borrows from `s.a.b` from index 11 to index 15 in the path. + // `g.c.d` borrows from `s.a.b` and `local2`from index 16 to index 18 in the path. + // Then BorrowTargetMap will be: + // local1 : { [ "", p1, "", 5, 10 ], + // [ "", p3, "", 6, 8 ] } + // local2 : { [ "", p2, "", 3, 4 ], + // [ "", p3, "", 6, 8 ], + // [ "", g, ".c.d", 16, 18 ] } + // s : { [ ".a.b", p4, "", 11, 15 ], + // [ ".a.b", g, ".c.d", 16, 18 ] } + BorrowTargetMapInfo BorrowTargetMap; + // save valid borrowed data within the current scope + llvm::DenseMap> ActiveBorrowTargetMap; + + void BuildBorrowTargetMap(); + void CheckBeBorrowedTarget(const CFGPath &Path); + void CheckBorrowNLLShorterThanTarget(); + void CheckLifetimeOfBorrowReturnValue(unsigned NumElements); + void CheckValIsLegalUse(VarDecl *VD, SourceLocation Loc); + VarDecl *FindTargetFromActiveBorrowTargetMap(VarDecl *VD); + void VisitArraySubscriptExpr(ArraySubscriptExpr *E); + void VisitBinaryOperator(BinaryOperator *BO); + void VisitCallExpr(CallExpr *CE); + void VisitConditionalOperator(ConditionalOperator *E); + void VisitCastExpr(CastExpr *E); + void VisitDeclRefExpr(DeclRefExpr *DRE); + void VisitDeclStmt(DeclStmt *DS); + void VisitInitListExpr(InitListExpr *E); + void VisitParenExpr(ParenExpr *Node); + void VisitUnaryOperator(UnaryOperator *UO); + void PushActiveBorrowTargetMap(); + void PopActiveBorrowTargetMap(); +}; + +// check current variable has been borrowed +VarDecl *BorrowRuleChecker::FindTargetFromActiveBorrowTargetMap(VarDecl *VD) { + if (VD->getType().isBorrowQualified()) { + for (auto v : ActiveBorrowTargetMap) { + auto it = v.second.rbegin(); + while (it != v.second.rend()) { + if (it->BorrowVD == VD) { + return v.first; + } + it++; + } + } + } else { + if (VD->getType()->isPointerType()) { + for (auto v : ActiveBorrowTargetMap) { + if ((v.first)->getBeginLoc() == VD->getBeginLoc() && + (v.first)->getLocation() == VD->getLocation()) { + return v.first; + } + } + } else { + for (auto v : ActiveBorrowTargetMap) { + if (v.first == VD) { + return v.first; + } + } + } + } + + return nullptr; +} + +void BorrowRuleChecker::CheckValIsLegalUse(VarDecl *VD, SourceLocation Loc) { + if (VarDecl *TVD = FindTargetFromActiveBorrowTargetMap(VD)) { + if (VD->getType().isBorrowQualified()) { + auto it = ActiveBorrowTargetMap[TVD].rbegin(); + if (VD->getType().isConstBorrow()) { + while (it != ActiveBorrowTargetMap[TVD].rend()) { + if (it->BorrowVD == VD) { + return; + } + // immut and mut borrow variables are not allowed to exist + // simultaneously. + if (!it->BorrowVD->getType().isConstBorrow()) { + BorrowCheckDiagInfo DI(VD->getNameAsString(), AtMostOneMutBorrow, + Loc, it->BorrowVD->getLocation()); + reporter.addDiagInfo(DI); + return; + } + it++; + } + } else { + assert(it != ActiveBorrowTargetMap[TVD].rend()); + if (it->BorrowVD == VD) { + return; + } else { + // Only allow used the last alive mut borrow variable. + BorrowCheckDiagInfo DI(VD->getNameAsString(), AtMostOneMutBorrow, Loc, + it->BorrowVD->getLocation()); + reporter.addDiagInfo(DI); + return; + } + } + } else { + auto it = ActiveBorrowTargetMap[TVD].rbegin(); + if (Op == Write) { + // Variables cannot be modified when be borrowed + BorrowCheckDiagInfo DI(VD->getNameAsString(), ModifyAfterBeBorrowed, + Loc, it->BorrowVD->getLocation()); + reporter.addDiagInfo(DI); + } else { + while (it != ActiveBorrowTargetMap[TVD].rend()) { + // Variables cannot be read when be mut borrowed + if (!it->BorrowVD->getType().isConstBorrow()) { + BorrowCheckDiagInfo DI(VD->getNameAsString(), + ReadAfterBeMutBorrowed, Loc, + it->BorrowVD->getLocation()); + reporter.addDiagInfo(DI); + return; + } + it++; + } + } + } + } +} + +void BorrowRuleChecker::VisitDeclRefExpr(DeclRefExpr *DRE) { + if (Op == None) { + return; + } + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { + CheckValIsLegalUse(VD, DRE->getLocation()); + } +} + +void BorrowRuleChecker::VisitUnaryOperator(UnaryOperator *UO) { + Operation TempOp = Op; + switch (UO->getOpcode()) { + case UO_PostInc: + case UO_PostDec: + case UO_PreInc: + case UO_PreDec: + Op = Write; + break; + case UO_AddrMut: + case UO_AddrConst: + case UO_AddrMutDeref: + case UO_AddrConstDeref: + return; + default: + break; + } + Visit(UO->getSubExpr()); + Op = TempOp; +} + +void BorrowRuleChecker::VisitCallExpr(CallExpr *CE) { + Operation TempOp = Op; + Op = Write; + for (auto it = CE->arg_begin(), ei = CE->arg_end(); it != ei; ++it) { + Visit(*it); + } + Op = TempOp; +} + +void BorrowRuleChecker::VisitConditionalOperator(ConditionalOperator *E) { + Visit(E->getCond()); + Visit(E->getTrueExpr()); + Visit(E->getFalseExpr()); +} + +void BorrowRuleChecker::VisitInitListExpr(InitListExpr *E) { + for (unsigned i = 0, e = E->getNumInits(); i != e; ++i) { + if (Expr *Init = E->getInit(i)) + Visit(Init); + } +} + +void BorrowRuleChecker::VisitCastExpr(CastExpr *E) { Visit(E->getSubExpr()); } + +void BorrowRuleChecker::VisitParenExpr(ParenExpr *E) { Visit(E->getSubExpr()); } + +void BorrowRuleChecker::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { + Visit(E->getLHS()); +} + +void BorrowRuleChecker::VisitBinaryOperator(BinaryOperator *BO) { + if (BO->isAssignmentOp()) { + Operation TempOp = Op; + if (DeclRefExpr *DRE = dyn_cast(BO->getLHS())) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { + if (VD->getType().isBorrowQualified()) { + return; + } + Op = Write; + CheckValIsLegalUse(VD, DRE->getLocation()); + Op = Read; + Visit(BO->getRHS()); + return; + } + } + Op = Write; + Visit(BO->getLHS()); + Op = Read; + Visit(BO->getRHS()); + Op = TempOp; + } +} + +void BorrowRuleChecker::VisitDeclStmt(DeclStmt *DS) { + Operation TempOp = Op; + for (auto *D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + if (VD->getType().isBorrowQualified()) { + return; + } + Expr *Init = VD->getInit(); + if (Init) { + Op = Read; + Visit(Init); + } + } + } + Op = TempOp; +} + +void BorrowRuleChecker::PushActiveBorrowTargetMap() { + for (auto v : BorrowTargetMap) { + for (auto w : v.second) { + unsigned firstElemId = w.Begin; + if (CurElemID == firstElemId) { + ActiveBorrowTargetMap[v.first].push_back(w); + } + } + } +} + +void BorrowRuleChecker::PopActiveBorrowTargetMap() { + for (auto v : ActiveBorrowTargetMap) { + for (auto w : v.second) { + unsigned secondElemId = w.End; + if (CurElemID == secondElemId) { + ActiveBorrowTargetMap[v.first].remove(w); + } + } + if (ActiveBorrowTargetMap[v.first].empty()) { + ActiveBorrowTargetMap.erase(v.first); + } + } +} + +void BorrowRuleChecker::CheckBeBorrowedTarget(const CFGPath &Path) { + CurElemID = 1; + PushActiveBorrowTargetMap(); + PopActiveBorrowTargetMap(); + for (auto blockIt = Path.begin(); blockIt != Path.end(); blockIt++) { + const CFGBlock *block = *blockIt; + + for (CFGBlock::const_iterator elemIt = block->begin(), + elemEI = block->end(); + elemIt != elemEI; ++elemIt) { + const CFGElement &elem = *elemIt; + CurElemID++; + Op = None; + PushActiveBorrowTargetMap(); + if (elem.getAs()) { + const Stmt *S = elem.castAs().getStmt(); + Visit(const_cast(S)); + } + PopActiveBorrowTargetMap(); + } + } +} + +void BorrowRuleChecker::BuildBorrowTargetMap() { + for (auto NLLOfVar : NLLForAllVars) { + VarDecl *BorrowVD = NLLOfVar.first; + NonLexicalLifetimeOfVar NLLRangesOfVar = NLLOfVar.second; + for (auto NLLRange : NLLRangesOfVar) { + if (NLLRange.Target.TargetVD) + BorrowTargetMap[NLLRange.Target.TargetVD].push_back(BorrowInfo( + NLLRange.Target.TargetFieldPath, BorrowVD, NLLRange.BorrowFieldPath, + NLLRange.Begin, NLLRange.End, NLLRange.Kind)); + } + } + // DEBUG PRINT + llvm::outs() << "-----------------print BorrowTargetMap--------------------\n"; + for (auto v : BorrowTargetMap) { + llvm::outs() << v.first->getNameAsString() << "\n"; + for (auto w : v.second) { + llvm::outs() << " "; + if (w.TargetFieldPath.length()) + llvm::outs() << w.TargetFieldPath; + else + llvm::outs() << "--"; + llvm::outs() << " " << w.BorrowVD->getNameAsString() << " " + << w.BorrowFieldPath << " " << w.Begin << " " << w.End + << " " << w.Kind << "\n"; + } + llvm::outs() << "\n"; + } +} + +// Core rule of borrow: +// the lifetime of borrow variable is shorter than its target variable. +void BorrowRuleChecker::CheckBorrowNLLShorterThanTarget() { + for (auto BorrowAndTarget : BorrowTargetMap) { + VarDecl* TargetVD = BorrowAndTarget.first; + for (auto Borrow : BorrowAndTarget.second) { + unsigned BorrowBegin = Borrow.Begin; + unsigned BorrowEnd = Borrow.End; + for (auto NLLOfTarget : NLLForAllVars[TargetVD]) { + if (NLLOfTarget.BorrowFieldPath == Borrow.TargetFieldPath) { + unsigned TargetBegin = NLLOfTarget.Begin; + unsigned TargetEnd = NLLOfTarget.End; + if (BorrowBegin <= TargetBegin || BorrowEnd >= TargetEnd) { + BorrowCheckDiagInfo DI(Borrow.BorrowVD->getNameAsString(), + LiveLonger, Borrow.BorrowVD->getLocation()); + reporter.addDiagInfo(DI); + } + } + } + } + } +} + +// Function cannot return a borrow of local variables. +// For example: +// int global = 5; +// const int* borrow test(const int* borrow p) { +// int local = 5; +// const int* borrow q = &mut local; +// return &const global; // right +// // return p; // right +// // return &const local; // error +// // return q; // error +// } +void BorrowRuleChecker::CheckLifetimeOfBorrowReturnValue(unsigned NumElements) { + for (auto NLLOfVar : NLLForAllVars) { + if (NLLOfVar.first->getNameAsString() == "_ReturnVar") { + for (NonLexicalLifetimeRange& Range : NLLOfVar.second) { + NonLexicalLifetimeRange TargetNLLRange = + NLLForAllVars[Range.Target.TargetVD][0]; + unsigned TargetNLLBegin = TargetNLLRange.Begin; + unsigned TargetNLLEnd = TargetNLLRange.End; + if (TargetNLLBegin > 0 || TargetNLLEnd < NumElements + 3) { + BorrowCheckDiagInfo DI(Range.Target.TargetVD->getNameAsString(), + ReturnLocal, NLLOfVar.first->getLocation()); + reporter.addDiagInfo(DI); + } + } + } + } +} + +static void FindBorrowFieldsOfStructDFS(FieldDecl *CurrFD, std::string FP, + llvm::SmallVector> + &BorrowFieldsOfStruct) { + QualType FQT = CurrFD->getType().getCanonicalType(); + if (FQT.isBorrowQualified()) { + BorrowKind BK = FQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + FP = FP + "." + CurrFD->getNameAsString(); + BorrowFieldsOfStruct.push_back(std::pair(FP, BK)); + } else if (FQT->hasBorrowFields()) { + FP = FP + "." + CurrFD->getNameAsString(); + if (auto RT = dyn_cast(FQT)) { + for (FieldDecl *FD : RT->getDecl()->fields()) + FindBorrowFieldsOfStructDFS(FD, FP, BorrowFieldsOfStruct); + } + } +} + +static void FindBorrowFieldsOfStruct(RecordDecl *RD, + llvm::SmallVector> + &BorrowFieldsOfStruct) { + for (FieldDecl *FD : RD->fields()) { + QualType FQT = FD->getType().getCanonicalType(); + if (FQT.isBorrowQualified()) { + BorrowKind BK = FQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + std::string FP = "." + FD->getNameAsString(); + BorrowFieldsOfStruct.push_back( + std::pair(FP, BK)); + } else if (FQT->hasBorrowFields()) { + FindBorrowFieldsOfStructDFS(FD, "", BorrowFieldsOfStruct); + } + } +} + +void NLLCalculator::VisitBinaryOperator(BinaryOperator *BO) { + // A borrow variable is reassigned, we would handle it like DeclStmt. + // For example: + // int* borrow p = &mut local1; + // p = &mut local2; // We handle it like `int* borrow p = &mut local2;` + if (BO->isAssignmentOp()) { + QualType QT = BO->getLHS()->getType().getCanonicalType(); + if (QT.isBorrowQualified()) { + BorrowKind BK = QT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + llvm::SmallVector Targets; + VisitInitForTargets(BO->getRHS(), Targets); + if (DeclRefExpr * DRE = dyn_cast(BO->getLHS())) { + if (VarDecl* VD = dyn_cast(DRE->getDecl())) { + // p = &mut local; + UpdateNLLWhenTargetFound(TargetInfo(VD), Targets); + for (TargetInfo Target : Targets) + NLLForAllVars[VD].push_back(NonLexicalLifetimeRange( + CurElemID, FoundVars.count(VD) ? FoundVars[VD] : CurElemID, + BK, Target)); + FoundVars.erase(VD); + } + } else if (MemberExpr *ME = dyn_cast(BO->getLHS())) { + // s.a.b = &mut local; + std::pair BorrowWithFieldPath; + VisitMEForFieldPath(ME, BorrowWithFieldPath); + UpdateNLLWhenTargetFound(TargetInfo(BorrowWithFieldPath.first, BorrowWithFieldPath.second), Targets); + for (TargetInfo Target : Targets) + NLLForAllVars[BorrowWithFieldPath.first].push_back( + NonLexicalLifetimeRange( + CurElemID, + FoundBorrowFields.count(BorrowWithFieldPath) + ? FoundBorrowFields[BorrowWithFieldPath] + : CurElemID, + BK, Target, BorrowWithFieldPath.second)); + FoundBorrowFields.erase(BorrowWithFieldPath); + } + } else if (QT->hasBorrowFields()) { + if (RecordDecl* RD = dyn_cast(QT)->getDecl()) { + if (DeclRefExpr * DRE = dyn_cast(BO->getLHS())) { + if (VarDecl* VD = dyn_cast(DRE->getDecl())) + // s = s1; + VisitInitForBorrowFieldTargets(VD, "", RD, BO->getRHS()); + } else if (MemberExpr *ME = dyn_cast(BO->getLHS())) { + // s.a = a1; + std::pair BorrowWithFieldPath; + VisitMEForFieldPath(ME, BorrowWithFieldPath); + VisitInitForBorrowFieldTargets(BorrowWithFieldPath.first, BorrowWithFieldPath.second, RD, BO->getRHS()); + } + } + } else { + CurrOpIsBorrowUse = true; + Visit(BO->getRHS()); + CurrOpIsBorrowUse = false; + } + } +} + +void NLLCalculator::VisitDeclRefExpr(DeclRefExpr *DRE) { + if (auto *VD = dyn_cast(DRE->getDecl())) { + // Add global variables to record which globals are used. + // Also add owned variables(local or parameter) to record the last location used. + if (((!VD->isLocalVarDecl() && !isa(VD)) || VD->getType().isOwnedQualified()) + && !FoundVars.count(VD)) + FoundVars[VD] = CurElemID; + + if (CurrOpIsBorrowUse) { + QualType VQT = VD->getType().getCanonicalType(); + if (VQT.isBorrowQualified() && !FoundVars.count(VD)) + FoundVars[VD] = CurElemID; + else if (VQT->hasBorrowFields()) { + if (auto RT = dyn_cast(VQT)) { + llvm::SmallVector> + BorrowFieldsOfStruct; + FindBorrowFieldsOfStruct(RT->getDecl(), BorrowFieldsOfStruct); + for (auto FieldPath : BorrowFieldsOfStruct) { + std::pair BorrowWithFieldPath( + VD, FieldPath.first); + if (!FoundBorrowFields.count(BorrowWithFieldPath)) + FoundBorrowFields[BorrowWithFieldPath] = CurElemID; + } + } + } + } + } +} + +void NLLCalculator::VisitReturnStmt(ReturnStmt *RS) { + Expr *RV = RS->getRetValue(); + if (!RV) + return; + QualType ReturnQT = RV->getType(); + if (ReturnQT.isBorrowQualified()) { + BorrowKind BK = + ReturnQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + // In order to check lifetime of borrow return value, we build a virtual ReturnVar. + std::string Name = "_ReturnVar"; + IdentifierInfo *ID = &Ctx.Idents.get(Name); + VarDecl *ReturnVD = VarDecl::Create(Ctx, DC, RV->getBeginLoc(), RV->getBeginLoc(), + ID, ReturnQT, nullptr, SC_None); + // ReturnVar itself only survive in current ReturnStmt, + // but we still should get its target to check if this function returns a + // borrow of local variable. + llvm::SmallVector Targets; + VisitInitForTargets(RV, Targets); + for (TargetInfo Target : Targets) + NLLForAllVars[ReturnVD].push_back( + NonLexicalLifetimeRange(CurElemID, CurElemID, BK, Target)); + } + + // For return stmt `return p;`, `return &mut* p`, `return *p;` or `return + // p->a` we think borrow pointer `p` is used. + CurrOpIsBorrowUse = true; + Visit(RV); + CurrOpIsBorrowUse = false; +} + +void NLLCalculator::VisitMemberExpr(MemberExpr *ME) { + if (CurrOpIsBorrowUse) { + QualType MQT = ME->getType().getCanonicalType(); + if (MQT.isBorrowQualified()) { + // Use borrow pointer directly, such as `s.p` + std::pair BorrowWithFieldPath; + VisitMEForFieldPath(ME, BorrowWithFieldPath); + if (!FoundBorrowFields.count(BorrowWithFieldPath)) + FoundBorrowFields[BorrowWithFieldPath] = CurElemID; + } else if (MQT->hasBorrowFields()) { + // Use borrow pointer indirectly, such as `s.a`, a is a struct type and + // has borrow fields. + std::pair BorrowWithFieldPath; + VisitMEForFieldPath(ME, BorrowWithFieldPath); + if (auto RT = dyn_cast(MQT)) { + llvm::SmallVector> + BorrowFieldsOfStruct; + FindBorrowFieldsOfStruct(RT->getDecl(), BorrowFieldsOfStruct); + for (auto FieldPath : BorrowFieldsOfStruct) { + std::pair BorrowWithFieldPathCopy( + BorrowWithFieldPath); + BorrowWithFieldPathCopy.second = + BorrowWithFieldPathCopy.second + FieldPath.first; + if (!FoundBorrowFields.count(BorrowWithFieldPathCopy)) + FoundBorrowFields[BorrowWithFieldPathCopy] = CurElemID; + } + } + } else + // Use borrow pointer indirectly, such as `p->a` + Visit(ME->getBase()); + } +} + +void NLLCalculator::VisitCallExpr(CallExpr *CE) { + // For function call `use(p)`, `use(&mut* p)`, `use(*p)` or `use(p->a)`, + // we think borrow pointer `p` is used here. + CurrOpIsBorrowUse = true; + for (auto it = CE->arg_begin(), ei = CE->arg_end(); it != ei; ++it) + Visit(*it); + CurrOpIsBorrowUse = false; +} + +void NLLCalculator::VisitImplicitCastExpr(ImplicitCastExpr *ICE) { + if (CurrOpIsBorrowUse) + Visit(ICE->getSubExpr()); +} + +void NLLCalculator::VisitUnaryOperator(UnaryOperator *UO) { + // For pointer deref `*p`,`&mut *p`, `&const *p` + // we think borrow pointer `p` is used here. + // we think borrow pointer `p` is used here. + if (CurrOpIsBorrowUse && + (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrMutDeref || + UO->getOpcode() == UO_AddrConstDeref)) + Visit(UO->getSubExpr()); +} + +void NLLCalculator::VisitDeclStmt(DeclStmt *DS) { + for (auto *D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + QualType VQT = VD->getType().getCanonicalType(); + if (VQT.isBorrowQualified()) { + BorrowKind BK = + VQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + // VD itself is a borrow, int* borrow p = &mut local; + llvm::SmallVector Targets; + VisitInitForTargets(VD->getInit(), Targets); + // When we find the target of a borrow, + // we should update previous target in NLLForAllVars. + UpdateNLLWhenTargetFound(TargetInfo(VD), Targets); + for (TargetInfo Target : Targets) + NLLForAllVars[VD].push_back(NonLexicalLifetimeRange( + CurElemID, FoundVars.count(VD) ? FoundVars[VD] : CurElemID, BK, + Target)); + FoundVars.erase(VD); + } else if (VQT->hasBorrowFields()) { + if (RecordDecl* RD = dyn_cast(VQT)->getDecl()) + VisitInitForBorrowFieldTargets(VD, "", RD, VD->getInit()); + } else if (VD->getInit()) { + CurrOpIsBorrowUse = true; + Visit(VD->getInit()); + CurrOpIsBorrowUse = false; + } + } + } +} + +void NLLCalculator::VisitInitForBorrowFieldTargets(VarDecl* VD, std::string FP, RecordDecl *RD, Expr * InitE) { + if (auto InitListE = dyn_cast(InitE)) { + // Init by InitListExpr, struct S s = { .a = &mut local }; + Expr **Inits = InitListE->getInits(); + for (auto FD : RD->fields()) { + Expr *FieldInit = Inits[FD->getFieldIndex()]; + VisitFieldInitForBorrowFieldTargets(FD, VD, FieldInit); + } + } else if (auto ICE = dyn_cast(InitE)) { + // Init by another struct + llvm::SmallVector> BorrowFieldsOfStruct; + FindBorrowFieldsOfStruct(RD, BorrowFieldsOfStruct); + for (auto FieldPath : BorrowFieldsOfStruct) { + TargetInfo Target; + llvm::SmallVector Targets; + std::pair BorrowWithFieldPath(VD, FP + FieldPath.first); + if (auto DRE = dyn_cast(ICE->getSubExpr())) { + // struct S s = s1; + // s = s1; + // s.a = s1; + if (VarDecl* IVD = dyn_cast(DRE->getDecl())) { + Target.TargetVD = IVD; + Target.TargetFieldPath = FieldPath.first; + } + } else if (auto ME = dyn_cast(ICE->getSubExpr())) { + // struct S s = s1.a; + // s = s1.a; + // s.a = s1.a; + std::pair TargetWithFieldPath; + VisitMEForFieldPath(ME, TargetWithFieldPath); + Target.TargetVD = TargetWithFieldPath.first; + Target.TargetFieldPath = TargetWithFieldPath.second + FieldPath.first; + } + Targets.push_back(Target); + UpdateNLLWhenTargetFound(TargetInfo(BorrowWithFieldPath.first, BorrowWithFieldPath.second), Targets); + NLLForAllVars[VD].push_back(NonLexicalLifetimeRange( + CurElemID, + FoundBorrowFields.count(BorrowWithFieldPath) + ? FoundBorrowFields[BorrowWithFieldPath] + : CurElemID, + FieldPath.second, Target, BorrowWithFieldPath.second)); + FoundBorrowFields.erase(BorrowWithFieldPath); + } + } else if (auto CE = dyn_cast(InitE)) { + llvm::SmallVector Targets; + VisitCallExprForBorrowFieldTargets(CE, Targets); + llvm::SmallVector> BorrowFieldsOfStruct; + FindBorrowFieldsOfStruct(RD, BorrowFieldsOfStruct); + for (auto FieldPath : BorrowFieldsOfStruct) { + UpdateNLLWhenTargetFound(TargetInfo(VD, FP + FieldPath.first), Targets); + std::pair BorrowWithFieldPath; + BorrowWithFieldPath.first = VD; + BorrowWithFieldPath.second = FP + FieldPath.first; + for (auto Target : Targets) + NLLForAllVars[VD].push_back(NonLexicalLifetimeRange( + CurElemID, + FoundBorrowFields.count(BorrowWithFieldPath) + ? FoundBorrowFields[BorrowWithFieldPath] + : CurElemID, + FieldPath.second, Target, FP + FieldPath.first)); + FoundBorrowFields.erase(BorrowWithFieldPath); + } + } +} + +void NLLCalculator::VisitFieldInitForBorrowFieldTargets(FieldDecl *FD, VarDecl *VD, Expr *InitE) { + QualType FQT = FD->getType().getCanonicalType(); + if (FQT.isBorrowQualified()) { + BorrowKind BK = + FQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + llvm::SmallVector Targets; + VisitInitForTargets(InitE, Targets); + std::pair BorrowWithFieldPath; + BorrowWithFieldPath.first = VD; + BorrowWithFieldPath.second = "." + FD->getNameAsString(); + UpdateNLLWhenTargetFound(TargetInfo(BorrowWithFieldPath.first, + BorrowWithFieldPath.second), + Targets); + for (TargetInfo Target : Targets) + NLLForAllVars[VD].push_back(NonLexicalLifetimeRange( + CurElemID, + FoundBorrowFields.count(BorrowWithFieldPath) + ? FoundBorrowFields[BorrowWithFieldPath] + : CurElemID, + BK, Target, BorrowWithFieldPath.second)); + FoundBorrowFields.erase(BorrowWithFieldPath); + } else if (FQT->hasBorrowFields()) { + if (auto RT = dyn_cast(FQT)) { + RecordDecl *RD = RT->getDecl(); + if (auto ICE = dyn_cast(InitE)) { + // Init by another struct + llvm::SmallVector> BorrowFieldsOfStruct; + FindBorrowFieldsOfStruct(RD, BorrowFieldsOfStruct); + for (auto FieldPath : BorrowFieldsOfStruct) { + TargetInfo Target; + llvm::SmallVector Targets; + std::pair BorrowWithFieldPath; + BorrowWithFieldPath.first = VD; + BorrowWithFieldPath.second = "." + FD->getNameAsString() + FieldPath.first; + if (auto DRE = dyn_cast(ICE->getSubExpr())) { + // .a = s1;` + if (VarDecl* IVD = dyn_cast(DRE->getDecl())) { + Target.TargetVD = IVD; + Target.TargetFieldPath = FieldPath.first; + } + } else if (auto ME = dyn_cast(ICE->getSubExpr())) { + // .a = s1.a;` + std::pair TargetWithFieldPath; + VisitMEForFieldPath(ME, TargetWithFieldPath); + Target.TargetVD = TargetWithFieldPath.first; + Target.TargetFieldPath = TargetWithFieldPath.second + FieldPath.first; + } + Targets.push_back(Target); + UpdateNLLWhenTargetFound(TargetInfo(BorrowWithFieldPath.first, BorrowWithFieldPath.second), Targets); + NLLForAllVars[VD].push_back(NonLexicalLifetimeRange( + CurElemID, + FoundBorrowFields.count(BorrowWithFieldPath) + ? FoundBorrowFields[BorrowWithFieldPath] + : CurElemID, + FieldPath.second, Target, BorrowWithFieldPath.second)); + FoundBorrowFields.erase(BorrowWithFieldPath); + } + } else if (auto CE = dyn_cast(InitE)) { + llvm::SmallVector Targets; + VisitCallExprForBorrowFieldTargets(CE, Targets); + llvm::SmallVector> BorrowFieldsOfStruct; + FindBorrowFieldsOfStruct(RD, BorrowFieldsOfStruct); + for (auto FieldPath : BorrowFieldsOfStruct) { + UpdateNLLWhenTargetFound(TargetInfo(VD, "." + FD->getNameAsString() + FieldPath.first), Targets); + std::pair BorrowWithFieldPath; + BorrowWithFieldPath.first = VD; + BorrowWithFieldPath.second = "." + FD->getNameAsString() + FieldPath.first; + for (auto Target : Targets) + NLLForAllVars[VD].push_back(NonLexicalLifetimeRange( + CurElemID, + FoundBorrowFields.count(BorrowWithFieldPath) + ? FoundBorrowFields[BorrowWithFieldPath] + : CurElemID, + FieldPath.second, Target, "." + FD->getNameAsString() + FieldPath.first)); + FoundBorrowFields.erase(BorrowWithFieldPath); + } + } + } + } +} + +void NLLCalculator::VisitCallExprForBorrowFieldTargets(CallExpr *CE, llvm::SmallVector &Targets) { + for (auto it = CE->arg_begin(), ei = CE->arg_end(); it != ei; ++it) { + if (Expr* ArgExpr = dyn_cast(*it)) { + QualType ArgQT = ArgExpr->getType().getCanonicalType(); + if (ArgQT.isBorrowQualified()) + VisitInitForTargets(ArgExpr, Targets); + else if (ArgQT->hasBorrowFields()) { + if (auto ICE = dyn_cast(ArgExpr)) { + RecordDecl *ArgRD = dyn_cast(ArgQT)->getDecl(); + llvm::SmallVector> BorrowFieldsOfStruct; + FindBorrowFieldsOfStruct(ArgRD, BorrowFieldsOfStruct); + for (auto FieldPath : BorrowFieldsOfStruct) { + TargetInfo Target; + if (auto DRE = dyn_cast(ICE->getSubExpr())) { + if (VarDecl* ArgVD = dyn_cast(DRE->getDecl())) { + Target.TargetVD = ArgVD; + Target.TargetFieldPath = FieldPath.first; + } + } else if (auto ME = dyn_cast(ICE->getSubExpr())) { + std::pair TargetWithFieldPath; + VisitMEForFieldPath(ME, TargetWithFieldPath); + Target.TargetVD = TargetWithFieldPath.first; + Target.TargetFieldPath = TargetWithFieldPath.second + FieldPath.first; + } + Targets.push_back(Target); + } + } + } + } + } +} + +void NLLCalculator::VisitMEForFieldPath( + MemberExpr *ME, std::pair &VDAndFP) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + VDAndFP.second = "." + FD->getNameAsString() + VDAndFP.second; + if (auto BaseME = dyn_cast(ME->getBase())) + VisitMEForFieldPath(BaseME, VDAndFP); + else if (auto BaseDRE = dyn_cast(ME->getBase())) { + VarDecl *BaseVD = dyn_cast(BaseDRE->getDecl()); + VDAndFP.first = BaseVD; + } + } +} + +void NLLCalculator::VisitInitForTargets( + Expr *InitE, llvm::SmallVector &Targets) { + if (auto* DRE = dyn_cast(InitE)) { + if (VarDecl* IVD = dyn_cast(DRE->getDecl())) { + QualType VQT = IVD->getType().getCanonicalType(); + if (VQT->hasBorrowFields()) { + if (auto RT = dyn_cast(VQT)) { + llvm::SmallVector> BorrowFieldsOfStruct; + FindBorrowFieldsOfStruct(RT->getDecl(), BorrowFieldsOfStruct); + for (auto FieldPath : BorrowFieldsOfStruct) + Targets.push_back(TargetInfo(IVD, FieldPath.first)); + } + } else + Targets.push_back(TargetInfo(IVD)); + } + return; + } else if (auto* ME = dyn_cast(InitE)) { + // int* borrow p = s.a.b; `s.a.b` is MemberExpr. + std::pair TargetWithFieldPath; + VisitMEForFieldPath(ME, TargetWithFieldPath); + QualType MQT = ME->getType().getCanonicalType(); + if (MQT->hasBorrowFields()) { + if (auto RT = dyn_cast(MQT)) { + llvm::SmallVector> BorrowFieldsOfStruct; + FindBorrowFieldsOfStruct(RT->getDecl(), BorrowFieldsOfStruct); + for (auto FieldPath : BorrowFieldsOfStruct) + Targets.push_back(TargetInfo(TargetWithFieldPath.first, TargetWithFieldPath.second + FieldPath.first)); + } + } else + Targets.push_back(TargetInfo(TargetWithFieldPath.first, TargetWithFieldPath.second)); + return; + } + + if (auto UO = dyn_cast(InitE)) { + if (UO->getOpcode() == UO_AddrMut || UO->getOpcode() == UO_AddrConst + || UO->getOpcode() == UO_AddrMutDeref || UO->getOpcode() == UO_AddrConstDeref) + VisitInitForTargets(UO->getSubExpr(), Targets); + } else if (auto ICE = dyn_cast(InitE)) { + // int* borrow p = p1; `p1` is ImplicitCastExpr. + VisitInitForTargets(ICE->getSubExpr(), Targets); + } else if (auto ASE = dyn_cast(InitE)) { + // int* borrow p = &mut arr[0]; `arr[0]` is ArraySubscriptExpr. + VisitInitForTargets(ASE->getBase(), Targets); + } else if (auto CE = dyn_cast(InitE)) { + // int* borrow p = foo(&mut local1, &mut local2); + // the lifetime of p should be smaller than the lifetime intersection of local1 and local2. + // we add local1 and local2 to Targets. + for (auto it = CE->arg_begin(), ei = CE->arg_end(); it != ei; ++it) + if (Expr* ArgExpr = dyn_cast(*it)) + VisitInitForTargets(ArgExpr, Targets); + } else if (auto CO = dyn_cast(InitE)) { + VisitInitForTargets(CO->getLHS(), Targets); + VisitInitForTargets(CO->getRHS(), Targets); + } +} + +void NLLCalculator::VisitScopeBegin(VarDecl *VD) { + QualType QT = VD->getType(); + if (QT->isPointerType() && !QT.isOwnedQualified() && !QT.isBorrowQualified()) { + // For local naked pointer, we create virtual ParentVar as target. + std::string Name = "_ParentVar_" + VD->getNameAsString(); + IdentifierInfo *ID = &Ctx.Idents.get(Name); + VarDecl *VirtualVD = VarDecl::Create(Ctx, DC, + VD->getBeginLoc(), VD->getLocation(), ID, + QualType(), nullptr, SC_None); + // Update previous NLL whose target is this naked pointer VD to virtual + // ParentVar + llvm::SmallVector VirtualTarget; + VirtualTarget.push_back(TargetInfo(VirtualVD)); + UpdateNLLWhenTargetFound(TargetInfo(VD), VirtualTarget); + // Add NLL for virtual ParentVar. + NLLForAllVars[VirtualVD].push_back(NonLexicalLifetimeRange(0, NumElements + 3)); + } + if (!QT.isBorrowQualified()) { + // For no-borrow local variable, its NLL is continous, and ScopeBegin marks the begin of its NLL. + NLLForAllVars[VD].push_back(NonLexicalLifetimeRange(CurElemID, FoundVars[VD])); + FoundVars.erase(VD); + } +} + +void NLLCalculator::VisitScopeEnd(VarDecl *VD) { + // For no-borrow and no-owned local variable, ScopeEnd marks the end of its NLL. + QualType QT = VD->getType(); + if (!QT.isBorrowQualified() && !QT.isOwnedQualified()) + FoundVars[VD] = CurElemID; +} + +// When we find the target of a borrow, +// we should update previous target in NLLForAllVars. +// For such example: +// int * borrow p1 = use_and_return(&mut local1, &mut local2); // 1 +// // p3:[3, 3]->local1, [3, 3]->local2 +// // p2:[2, 2]->local1, [2, 2]->local2 +// // p1:[1, 1]->local1, [1, 1]->local2 +// int * borrow p2 = p1; // 2 +// // p3:[3, 3]->p1 +// // p2:[2, 2]->p1 +// int * borrow p3 = p2; // 3 +// // p3:[3, 3]->p2 +// Because we traverse path in reverse order, we handle in order 3->2->1. +// When we handle 3, we know p3 targeting to p2, +// but we don't know p2 targeting to which. +// When we handle 2, we know p2 targeting to p1, +// we should update the target of p3 from p2 to p1. +// When we handle 1, we know p1 targeting to local1 and local2, +// we should update the target of p2 and p3 from p1 to local1 and local2. +void NLLCalculator::UpdateNLLWhenTargetFound( + TargetInfo OldTarget, const llvm::SmallVector &NewTargets) { + for (auto& NLLOfVar : NLLForAllVars) { + NonLexicalLifetimeOfVar& NLLRanges = NLLOfVar.second; + llvm::SmallVector> + RangesNeedUpdate; + // Remove ranges whose target is OldTarget + for (auto it = NLLRanges.begin(); it != NLLRanges.end();) { + if (it->Target == OldTarget) { + RangesNeedUpdate.push_back( + std::tuple( + it->BorrowFieldPath, it->Begin, it->End, it->Kind)); + it = NLLRanges.erase(it); + } else + ++it; + } + // Build new ranges targeting to NewTarget and insert them to NLLRanges. + for (std::tuple + RangeNeedUpdate : RangesNeedUpdate) { + for (TargetInfo NewTarget : NewTargets) { + NonLexicalLifetimeRange NewRange( + std::get<1>(RangeNeedUpdate), std::get<2>(RangeNeedUpdate), + std::get<3>(RangeNeedUpdate), NewTarget, + std::get<0>(RangeNeedUpdate)); + if (std::find(NLLRanges.begin(), NLLRanges.end(), NewRange) == NLLRanges.end()) + NLLRanges.push_back(NewRange); + } + } + } +} + +// In order to calculate NLL for global and parameter, +// we use virtual CFGElement index to mark the begin and end of global variable and parameter +// NLL begin of global/virtual ParentVar :0 +// NLL begin of parameter :1 +// local variables :from 2 to NumElements + 1 +// NLL end of borrow or owned parameter :last use location +// NLL end of no-borrow-no-owned parameter :NumElements + 2 +// NLL end of global/virtual ParentVar :NumElements + 3 +void NLLCalculator::HandleNLLAfterTraversing( + const std::map, TargetInfo> &ParamTarget) { + // Handle parameter + for (auto Param : ParamTarget) { + ParmVarDecl* PVD = std::get<0>(Param.first); + std::string PFP = std::get<1>(Param.first); + BorrowKind PBK = std::get<2>(Param.first); + QualType PQT = PVD->getType(); + if (PFP.size() > 0) { + llvm::SmallVector VirtualTarget; + VirtualTarget.push_back(Param.second); + UpdateNLLWhenTargetFound(TargetInfo(PVD, PFP), VirtualTarget); + // Add NLL for virtual ParentVar. + NLLForAllVars[Param.second.TargetVD].push_back( + NonLexicalLifetimeRange(0, NumElements + 3)); + // Add NLL for param borrow field with target. + std::pair BorrowWithFieldPath(PVD, PFP); + NLLForAllVars[PVD].push_back(NonLexicalLifetimeRange( + 1, FoundBorrowFields.count(BorrowWithFieldPath) ? FoundBorrowFields[BorrowWithFieldPath] : 1, PBK, Param.second, PFP)); + FoundBorrowFields.erase(BorrowWithFieldPath); + } else { + if (PQT->isPointerType() && !PQT.isOwnedQualified()) { + // Handle borrow or naked pointer parameter which have virtual ParentVar. + // Update previous NLL whose target is PVD to virtual ParentVar + llvm::SmallVector VirtualTarget; + VirtualTarget.push_back(Param.second); + UpdateNLLWhenTargetFound(TargetInfo(PVD), VirtualTarget); + // Add NLL for virtual ParentVar. + NLLForAllVars[Param.second.TargetVD].push_back( + NonLexicalLifetimeRange(0, NumElements + 3)); + // Add NLL for param with target. + if (PQT.isBorrowQualified()) { // PVD is borrow pointer + BorrowKind BK = + PQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + NLLForAllVars[PVD].push_back(NonLexicalLifetimeRange( + 1, FoundVars.count(PVD) ? FoundVars[PVD] : 1, BK, Param.second)); + } else // PVD is naked pointer + NLLForAllVars[PVD].push_back( + NonLexicalLifetimeRange(1, NumElements + 2)); + } else if (PQT.isOwnedQualified()) { + // Add NLL for owned param. + NLLForAllVars[PVD].push_back(NonLexicalLifetimeRange(1, FoundVars[PVD])); + } else + // Add NLL for other param. + NLLForAllVars[PVD].push_back(NonLexicalLifetimeRange(1, NumElements + 2)); + FoundVars.erase(PVD); + } + } + // Handle global variable + // There are only global variables left in FoundVars at this time. + for (auto globalVar : FoundVars) + NLLForAllVars[globalVar.first].push_back(NonLexicalLifetimeRange(0, NumElements + 3)); +} + +// Compute non-lexical Lifetime for all variables in a certain path. +NonLexicalLifetime BorrowCheckImpl::CalculateNLLForPath( + const CFGPath &Path, + const std::map, TargetInfo> &ParamTarget, + unsigned NumElements) { + NLLCalculator Calculator(Ctx, const_cast(fd.getDeclContext()), NumElements); + + // To find when a borrow or owned variable is used for the last time, + // we should traverse the path in reverse order. + for (auto revBlockIt = Path.rbegin(); revBlockIt != Path.rend(); revBlockIt++) { + const CFGBlock* block = *revBlockIt; + llvm::outs() << "Current block id: " << block->getBlockID() << " "; + // we should also traverse the block in reverse order. + for (CFGBlock::const_reverse_iterator revElemIt = block->rbegin(), + revElemEI = block->rend(); + revElemIt != revElemEI; ++revElemIt) { + const CFGElement &elem = *revElemIt; + + if (elem.getAs()) { + const Stmt *S = elem.castAs().getStmt(); + Calculator.Visit(const_cast(S)); + } + // Local Variable in scope + if (elem.getAs()) { + const VarDecl *VD = elem.castAs().getVarDecl(); + Calculator.VisitScopeBegin(const_cast(VD)); + } + + if (elem.getAs()) { + const VarDecl *VD = elem.castAs().getVarDecl(); + Calculator.VisitScopeEnd(const_cast(VD)); + } + llvm::outs() << Calculator.CurElemID << " "; + Calculator.CurElemID--; + } + llvm::outs() << "\n"; + } + + // After traversing all CFGElement in path, we have got NLL for all local variables, + // here we add NLL for global variables and parameters. + Calculator.HandleNLLAfterTraversing(ParamTarget); + + // DEBUG PRINT + llvm::outs() << "----------------print NLL------------------\n"; + for (auto u : Calculator.NLLForAllVars) { + llvm::outs() << u.first->getNameAsString() << "\n"; // VarDecl + for (auto v : u.second) { // NLL + llvm::outs() << " "; + if (v.BorrowFieldPath.length()) + llvm::outs() << v.BorrowFieldPath; + else + llvm::outs() << "--"; + llvm::outs() << " " << v.Begin << " " << v.End; + if (v.Target.TargetVD) + llvm::outs() << " " << v.Target.TargetVD->getNameAsString() << " " + << v.Target.TargetFieldPath; + llvm::outs() << " " << v.Kind << "\n"; + } + } + llvm::outs() << "\n"; + + return Calculator.NLLForAllVars; +} + +// For borrow or naked pointer parameter, we create virtual ParentVar as target. +// Other kind of parameter don't have target. +void BorrowCheckImpl::BuildParamTarget(std::map, TargetInfo>& ParamTarget) { + for (ParmVarDecl *PVD : fd.parameters()) { + QualType PQT = PVD->getType().getCanonicalType(); + if (PQT->isPointerType() && !PQT.isOwnedQualified()) { + std::string Name = "_ParentVar_" + PVD->getNameAsString(); + IdentifierInfo *ID = &Ctx.Idents.get(Name); + VarDecl *VD = VarDecl::Create(Ctx, const_cast(fd.getDeclContext()), + PVD->getBeginLoc(), PVD->getLocation(), ID, + QualType(), nullptr, SC_None); + if (PQT.isBorrowQualified()) { + BorrowKind BK = PQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + ParamTarget[std::tuple(PVD, "", BK)] = TargetInfo(VD); + } else + ParamTarget[std::tuple(PVD, "", BorrowKind::NoBorrow)] = TargetInfo(VD); + continue; + } else if (PQT->hasBorrowFields()) { + if (auto RT = dyn_cast(PQT)) { + RecordDecl* RD = RT->getDecl(); + llvm::SmallVector> BorrowFieldsOfStruct; + FindBorrowFieldsOfStruct(RD, BorrowFieldsOfStruct); + for (auto FieldPath : BorrowFieldsOfStruct) { + std::tuple BorrowWithFieldPath; + std::get<0>(BorrowWithFieldPath) = PVD; + std::get<1>(BorrowWithFieldPath) = FieldPath.first; + std::get<2>(BorrowWithFieldPath) = FieldPath.second; + std::string FP = FieldPath.first; + FP.erase(std::remove(FP.begin(), FP.end(), '.'), FP.end()); + std::string Name = "_ParentVar_" + PVD->getNameAsString() + FP; + IdentifierInfo *ID = &Ctx.Idents.get(Name); + VarDecl *VD = VarDecl::Create(Ctx, const_cast(fd.getDeclContext()), + PVD->getBeginLoc(), PVD->getLocation(), ID, + QualType(), nullptr, SC_None); + ParamTarget[BorrowWithFieldPath] = TargetInfo(VD); + } + } + } + ParamTarget[std::tuple(PVD, "", BorrowKind::NoBorrow)] = TargetInfo(); + } +} + +void BorrowCheckImpl::BorrowCheckForPath(const CFGPath &Path, BorrowCheckDiagReporter &reporter, + const NonLexicalLifetime& NLLForAllVars, unsigned NumElements) { + BorrowRuleChecker BRC(reporter, NLLForAllVars); + BRC.BuildBorrowTargetMap(); + BRC.CheckBorrowNLLShorterThanTarget(); + if (fd.getReturnType().isBorrowQualified()) + BRC.CheckLifetimeOfBorrowReturnValue(NumElements); + BRC.CheckBeBorrowedTarget(Path); +} + +void clang::runBorrowCheck(const FunctionDecl &fd, const CFG &cfg, + BorrowCheckDiagReporter &reporter, ASTContext& Ctx) { + // The analysis currently has scalability issues for very large CFGs. + // Bail out if it looks too large. + if (cfg.getNumBlockIDs() > 300000) + return; + + BorrowCheckImpl BC(Ctx, fd); + + std::map, TargetInfo> ParamTarget; + BC.BuildParamTarget(ParamTarget); + + // First we find all paths from entry block to exit block of a cfg. + CFGPathFinder PathFinder; + CFGPathVec CFGAllPaths = PathFinder.FindPathOfCFG(cfg); + + // DEBUG PRINT + llvm::outs() << "------------------print PathSet----------------" << "\n"; + for (CFGPath Path : CFGAllPaths) { + for (const CFGBlock* bb : Path) { + llvm::outs() << bb->getBlockID() << "->"; + } + llvm::outs() << "\n"; + } + for (const CFGBlock *BB : cfg.const_reverse_nodes()) + BB->dump(); + + // Calculation of NLL and borrow check will be executed for each path. + for (CFGPath Path : CFGAllPaths) { + // Count the number of all CFGElements in this path. + unsigned NumElements = 0; + for (auto block: Path) + NumElements += block->size(); + // Calculate NLL for all variables in current path. + NonLexicalLifetime NLLForAllVars = BC.CalculateNLLForPath(Path, ParamTarget, NumElements); + // Check all borrow rules in current path. + BC.BorrowCheckForPath(Path, reporter, NLLForAllVars, NumElements); + } +} + +#endif // ENABLE_BSC \ No newline at end of file diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt index 6da0675f6bdcd0e5816f59fcad582243588d0f96..978c1648df8e345b40a9a4799c9c8f650892c1d6 100644 --- a/clang/lib/Analysis/CMakeLists.txt +++ b/clang/lib/Analysis/CMakeLists.txt @@ -7,6 +7,7 @@ add_clang_library(clangAnalysis AnalysisDeclContext.cpp BodyFarm.cpp BSCOwnership.cpp + BSCBorrowCheck.cpp CalledOnceCheck.cpp CFG.cpp CFGReachabilityAnalysis.cpp diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp index 0d1fad4f29e551d8383348a21770a98165a9808f..1a39cffebf2194a5d991042d0e4d7f8f30f7c542 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -141,6 +141,7 @@ void DiagnosticsEngine::Reset(bool soft /*=false*/) { NumErrors = 0; #if ENABLE_BSC NumOwnershipErrors = 0; + NumBorrowCheckErrors = 0; #endif TrapNumErrorsOccurred = 0; TrapNumUnrecoverableErrorsOccurred = 0; diff --git a/clang/lib/Sema/BSC/SemaBSCOwnership.cpp b/clang/lib/Sema/BSC/SemaBSCOwnership.cpp index 726836e40a7f3721c53821eea2d48577b1145e19..1daa964ec707bf3e4f667f7c5109448d4eb62968 100644 --- a/clang/lib/Sema/BSC/SemaBSCOwnership.cpp +++ b/clang/lib/Sema/BSC/SemaBSCOwnership.cpp @@ -15,6 +15,7 @@ #include "clang/AST/Type.h" #include "clang/Analysis/Analyses/BSCOwnership.h" +#include "clang/Analysis/Analyses/BSCBorrowCheck.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Sema/Sema.h" #include "clang/Sema/SemaDiagnostic.h" @@ -221,8 +222,15 @@ void Sema::CheckBSCOwnership(const Decl *D) { AC.getCFGBuildOptions().setAllAlwaysAdd(); OwnershipDiagReporter Reporter(*this); - if (AC.getCFG()) + if (AC.getCFG()) { runOwnershipAnalysis(*cast(D), *AC.getCFG(), AC, Reporter); + Reporter.flushDiagnostics(); + // Run borrow check when there is no other ownership errors in current function. + if (!Reporter.getNumErrors()) { + BorrowCheckDiagReporter BorrowCheckReporter(*this); + runBorrowCheck(*cast(D), *AC.getCFG(), BorrowCheckReporter, Context); + } + } } void Sema::CheckMoveVarMemoryLeak(Expr* E, SourceLocation SL) { diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 1ecb8fc9fb3caabbc6deda0c94897b6152429493..c6f64ac1cb5f48547f6457a0f18b74945f94b118 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -2216,7 +2216,7 @@ Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP, if (!DisableOwnershipCheck) { if (LangOpts.BSC && !isBSCCoroutine && (getDiagnostics().getNumErrors() == - getDiagnostics().getNumOwnershipErrors()) && + getDiagnostics().getNumOwnershipErrors() + getDiagnostics().getNumBorrowCheckErrors()) && D) if (const auto *const CastReturn = dyn_cast_or_null(D)) CheckBSCOwnership(D); diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs new file mode 100644 index 0000000000000000000000000000000000000000..068a04176975d41c71984544f92dee0395fead24 --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs @@ -0,0 +1,130 @@ +// RUN: %clang_cc1 -ast-dump -verify %s + +void use_mut(int *borrow p) {} +void use_immut(const int *borrow p) {} +T* owned safe_malloc(T value); +void free_owned(T* owned p); +void free(void*); + +void test1() { + int local = 42; + int *borrow p1 = &mut local; + int *borrow p2 = &mut local; // expected-note {{previous borrow is here}} + use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'p1' at the same time}} + use_mut(p2); +} + +void test2() { + int *owned oriPtr = safe_malloc(0); + int *borrow p1 = &mut *oriPtr; + int *borrow p2 = &mut *oriPtr; // expected-note {{previous borrow is here}} + use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'p1' at the same time}} + use_mut(p2); + free_owned(oriPtr); +} + +void test3(int* oriPtr) { + int *borrow p1 = &mut * oriPtr; + int *borrow p2 = &mut * oriPtr; + use_mut(p1); // error + free(oriPtr); +} + +void test4() { + int local = 5; + int *borrow p = &mut local; + int *borrow p1 = &mut local; + const int *borrow p2 = &const *p; // expected-note {{previous borrow is here}} + // expected-note@-1 {{previous borrow is here}} + use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'p' at the same time}} + use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'p1' at the same time}} + use_immut(p2); +} + +void test5() { + int local = 5; + int *borrow p1 = &mut local; + const int *borrow p2 = &const local; // expected-note {{previous borrow is here}} + use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'p1' at the same time}} + use_immut(p2); +} + +void test6(int* borrow p) { + int local = 5; + p = &mut local; + const int *borrow p1 = &const *p; // expected-note {{previous borrow is here}} + use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'p' at the same time}} + use_immut(p1); +} + +void test7(int* borrow p) { + int *borrow p1 = p; // expected-note {{previous borrow is here}} + use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'p' at the same time}} + use_mut(p1); +} + +void test8() { + int arr[5]; + int *borrow p = &mut arr[0]; + int *borrow p1 = &mut arr[1]; // expected-note {{previous borrow is here}} + use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'p' at the same time}} + use_mut(p1); +} + +void use_two_borrow(int *borrow p1, int *borrow p2) {} +void test9() { + int local = 42; + int *borrow p1 = &mut local; + int *borrow p2 = &mut local; // expected-note {{previous borrow is here}} + use_two_borrow(p1, p2); // expected-error {{There should be at most one mutable borrow targeting to 'p1' at the same time}} +} + +void test10() { + int local = 5; + int *borrow p1 = &mut local; + const int *borrow p2 = &const local; // expected-note {{previous borrow is here}} + int *owned oriPtr = safe_malloc(0); + if (local > 0) { + p1 = &mut *oriPtr; + p2 = &const *oriPtr; + } else { + p2 = &const *oriPtr; + p1 = &mut *oriPtr; + } + use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'p1' at the same time}} + use_immut(p2); + free_owned(oriPtr); +} + +void test11() { + int local = 5; + int *borrow p1 = &mut local; + const int *borrow p2 = &const local; // expected-note {{previous borrow is here}} + int *owned oriPtr = safe_malloc(0); + if (1) { + p1 = &mut *oriPtr; + p2 = &const *oriPtr; + } else { //Dead code + p1 = &mut *oriPtr; + p2 = &const *oriPtr; + } + use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'p1' at the same time}} + use_immut(p2); + free_owned(oriPtr); +} + +int main() { + int local = 5; + test1(); + test2(); + test3(&local); + test4(); + test5(); + test6(&mut local); + test7(&mut local); + test8(); + test9(); + test10(); + test11(); + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer/borrow_live_longer.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer/borrow_live_longer.cbs new file mode 100644 index 0000000000000000000000000000000000000000..c870705af7adb9b087fc43a4c93a907c1e81c74a --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer/borrow_live_longer.cbs @@ -0,0 +1,89 @@ +// RUN: %clang_cc1 -verify %s + +void use_mut(int *borrow p) {} +T* owned safe_malloc(T value); +void free_owned(T* owned p); +void free(void*); + +void test1() { + int local = 42; + int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + { + int local1 = 42; + p = &mut local1; + } + use_mut(p); +} + +void test2(int* borrow p) { // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + { + int local1 = 42; + p = &mut local1; + } + use_mut(p); +} + +void test3() { + int local = 42; + int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + if (1) { + int local1 = 42; + p = &mut local1; + } + use_mut(p); +} + +void test4() { + int local = 42; + int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + if (local > 0) { + int local1 = 42; + p = &mut local1; + } else { + int local2 = 42; + p = &mut local2; + } + use_mut(p); +} + +void test5() { + int *owned oriPtr = safe_malloc(0); + int *borrow p = &mut * oriPtr; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + // expected-note@-1 {{previous borrow is here}} + free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + use_mut(p); +} + +void test6(int *owned oriPtr) { + int *borrow p = &mut * oriPtr; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + // expected-note@-1 {{previous borrow is here}} + free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + use_mut(p); +} + +void test7() { + int a = 5; + int *owned oriPtr = safe_malloc(0); + int *borrow p = &mut* oriPtr; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + // expected-note@-1 {{previous borrow is here}} + // expected-note@-2 {{previous borrow is here}} + if (a > 0) { + free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + } else { + free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + } + use_mut(p); +} + +int main() { + int local = 5; + int *owned oriPtr = safe_malloc(0); + test1(); + test2(&mut local); + test3(); + test4(); + test5(); + test6(oriPtr); + test7(); + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow/return_borrow.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow/return_borrow.cbs new file mode 100644 index 0000000000000000000000000000000000000000..fcce6caedaabf3504b68eadcdc5258d25c92557f --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow/return_borrow.cbs @@ -0,0 +1,132 @@ +// RUN: %clang_cc1 -verify %s + +T* owned safe_malloc(T value); +void free_owned(T* owned p); +void free(void*); + +int *borrow test1(int *borrow p) { + int local = 42; + return &mut local; //expected-error{{Return value cannot be a borrow from local variable 'local'}} +} + +int *borrow test2(int *borrow p) { + int local = 42; + p = &mut local; + return p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} +} + +int *borrow test3(int *borrow p, int a) { + return &mut a; //expected-error{{Return value cannot be a borrow from local variable 'a'}} +} + +int *borrow test4(int *borrow p, int a) { + p = &mut a; + return p; //expected-error{{Return value cannot be a borrow from local variable 'a'}} +} + +int *borrow test5(int *borrow p) { + int local = 5; + p = &mut local; + int* borrow p1 = &mut* p; + return p1; //expected-error{{Return value cannot be a borrow from local variable 'local'}} +} + +int *borrow test6(int *borrow p) { + int local = 5; + p = &mut local; + return &mut* p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} +} + +int* borrow test7(int* borrow p) { // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + // expected-note@-1 {{previous borrow is here}} + int *owned oriPtr = safe_malloc(0); + p = &mut * oriPtr; + free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + return p; /* expected-error {{borrow pointer variable '_ReturnVar' lives longer than target variable}} + expected-error {{Return value cannot be a borrow from local variable 'oriPtr'}} */ +} + +int* borrow test8(int *owned oriPtr, int* borrow p) { // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + // expected-note@-1 {{previous borrow is here}} + p = &mut * oriPtr; + free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + return p; /* expected-error {{borrow pointer variable '_ReturnVar' lives longer than target variable}} + expected-error {{Return value cannot be a borrow from local variable 'oriPtr'}} */ +} + +int *borrow test9(int *borrow p) { + int local = 42; + if (local > 0) { + p = &mut local; + return &mut *p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + } else + return p; +} + +int *borrow test10(int *borrow p) { + int local = 42; + if (local > 0) + return &mut local; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + else { + p = &mut local; + return p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + } +} + +int *borrow test11(int *borrow p) { + int local = 42; + if (local > 0) { + int local2 = 42; + if (local2 > 0) + return &mut local2; //expected-error{{Return value cannot be a borrow from local variable 'local2'}} + else + return p; + } else { + int local3 = 42; + if (local3 > 0) + return &mut local3; //expected-error{{Return value cannot be a borrow from local variable 'local3'}} + else + return p; + } +} + +int* borrow test12(int* borrow p) { + int local = 42; + int *owned oriPtr = safe_malloc(0); + if (local > 0) { + free_owned(oriPtr); + return &mut * oriPtr; /* expected-error{{Return value cannot be a borrow from local variable 'oriPtr'}} + expected-error{{borrow pointer variable '_ReturnVar' lives longer than target variable}} */ + } + free_owned(oriPtr); + return p; +} + +int* borrow test13(int* borrow p) { + int *owned oriPtr = safe_malloc(0); + if (1) { + free_owned(oriPtr); + return &mut * oriPtr; /* expected-error{{Return value cannot be a borrow from local variable 'oriPtr'}} + expected-error{{borrow pointer variable '_ReturnVar' lives longer than target variable}} */ + } + return &mut * oriPtr; //Dead Code +} + +int main() { + int local = 5; + int *owned oriPtr = safe_malloc(0); + test1(&mut local); + test2(&mut local); + test3(&mut local, 5); + test4(&mut local, 5); + test5(&mut local); + test6(&mut local); + test7(&mut local); + test8(oriPtr, &mut local); + test9(&mut local); + test10(&mut local); + test11(&mut local); + test12(&mut local); + test13(&mut local); + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_arr_freeze/borrow_arr_freeze.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_arr_freeze/borrow_arr_freeze.cbs new file mode 100644 index 0000000000000000000000000000000000000000..df64e655a277cf9f5b9da14f030b1334dfab72bd --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_arr_freeze/borrow_arr_freeze.cbs @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -ast-dump -verify %s + +void test1(int *p) {} + +void test_freeze_arrow_var1() { + int arr[10] = {0}; + int * borrow c = &mut arr[1]; // expected-note {{previous borrow is here}} + // expected-note@-1 {{previous borrow is here}} + // expected-note@-2 {{previous borrow is here}} + // expected-note@-3 {{previous borrow is here}} + // expected-note@-4 {{previous borrow is here}} + int a = arr[1]; // expected-error {{Can not read 'arr' because be mut borrowed}} + int b = arr[2]; // expected-error {{Can not read 'arr' because be mut borrowed}} + arr[1] = 2; // expected-error {{Can not modify 'arr' because be borrowed}} + arr[2] = 2; // expected-error {{Can not modify 'arr' because be borrowed}} + test1(arr); // expected-error {{Can not modify 'arr' because be borrowed}} + int arr2[10] = {}; + arr2[arr[1]] = 5; + int m = *c; +} + +void test2(int p[10][10]) {} + +void test_freeze_arrow_var2() { + int arr[10][10] = {0}; + int * borrow k = &mut arr[1][1]; // 当前借用二级指针生命周期没有,错误无法触发 + int a = arr[1][1]; // 不允许读e成员 + int b = arr[1][2]; // 不允许读e成员 + int c = arr[2][2]; // 不允许读e成员 + test1(arr[1]); // 不允许读写e成员 + arr[1][1] = 2; // error 不允许写e成员 + arr[1][2] = 2; // error 不允许写e成员 + arr[2][2] = 2; // error 不允许写e成员 + test2(arr); // 不允许读写e + int m = *k; +} + +void test_freeze_arrow_var3() { + int arr[10] = {0}; + int * borrow c = &mut arr[1]; + int a = arr[1]; + int b = arr[2]; + arr[1] = 2; + arr[2] = 2; + test1(arr); +} + +void test_freeze_arrow_var4() { + int arr[10][10] = {0}; + int * borrow k = &mut arr[1][1]; + int a = arr[1][1]; + int b = arr[1][2]; + int c = arr[2][2]; + test1(arr[1]); + arr[1][1] = 2; + arr[1][2] = 2; + arr[2][2] = 2; + test2(arr); +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_ident_freeze/borrow_ident_freeze.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_ident_freeze/borrow_ident_freeze.cbs new file mode 100644 index 0000000000000000000000000000000000000000..38cfd8fbe8a2aa8ce1e82fde2cdb1a830fa8c025 --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_ident_freeze/borrow_ident_freeze.cbs @@ -0,0 +1,177 @@ +// RUN: %clang_cc1 -ast-dump -verify %s + +void test_freeze_ident1() { + int a = 1; + int * borrow c = &mut a; + // we can modify "a" when c life time is end; + a = 2; +} + +void test_freeze_ident2() { + int a = 1; + int * borrow c = &mut a; // expected-note {{previous borrow is here}} + a = 2; // expected-error {{Can not modify 'a' because be borrowed}} + int d = *c; +} + +void test_freeze_ident2_0() { + int a = 1; + int * borrow c = &mut a; // expected-note {{previous borrow is here}} + int m = a; // expected-error {{Can not read 'a' because be mut borrowed}} + int d = *c; +} + +void test_freeze_ident3() { + int a = 1; + int b = 2; + int * borrow c = &mut a; + // "c" life time is end at before, and reactivate at after; + a = 2; + c = &mut b; +} + +void test_freeze_ident4() { + int a = 1; + int * borrow c = &mut a; + int * borrow d = &mut a; +} + +void test_freeze_ident5() { + int a = 1; + int * borrow c = &mut a; + int * borrow d = &mut a; + int e = *c; +} + +void test_freeze_ident6() { + int a = 1; + int * borrow c = &mut a; + int * borrow d = &mut a; + int e = *d; +} + +void test_freeze_ident6_0() { + int a = 1; + int * borrow c = &mut a; + int * borrow d = &mut a; + int e = *d; + int f = *c; +} + +void test_freeze_ident6_1() { + int a = 1; + int * borrow c = &mut a; + int * borrow d = &mut a; // expected-note {{previous borrow is here}} + int e = *c; // expected-error {{There should be at most one mutable borrow targeting to 'c' at the same time}} + int f = *d; +} + +void test_freeze_ident6_2() { + int a = 1; + int * borrow c = &mut a; + int * borrow d = c; // expected-note {{previous borrow is here}} + int e = *c; // expected-error {{There should be at most one mutable borrow targeting to 'c' at the same time}} + int f = *d; +} + +void use(int * borrow a) {} + +void test_freeze_ident6_3() { + int a = 1; + int * borrow c = &mut a; + int * borrow d = c; // expected-note {{previous borrow is here}} + int e = *c; // expected-error {{There should be at most one mutable borrow targeting to 'c' at the same time}} + use(d); +} + +void test_freeze_ident7() { + int a = 1; + int * borrow c = &mut a; + const int * borrow d = &const a; +} + +void test_freeze_ident8() { + int a = 1; + int * borrow c = &mut a; + const int * borrow d = &const a; + int e = *c; +} + +void test_freeze_ident9() { + int a = 1; + int * borrow c = &mut a; + const int * borrow d = &const a; + int e = *d; +} + +void test_freeze_ident10() { + int a = 1; + int * borrow c = &mut a; + const int * borrow d = &const a; + int b = *d; + int e = *c; +} + +void const_use(const int * borrow a) {}; + +void test_freeze_ident11() { + int a = 1; + int * borrow c = &mut a; + const int * borrow d = &const a; // expected-note {{previous borrow is here}} + int b = *c; // expected-error {{There should be at most one mutable borrow targeting to 'c' at the same time}} + const_use(d); +} + +void test_freeze_ident12() { + int a = 1; + const int * borrow c = &const a; + const int * borrow d = &const a; + int b = *c; + int e = *d; +} + +void test_freeze_ident13() { + int a = 1; + const int * borrow c = &const a; // expected-note {{previous borrow is here}} + a = 2; // expected-error {{Can not modify 'a' because be borrowed}} + int b = *c; +} + +int test_freeze_ident14(){ + int a = 1; + int b = 2; + int *c, *d; + c = &a; + d = &b; + int * borrow e = &mut *c; + int m = *d; + use(e); + return 0; +} + +int test_freeze_ident15(int * borrow m){ + int a = 1; + int b = 2; + int *c, *d; + c = &a; + d = &b; + int * borrow e = &mut *c; + int * borrow f = &mut *c; // expected-note {{previous borrow is here}} + int g = f == e ? *f : *e; // expected-error {{There should be at most one mutable borrow targeting to 'e' at the same time}} + use(f); + use(e); + return 0; +} + +struct S { + int a; + int * borrow b; +}; + +int test_freeze_ident16() { + int local = 1; + struct S s = { .a = local, .b = &mut local }; // expected-note {{previous borrow is here}} + // expected-error@-1 {{Can not read 'local' because be mut borrowed}} + use(s.b); + return 0; +} diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_star_freeze/borrow_star_freeze.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_star_freeze/borrow_star_freeze.cbs new file mode 100644 index 0000000000000000000000000000000000000000..b450b3017def303f7c2471083c1949759cf2a191 --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_star_freeze/borrow_star_freeze.cbs @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -ast-dump -verify %s + +void test1(int *p) {} + +void test_freeze_star_var1() { + int a = 1; + int * b = &a; + int * borrow c = &mut *b; // expected-note {{previous borrow is here}} + a = 2; // ok; + b = &a; // expected-error {{Can not modify 'b' because be borrowed}} + int m = *c; +} + +void test_freeze_star_var2() { + int a = 1; + int * b = &a; + int * borrow c = &mut *b; + a = 2; // ok; + b = &a; // ok +} + +void test_freeze_star_var3() { + int a = 1; + int * b = &a; + int * borrow c = &mut *b; + a = 2; // ok; + int m = *c; + b = &a; // ok +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs new file mode 100644 index 0000000000000000000000000000000000000000..19b68dbf8b85692ed14a443c50b93033645d720e --- /dev/null +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs @@ -0,0 +1,100 @@ +// RUN: %clang %s -o %t.output +// RUN: %t.output +// expected-no-diagnostics + +#include +void use_mut(int *borrow p) {} +void use_immut(const int *borrow p) {} + +T* owned safe_malloc(T value) { + T * p = (T *) malloc( sizeof(T) ); + *p = value; + return (T* owned)p; +} + +void free_owned(T* owned p) { + free( (T*)p ); +} + +void test1() { + int *oriPtr = malloc(sizeof(int)); + int *borrow p1 = &mut * oriPtr; + const int *borrow p2 = &const * oriPtr; + int *borrow p3 = &mut * oriPtr; + use_mut(p3); + use_immut(p2); + use_mut(p1); + free(oriPtr); +} + +void test2() { + int *owned oriPtr = safe_malloc(0); + const int *borrow p1 = &const * oriPtr; + const int *borrow p2 = &const * oriPtr; + use_immut(p1); + use_immut(p2); + free_owned(oriPtr); +} + +void test3() { + int a = 5; + int b = 5; + int *owned oriPtr = safe_malloc(0); + int *borrow p1 = &mut a; + int *borrow p2 = &mut b; + if (a > 0) + p1 = &mut * oriPtr; + else + p2 = &mut * oriPtr; + use_mut(p1); + use_mut(p2); + free_owned(oriPtr); +} + +void test4() { + int a = 5; + int b = 5; + int *owned oriPtr = safe_malloc(0); + int *borrow p1 = &mut a; + int *borrow p2 = &mut b; + if (1) { + p2 = &mut * oriPtr; + p1 = &mut * oriPtr; + } else { // Dead Code + p1 = &mut * oriPtr; + p2 = &mut * oriPtr; + } + use_mut(p1); + use_mut(p2); + free_owned(oriPtr); +} + + +void test5() { + int local = 5; + int *borrow p = &mut local; + int *borrow p1 = &mut *p; + use_mut(p1); + use_mut(&mut *p); +} + +int* borrow use_borrow_and_return(int *borrow p) { + return p; +} +void test6() { + int local = 42; + int *borrow p1 = &mut local; + int *borrow p2 = use_borrow_and_return(p1); + use_mut(p2); + use_mut(p1); +} + +int main() { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_borrow.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_borrow.cbs new file mode 100644 index 0000000000000000000000000000000000000000..cc280d4ef524dc0504a483927257e36f0ae1d1c2 --- /dev/null +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_borrow.cbs @@ -0,0 +1,98 @@ +// RUN: %clang %s -o %t.output +// RUN: %t.output +// expected-no-diagnostics + +T* owned safe_malloc(T value) { + T * p = (T *) malloc( sizeof(T) ); + *p = value; + return (T* owned)p; +} + +void free_owned(T* owned p) { + free( (T*)p ); +} + +int g = 5; +const int *borrow test1(const int *borrow p) { + return &const g; +} + +int *borrow test2(int *borrow p) { + return p; +} + +const int *borrow test3(const int *borrow p) { + p = &const g; + return p; +} + +int *borrow test4(int *borrow p) { + int local = 5; + int *oriPtr = &local; + return &mut * oriPtr; +} + +int *borrow test5(int *borrow p) { + int local = 5; + int *oriPtr = &local; + p = &mut * oriPtr; + return p; +} + +int *borrow test6(int *borrow p, int* a) { + return &mut * a; +} + +int *borrow test7(int *borrow p, int* a) { + p = &mut * a; + return p; +} + +int *borrow test8(int *borrow p) { + int* borrow p1 = &mut * p; + return p1; +} + +int *borrow test9(int *borrow p) { + return &mut * p; +} + +int* borrow test10(int *borrow p, int* a) { + if (a) { + return &mut * a; + } + return p; +} + +int* borrow test11(int *borrow p, int a) { + if (1) { + return p; + } + return &mut a; // Dead Code, this ReturnStmt is unreachable. +} + +int* borrow test12(int* borrow p) { + int *owned oriPtr = safe_malloc(0); + if (1) { + free_owned(oriPtr); + return p; + } + return &mut * oriPtr; // Dead Code, this ReturnStmt is unreachable. +} + +int main() { + int local = 5; + test1(&const local); + test2(&mut local); + test3(&const local); + test4(&mut local); + test5(&mut local); + test6(&mut local, &local); + test7(&mut local, &local); + test8(&mut local); + test9(&mut local); + test10(&mut local, &local); + test11(&mut local, 5); + test12(&mut local); + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/virtual_target_live_longer/virtual_target_live_longer.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/virtual_target_live_longer/virtual_target_live_longer.cbs new file mode 100644 index 0000000000000000000000000000000000000000..5d67f51b5d9b99b0ed146d101691b2fe8349f1d6 --- /dev/null +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/virtual_target_live_longer/virtual_target_live_longer.cbs @@ -0,0 +1,32 @@ +// RUN: %clang %s -o %t.output +// RUN: %t.output +// expected-no-diagnostics +#include +void use_mut(int *borrow p) {} + +void test1() { + int *a = (int *)malloc(sizeof(int)); + int *borrow p = &mut * a; + use_mut(p); + free(a); +} + +void test2(int* a) { + int *borrow p = &mut * a; + use_mut(p); + free(a); +} + +void test3(int *borrow p) { + int *borrow p1 = &mut * p; + use_mut(p1); +} + +int main() { + int local = 5; + int *a = (int *)malloc(sizeof(int)); + test1(); + test2(a); + test3(&mut local); + return 0; +} \ No newline at end of file