From 812bc2d8863fb4d4380d1261b106ffa5cca569ae Mon Sep 17 00:00:00 2001 From: liuxinyi Date: Tue, 23 Jul 2024 14:48:33 +0800 Subject: [PATCH 1/6] 1.calculate all paths for a cfg 2.calculate NLL for global,local variables and parameter in a path 3.check borrow lifetime shorter than its target 4.check return value, cannot return borrow from local variables --- .../clang/Analysis/Analyses/BSCBorrowCheck.h | 141 +++++ .../clang/Analysis/Analyses/BSCOwnership.h | 4 +- .../clang/Basic/BSC/DiagnosticBSCSemaKinds.td | 6 + clang/include/clang/Basic/Diagnostic.h | 5 + clang/lib/Analysis/BSCBorrowCheck.cpp | 596 ++++++++++++++++++ clang/lib/Analysis/CMakeLists.txt | 1 + clang/lib/Basic/Diagnostic.cpp | 1 + clang/lib/Sema/BSC/SemaBSCOwnership.cpp | 10 +- clang/lib/Sema/Sema.cpp | 2 +- .../at_most_one_mut_borrow.cbs | 127 ++++ .../borrow_live_longer/borrow_live_longer.cbs | 85 +++ .../return_borrow/return_borrow.cbs | 130 ++++ .../at_most_one_mut_borrow.cbs | 100 +++ .../return_borrow/return_borrow.cbs | 98 +++ .../virtual_target_live_longer.cbs | 32 + 15 files changed, 1334 insertions(+), 4 deletions(-) create mode 100644 clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h create mode 100644 clang/lib/Analysis/BSCBorrowCheck.cpp create mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs create mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer/borrow_live_longer.cbs create mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow/return_borrow.cbs create mode 100644 clang/test/BSC/Positive/Ownership/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs create mode 100644 clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_borrow.cbs create mode 100644 clang/test/BSC/Positive/Ownership/borrow_check_rules/virtual_target_live_longer/virtual_target_live_longer.cbs diff --git a/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h b/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h new file mode 100644 index 000000000000..3c2cb41ac949 --- /dev/null +++ b/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h @@ -0,0 +1,141 @@ +//===- 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 { +struct NonLexicalLifetimeRange { + // The first element of pair is begin, the second is end. + std::pair Range; + + // Record the targets of a borrow during this NonLexicalLifetimeSegment. + // For owned and other variables, Targets will be void. + VarDecl* Target; + + NonLexicalLifetimeRange() {} + + // These two constructors are for borrow variables, which have binding targets. + NonLexicalLifetimeRange(std::pair range, VarDecl* target) + : Range(range), Target(target) {} + NonLexicalLifetimeRange(unsigned begin, unsigned end, VarDecl* target) + : Range(std::pair(begin, end)), Target(target) {} + + // This constructor is for no-borrow variables, which don't have binding targets. + NonLexicalLifetimeRange(unsigned begin, unsigned end) + : Range(std::pair(begin, end)), Target(nullptr) {} + + bool operator==(const NonLexicalLifetimeRange &Other) const { + return Range.first == Other.Range.first && + Range.second == Other.Range.second && + Target == Other.Target; + } +}; + +// 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; + +enum BorrowCheckDiagKind { + LiveLonger, + AtMostOneMutBorrow, + ReturnLocal, +}; + +struct BorrowCheckDiagInfo { + std::string Name; + BorrowCheckDiagKind Kind; + SourceLocation Loc; + + BorrowCheckDiagInfo(std::string Name, BorrowCheckDiagKind Kind, SourceLocation Loc) + : Name(Name), Kind(Kind), Loc(Loc) {} + + 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; + break; + case ReturnLocal: + S.Diag(DI.Loc, diag::err_return_value_borrow_local) << DI.Name; + 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 abef07863fec..42a2e5bdcf83 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 252a24fff977..3942c507d3d2 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -125,4 +125,10 @@ 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'">; diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index 07b67f7d7f0d..bf96300ec317 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 000000000000..7c0795d301dd --- /dev/null +++ b/clang/lib/Analysis/BSCBorrowCheck.cpp @@ -0,0 +1,596 @@ +//===- 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 llvm::DenseMap& ParamTarget, + unsigned NumElements); + void BorrowCheckForPath(const CFGPath &Path, BorrowCheckDiagReporter &reporter, + const NonLexicalLifetime& NLLForAllVars, unsigned NumElements); + void BuildParamTarget(llvm::DenseMap& 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; + unsigned NumElements; +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 VisitScopeEnd(VarDecl *VD); + void VisitScopeBegin(VarDecl *VD); + void HandleNLLAfterTraversing(const llvm::DenseMap& ParamTarget); + +private: + void MarkBorrowLastUseLoc(Expr* E); + void VisitInitForTargets(Expr *InitE, llvm::SmallVector& TargetSet); + void UpdateNLLWhenTargetFound(VarDecl* OldTarget, const llvm::SmallVector& NewTargets); +}; + +class BorrowRuleChecker : public StmtVisitor { + BorrowCheckDiagReporter &reporter; + +public: + NonLexicalLifetime NLLForAllVars; + 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. + // Then BorrowTargetMap will be: + // local1 : [ (p1, 5, 10), (p3, 6, 8) ], local2 : [ (p2, 3, 4), (p3, 6, 8) ] + llvm::DenseMap>> BorrowTargetMap; + void BuildBorrowTargetMap(); + void CheckBorrowNLLShorterThanTarget(); + void CheckLifetimeOfBorrowReturnValue(unsigned NumElements); + // void VisitDeclStmt(DeclStmt *DS); + // void VisitBinaryOperator(BinaryOperator *BO); + // void VisitDeclRefExpr(DeclRefExpr *DRE); +}; + +void BorrowRuleChecker::BuildBorrowTargetMap() { + for (auto NLLOfVar : NLLForAllVars) { + VarDecl* VD = NLLOfVar.first; + NonLexicalLifetimeOfVar NLLRangesOfVar = NLLOfVar.second; + for (auto NLLRange : NLLRangesOfVar) { + if (NLLRange.Target) + BorrowTargetMap[NLLRange.Target].push_back(std::tuple(VD, NLLRange.Range.first, NLLRange.Range.second)); + } + } + // DEBUG PRINT + for (auto v : BorrowTargetMap) { + llvm::outs() << v.first->getNameAsString() << ": [ "; + for (auto w : v.second) { + VarDecl* V = std::get<0>(w); + llvm::outs() << "(" << V->getNameAsString() << ", " << std::get<1>(w) << ", " << std::get<2>(w) << "), "; + } + 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; + unsigned TargetVDBegin = NLLForAllVars[TargetVD][0].Range.first; + unsigned TargetVDEnd = NLLForAllVars[TargetVD][0].Range.second; + for (auto Borrow : BorrowAndTarget.second) { + VarDecl* BorrowVD = std::get<0>(Borrow); + QualType BorrowQT = BorrowVD->getType(); + if (BorrowQT.isBorrowQualified()) { // Naked pointer also have target, but we don't need to consider it. + unsigned BorrowVDBegin = std::get<1>(Borrow); + unsigned BorrowVDEnd = std::get<2>(Borrow); + if (BorrowVDBegin <= TargetVDBegin || BorrowVDEnd >= TargetVDEnd) { + BorrowCheckDiagInfo DI(BorrowVD->getNameAsString(), LiveLonger, 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][0]; + unsigned TargetNLLBegin = TargetNLLRange.Range.first; + unsigned TargetNLLEnd = TargetNLLRange.Range.second; + if (TargetNLLBegin > 0 || TargetNLLEnd < NumElements + 3) { + BorrowCheckDiagInfo DI(Range.Target->getNameAsString(), ReturnLocal, NLLOfVar.first->getLocation()); + reporter.addDiagInfo(DI); + } + } + } + } +} + +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()) { + if (DeclRefExpr * DRE = dyn_cast(BO->getLHS())) { + if (VarDecl* VD = dyn_cast(DRE->getDecl())) { + if (VD->getType().isBorrowQualified()) { + Expr *Init = BO->getRHS(); + llvm::SmallVector Targets; + VisitInitForTargets(Init, Targets); + // When we find the target of a borrow, + // we should update previous target in NLLForAllVars. + UpdateNLLWhenTargetFound(VD, Targets); + for (VarDecl* Target : Targets) + NLLForAllVars[VD].push_back( + NonLexicalLifetimeRange(CurElemID, FoundVars.count(VD) ? FoundVars[VD] : CurElemID, Target)); + FoundVars.erase(VD); + } + } + } + } +} + +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; + } +} + +void NLLCalculator::VisitReturnStmt(ReturnStmt *RS) { + Expr *RV = RS->getRetValue(); + if (!RV) + return; + // For return stmt `return p;` or `return &mut* p`, + // we think borrow pointer `p` is used. + MarkBorrowLastUseLoc(RV); + QualType ReturnQT = RV->getType(); + if (ReturnQT.isBorrowQualified()) { + // 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 (VarDecl* Target : Targets) + NLLForAllVars[ReturnVD].push_back(NonLexicalLifetimeRange(CurElemID, CurElemID, Target)); + } +} + +void NLLCalculator::VisitCallExpr(CallExpr *CE) { + // For function call `use(p)` or `use(&mut* p)`, + // we think borrow pointer `p` is used here. + for (auto it = CE->arg_begin(), ei = CE->arg_end(); it != ei; ++it) + MarkBorrowLastUseLoc(*it); +} + +void NLLCalculator::VisitUnaryOperator(UnaryOperator *UO) { + // For pointer deref `*p`, + // we think borrow pointer `p` is used here. + if (UO->getOpcode() == UO_Deref) + MarkBorrowLastUseLoc(UO->getSubExpr()); +} + +void NLLCalculator::VisitDeclStmt(DeclStmt *DS) { + for (auto *D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + if (VD->getType().isBorrowQualified()) { + Expr *Init = VD->getInit(); + llvm::SmallVector Targets; + VisitInitForTargets(Init, Targets); + // When we find the target of a borrow, + // we should update previous target in NLLForAllVars. + UpdateNLLWhenTargetFound(VD, Targets); + for (VarDecl* Target : Targets) + NLLForAllVars[VD].push_back( + NonLexicalLifetimeRange(CurElemID, FoundVars.count(VD) ? FoundVars[VD] : CurElemID, Target)); + FoundVars.erase(VD); + } + } + } +} + +void NLLCalculator::MarkBorrowLastUseLoc(Expr* E) { + DeclRefExpr* DRE = nullptr; + if (auto ICE = dyn_cast(E)) { + DRE = dyn_cast(ICE->getSubExpr()); + } else if (auto UO = dyn_cast(E)) { + if (UO->getOpcode() == UO_AddrMutDeref || UO->getOpcode() == UO_AddrConstDeref) + DRE = dyn_cast(UO->getSubExpr()); + } + + if (DRE) + if (VarDecl* VD = dyn_cast(DRE->getDecl())) + if (VD->getType().isBorrowQualified() && !FoundVars.count(VD)) + FoundVars[VD] = CurElemID; +} + + +void NLLCalculator::VisitInitForTargets(Expr *InitE, llvm::SmallVector& Targets) { + if (auto UO = dyn_cast(InitE)) { + if (UO->getOpcode() == UO_AddrMut || UO->getOpcode() == UO_AddrConst + || UO->getOpcode() == UO_AddrMutDeref || UO->getOpcode() == UO_AddrConstDeref) { + if (auto* DRE = dyn_cast(UO->getSubExpr())) { + // int* borrow p = &mut local; local is DeclRefExpr. + VarDecl* IVD = dyn_cast(DRE->getDecl()); + Targets.push_back(IVD); + } else if (auto ASE = dyn_cast(UO->getSubExpr())) { + // int* borrow p = &mut arr[0]; arr[0] is ArraySubscriptExpr. + VisitInitForTargets(ASE->getBase(), Targets); + } else if (auto ICE = dyn_cast(UO->getSubExpr())) { + // int* borrow p = &mut *q; q is ImplicitCastExpr. + VisitInitForTargets(ICE, Targets); + } + } + } else if (auto ICE = dyn_cast(InitE)) { + // ImplicitCastExpr: int* borrow p2 = p1; + // p2 refers to p1, if p1 is borrow variable, we add p1 to Targets; + if (auto* DRE = dyn_cast(ICE->getSubExpr())) { + VarDecl* IVD = dyn_cast(DRE->getDecl()); + Targets.push_back(IVD); + } + } else if (auto CE = dyn_cast(InitE)) { + // CallExpr: int* borrow p3 = foo(&mut local1, &mut local2); + // the lifetime of p3 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); + } + } +} + +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(VirtualVD); + UpdateNLLWhenTargetFound(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(VarDecl* 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(it->Range); + it = NLLRanges.erase(it); + } else + ++it; + } + // Build new ranges targeting to NewTarget and insert them to NLLRanges. + for (std::pair RangeNeedUpdate : RangesNeedUpdate) { + for (VarDecl* NewTarget : NewTargets) { + NonLexicalLifetimeRange NewRange(RangeNeedUpdate, NewTarget); + 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 llvm::DenseMap& ParamTarget) { + // Handle parameter + for (auto Param : ParamTarget) { + ParmVarDecl* PVD = Param.first; + QualType PQT = PVD->getType(); + // Handle borrow or naked pointer parameter which have virtual ParentVar. + if (PQT->isPointerType() && !PQT.isOwnedQualified()) { + // Update previous NLL whose target is PVD to virtual ParentVar + llvm::SmallVector VirtualTarget; + VirtualTarget.push_back(Param.second); + UpdateNLLWhenTargetFound(PVD, VirtualTarget); + // Add NLL for virtual ParentVar. + NLLForAllVars[Param.second].push_back(NonLexicalLifetimeRange(0, NumElements + 3)); + // Add NLL for param with target. + if (PQT.isBorrowQualified()) // PVD is borrow pointer + NLLForAllVars[PVD].push_back( + NonLexicalLifetimeRange(1, FoundVars.count(PVD) ? FoundVars[PVD] : 1, Param.second)); + else // PVD is naked pointer + NLLForAllVars[PVD].push_back( + NonLexicalLifetimeRange(1, NumElements + 2, Param.second)); + } 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 llvm::DenseMap& 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() << " " << v.Range.first << "->" << v.Range.second; + if (v.Target) + llvm::outs() << " " << v.Target->getNameAsString() << "\n"; + else + llvm::outs() << "\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(llvm::DenseMap& ParamTarget) { + for (ParmVarDecl *PVD : fd.parameters()) { + if (PVD->getType()->isPointerType() && !PVD->getType().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); + ParamTarget[PVD] = VD; + } else + ParamTarget[PVD] = nullptr; + } +} + +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); +} + +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); + + llvm::DenseMap 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 6da0675f6bdc..978c1648df8e 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 0d1fad4f29e5..1a39cffebf21 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 726836e40a7f..1daa964ec707 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 1ecb8fc9fb3c..c6f64ac1cb5f 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 000000000000..65d0682999f1 --- /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,127 @@ +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; + use_mut(p1); // error + use_mut(p2); +} + +void test2() { + int *owned oriPtr = safe_malloc(0); + int *borrow p1 = &mut *oriPtr; + int *borrow p2 = &mut *oriPtr; + use_mut(p1); // error + 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; + use_mut(p); // error + use_mut(p1); // error + use_immut(p2); +} + +void test5() { + int local = 5; + int *borrow p1 = &mut local; + const int *borrow p2 = &const local; + use_mut(p1); //error + use_immut(p2); +} + +void test6(int* borrow p) { + int local = 5; + p = &mut local; + const int *borrow p1 = &const *p; + use_mut(p); //error + use_immut(p1); +} + +void test7(int* borrow p) { + int *borrow p1 = p; + use_mut(p); // error, p is frozen + use_mut(p1); +} + +void test8() { + int arr[5]; + int *borrow p = &mut arr[0]; + int *borrow p1 = &mut arr[1]; + use_mut(p); // error + 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; + use_two_borrow(p1, p2); // error +} + +void test10() { + int local = 5; + int *borrow p1 = &mut local; + const int *borrow p2 = &const local; + 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); //error only in `if` branch + use_immut(p2); + free_owned(oriPtr); +} + +void test11() { + int local = 5; + int *borrow p1 = &mut local; + const int *borrow p2 = &const local; + 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); // error only in `if` branch + 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 000000000000..a61b469be7cc --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer/borrow_live_longer.cbs @@ -0,0 +1,85 @@ +// 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}} + free_owned(oriPtr); + use_mut(p); +} + +void test6(int *owned oriPtr) { + int *borrow p = &mut * oriPtr; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + free_owned(oriPtr); + 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}} + if (a > 0) { + free_owned(oriPtr); + } else { + free_owned(oriPtr); + } + 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 000000000000..ef9d748147ae --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow/return_borrow.cbs @@ -0,0 +1,130 @@ +// 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}} + int *owned oriPtr = safe_malloc(0); + p = &mut * oriPtr; + free_owned(oriPtr); + 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}} + p = &mut * oriPtr; + free_owned(oriPtr); + 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/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 000000000000..19b68dbf8b85 --- /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 000000000000..cc280d4ef524 --- /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 000000000000..65413a6a5cca --- /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; + free(a); // We assume the virtual target of naked pointer live longer than all local variables + use_mut(p); +} + +void test2(int* a) { + int *borrow p = &mut * a; + free(a); + use_mut(p); +} + +void test3(int *borrow p) { // We assume the virtual target of borrow parameter live longer than all local variables + 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 -- Gitee From 66dc0bbfb79c40fd11dc016578f9e8947c3c7800 Mon Sep 17 00:00:00 2001 From: wuhuiquan Date: Tue, 30 Jul 2024 10:03:10 +0800 Subject: [PATCH 2/6] [Huawei] check be borrowed expression --- .../clang/Analysis/Analyses/BSCBorrowCheck.h | 18 +- .../clang/Basic/BSC/DiagnosticBSCSemaKinds.td | 6 +- clang/lib/Analysis/BSCBorrowCheck.cpp | 233 +++++++++++++++++- .../at_most_one_mut_borrow.cbs | 45 ++-- .../borrow_live_longer/borrow_live_longer.cbs | 12 +- .../return_borrow/return_borrow.cbs | 6 +- .../borrow_arr_freeze/borrow_arr_freeze.cbs | 57 +++++ .../borrow_ident_freeze.cbs | 133 ++++++++++ 8 files changed, 477 insertions(+), 33 deletions(-) create mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_arr_freeze/borrow_arr_freeze.cbs create mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_ident_freeze/borrow_ident_freeze.cbs diff --git a/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h b/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h index 3c2cb41ac949..b503feda1282 100644 --- a/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h +++ b/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h @@ -68,15 +68,20 @@ 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) - : Name(Name), Kind(Kind), Loc(Loc) {} + 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 && @@ -118,10 +123,19 @@ private: 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; diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td index 3942c507d3d2..b09088df53d6 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -131,4 +131,8 @@ 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/lib/Analysis/BSCBorrowCheck.cpp b/clang/lib/Analysis/BSCBorrowCheck.cpp index 7c0795d301dd..3fc51ba0c703 100644 --- a/clang/lib/Analysis/BSCBorrowCheck.cpp +++ b/clang/lib/Analysis/BSCBorrowCheck.cpp @@ -127,6 +127,9 @@ class BorrowRuleChecker : public StmtVisitor { public: NonLexicalLifetime NLLForAllVars; + unsigned CurElemID = 0; + enum Operation { None, Read, Write }; + Operation Op = None; BorrowRuleChecker(BorrowCheckDiagReporter &reporter, const NonLexicalLifetime& NLLForVars) : reporter(reporter), NLLForAllVars(NLLForVars) {} @@ -139,14 +142,237 @@ public: // Then BorrowTargetMap will be: // local1 : [ (p1, 5, 10), (p3, 6, 8) ], local2 : [ (p2, 3, 4), (p3, 6, 8) ] llvm::DenseMap>> 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 VisitDeclStmt(DeclStmt *DS); - // void VisitBinaryOperator(BinaryOperator *BO); - // void VisitDeclRefExpr(DeclRefExpr *DRE); + void CheckValIsLegalUse(VarDecl *VD, SourceLocation Loc); + VarDecl *FindTargetFromActiveBorrowTargetMap(VarDecl *VD); + void VisitArraySubscriptExpr(ArraySubscriptExpr *E); + void VisitBinaryOperator(BinaryOperator *BO); + void VisitCallExpr(CallExpr *CE); + void VisitCastExpr(CastExpr *E); + void VisitDeclRefExpr(DeclRefExpr *DRE); + void VisitDeclStmt(DeclStmt *DS); + void VisitImplicitCastExpr(ImplicitCastExpr *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 (std::get<0>(*it) == VD) { + return v.first; + } + it++; + } + } + } 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 (std::get<0>(*it) == VD) { + return; + } + // immut and mut borrow variables are not allowed to exist + // simultaneously. + if (!std::get<0>(*it)->getType().isConstBorrow()) { + BorrowCheckDiagInfo DI(VD->getNameAsString(), AtMostOneMutBorrow, + Loc, std::get<0>(*it)->getLocation()); + reporter.addDiagInfo(DI); + return; + } + it++; + } + } else { + assert(it != ActiveBorrowTargetMap[TVD].rend()); + if (std::get<0>(*it) == VD) { + return; + } else { + // Only allow used the last alive mut borrow variable. + BorrowCheckDiagInfo DI(VD->getNameAsString(), AtMostOneMutBorrow, Loc, + std::get<0>(*it)->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, std::get<0>(*it)->getLocation()); + reporter.addDiagInfo(DI); + } else { + while (it != ActiveBorrowTargetMap[TVD].rend()) { + // Variables cannot be read when be mut borrowed + if (!std::get<0>(*it)->getType().isConstBorrow()) { + BorrowCheckDiagInfo DI(VD->getNameAsString(), + ReadAfterBeMutBorrowed, Loc, + std::get<0>(*it)->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; + default: + break; + } + Visit(UO->getSubExpr()); + Op = TempOp; +} + +void BorrowRuleChecker::VisitCallExpr(CallExpr *CE) { + Op = Write; + for (auto it = CE->arg_begin(), ei = CE->arg_end(); it != ei; ++it) { + Visit(*it); + } + Op = None; +} + +void BorrowRuleChecker::VisitCastExpr(CastExpr *E) { Visit(E->getSubExpr()); } + +void BorrowRuleChecker::VisitParenExpr(ParenExpr *E) { Visit(E->getSubExpr()); } + +void BorrowRuleChecker::VisitImplicitCastExpr(ImplicitCastExpr *E) { + Visit(E->getSubExpr()); +} +void BorrowRuleChecker::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { + Visit(E->getLHS()); + Visit(E->getRHS()); +} + +void BorrowRuleChecker::VisitBinaryOperator(BinaryOperator *BO) { + if (BO->isAssignmentOp()) { + 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 = None; +} + +void BorrowRuleChecker::VisitDeclStmt(DeclStmt *DS) { + 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 = None; +} + +void BorrowRuleChecker::PushActiveBorrowTargetMap() { + for (auto v : BorrowTargetMap) { + for (auto w : v.second) { + unsigned firstElemId = std::get<1>(w); + if (CurElemID == firstElemId) { + ActiveBorrowTargetMap[v.first].push_back(w); + } + } + } +} + +void BorrowRuleChecker::PopActiveBorrowTargetMap() { + for (auto v : BorrowTargetMap) { + for (auto w : v.second) { + unsigned secondElemId = std::get<2>(w); + 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* VD = NLLOfVar.first; @@ -551,6 +777,7 @@ void BorrowCheckImpl::BorrowCheckForPath(const CFGPath &Path, BorrowCheckDiagRep BRC.CheckBorrowNLLShorterThanTarget(); if (fd.getReturnType().isBorrowQualified()) BRC.CheckLifetimeOfBorrowReturnValue(NumElements); + BRC.CheckBeBorrowedTarget(Path); } void clang::runBorrowCheck(const FunctionDecl &fd, const CFG &cfg, 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 index 65d0682999f1..068a04176975 100644 --- 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 @@ -1,3 +1,5 @@ +// 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); @@ -7,16 +9,16 @@ void free(void*); void test1() { int local = 42; int *borrow p1 = &mut local; - int *borrow p2 = &mut local; - use_mut(p1); // error + 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; - use_mut(p1); // error + 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); } @@ -32,39 +34,40 @@ void test4() { int local = 5; int *borrow p = &mut local; int *borrow p1 = &mut local; - const int *borrow p2 = &const *p; - use_mut(p); // error - use_mut(p1); // error + 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; - use_mut(p1); //error + 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; - use_mut(p); //error + 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; - use_mut(p); // error, p is frozen + 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]; - use_mut(p); // error + 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); } @@ -72,14 +75,14 @@ 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; - use_two_borrow(p1, p2); // error + 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; + const int *borrow p2 = &const local; // expected-note {{previous borrow is here}} int *owned oriPtr = safe_malloc(0); if (local > 0) { p1 = &mut *oriPtr; @@ -88,7 +91,7 @@ void test10() { p2 = &const *oriPtr; p1 = &mut *oriPtr; } - use_mut(p1); //error only in `if` branch + 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); } @@ -96,7 +99,7 @@ void test10() { void test11() { int local = 5; int *borrow p1 = &mut local; - const int *borrow p2 = &const local; + const int *borrow p2 = &const local; // expected-note {{previous borrow is here}} int *owned oriPtr = safe_malloc(0); if (1) { p1 = &mut *oriPtr; @@ -105,7 +108,7 @@ void test11() { p1 = &mut *oriPtr; p2 = &const *oriPtr; } - use_mut(p1); // error only in `if` branch + 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); } 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 index a61b469be7cc..c870705af7ad 100644 --- 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 @@ -49,13 +49,15 @@ void test4() { void test5() { int *owned oriPtr = safe_malloc(0); int *borrow p = &mut * oriPtr; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} - free_owned(oriPtr); + // 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}} - free_owned(oriPtr); + // expected-note@-1 {{previous borrow is here}} + free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} use_mut(p); } @@ -63,10 +65,12 @@ 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); + free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} } else { - free_owned(oriPtr); + free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} } use_mut(p); } 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 index ef9d748147ae..fcce6caedaab 100644 --- 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 @@ -38,16 +38,18 @@ int *borrow test6(int *borrow p) { } 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); + 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); + 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'}} */ } 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 000000000000..ddaa35e3b125 --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_arr_freeze/borrow_arr_freeze.cbs @@ -0,0 +1,57 @@ +// 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 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 000000000000..612293fd83f6 --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_ident_freeze/borrow_ident_freeze.cbs @@ -0,0 +1,133 @@ +// 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 use(int * borrow a) {} + +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}} + use(d); + use(c); +} + +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}} + use(d); + use(c); +} + +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); + use(c); +} + +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; +} -- Gitee From 941dbfaddcd5ca6dfc2692914a79e0b334cca750 Mon Sep 17 00:00:00 2001 From: liuxinyi Date: Thu, 1 Aug 2024 09:15:41 +0800 Subject: [PATCH 3/6] borrow struct NLL --- .../clang/Analysis/Analyses/BSCBorrowCheck.h | 88 +++- clang/lib/Analysis/BSCBorrowCheck.cpp | 483 ++++++++++++++---- 2 files changed, 455 insertions(+), 116 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h b/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h index b503feda1282..1fe821d7a655 100644 --- a/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h +++ b/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h @@ -24,30 +24,65 @@ #include namespace clang { -struct NonLexicalLifetimeRange { - // The first element of pair is begin, the second is end. - std::pair Range; - - // Record the targets of a borrow during this NonLexicalLifetimeSegment. - // For owned and other variables, Targets will be void. - VarDecl* Target; +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 two constructors are for borrow variables, which have binding targets. - NonLexicalLifetimeRange(std::pair range, VarDecl* target) - : Range(range), Target(target) {} - NonLexicalLifetimeRange(unsigned begin, unsigned end, VarDecl* target) - : Range(std::pair(begin, end)), Target(target) {} + // 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) - : Range(std::pair(begin, end)), Target(nullptr) {} + : Begin(begin), End(end) {} - bool operator==(const NonLexicalLifetimeRange &Other) const { - return Range.first == Other.Range.first && - Range.second == Other.Range.second && - Target == Other.Target; + bool operator==(const NonLexicalLifetimeRange &Other) const { + return Begin == Other.Begin && End == Other.End && Kind == Other.Kind && + Target == Other.Target && BorrowFieldPath == Other.BorrowFieldPath; } }; @@ -64,6 +99,25 @@ using NonLexicalLifetimeOfVar = llvm::SmallVector; // 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) {} +}; + +using BorrowTargetMapInfo = llvm::DenseMap>; + enum BorrowCheckDiagKind { LiveLonger, AtMostOneMutBorrow, diff --git a/clang/lib/Analysis/BSCBorrowCheck.cpp b/clang/lib/Analysis/BSCBorrowCheck.cpp index 3fc51ba0c703..61ac05cd8681 100644 --- a/clang/lib/Analysis/BSCBorrowCheck.cpp +++ b/clang/lib/Analysis/BSCBorrowCheck.cpp @@ -32,11 +32,11 @@ public: const FunctionDecl& fd; NonLexicalLifetime CalculateNLLForPath(const CFGPath &Path, - const llvm::DenseMap& ParamTarget, + const llvm::DenseMap& ParamTarget, unsigned NumElements); void BorrowCheckForPath(const CFGPath &Path, BorrowCheckDiagReporter &reporter, const NonLexicalLifetime& NLLForAllVars, unsigned NumElements); - void BuildParamTarget(llvm::DenseMap& ParamTarget); + void BuildParamTarget(llvm::DenseMap& ParamTarget); BorrowCheckImpl(ASTContext& ASTCtx, const FunctionDecl& FD) : Ctx(ASTCtx), fd(FD) {} @@ -96,7 +96,18 @@ class NLLCalculator : public StmtVisitor { // 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. + // 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; @@ -112,14 +123,23 @@ public: 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 llvm::DenseMap& ParamTarget); + void HandleNLLAfterTraversing(const llvm::DenseMap& ParamTarget); private: void MarkBorrowLastUseLoc(Expr* E); - void VisitInitForTargets(Expr *InitE, llvm::SmallVector& TargetSet); - void UpdateNLLWhenTargetFound(VarDecl* OldTarget, const llvm::SmallVector& NewTargets); + void MarkBorrowFieldsLastUseLoc(Expr* E); + void VisitInitForTargets(Expr *InitE, llvm::SmallVector& Targets); + void VisitMEForBorrowFieldPath(MemberExpr *ME, std::pair& BorrowWithFieldPath); + void VisitMEForTargetFieldPath(MemberExpr *ME, TargetInfo& TargetWithFieldPath); + void UpdateNLLWhenTargetFound(TargetInfo OldTarget, const llvm::SmallVector& NewTargets); + void FindBorrowFieldsOfStruct(RecordDecl* RD, + llvm::SmallVector>& BorrowFieldsOfStruct); + void FindBorrowFieldsOfStructDFS(FieldDecl* CurrFD, std::string FP, + llvm::SmallVector>& BorrowFieldsOfStruct); }; class BorrowRuleChecker : public StmtVisitor { @@ -133,17 +153,23 @@ public: 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. - // Then BorrowTargetMap will be: - // local1 : [ (p1, 5, 10), (p3, 6, 8) ], local2 : [ (p2, 3, 4), (p3, 6, 8) ] - llvm::DenseMap>> BorrowTargetMap; - // save valid borrowed data within the current scope - llvm::DenseMap>> ActiveBorrowTargetMap; +// 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; void BuildBorrowTargetMap(); void CheckBeBorrowedTarget(const CFGPath &Path); @@ -375,21 +401,27 @@ void BorrowRuleChecker::CheckBeBorrowedTarget(const CFGPath &Path) { void BorrowRuleChecker::BuildBorrowTargetMap() { for (auto NLLOfVar : NLLForAllVars) { - VarDecl* VD = NLLOfVar.first; + VarDecl* BorrowVD = NLLOfVar.first; NonLexicalLifetimeOfVar NLLRangesOfVar = NLLOfVar.second; for (auto NLLRange : NLLRangesOfVar) { - if (NLLRange.Target) - BorrowTargetMap[NLLRange.Target].push_back(std::tuple(VD, NLLRange.Range.first, NLLRange.Range.second)); + 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() << ": [ "; + llvm::outs() << v.first->getNameAsString() << "\n"; for (auto w : v.second) { - VarDecl* V = std::get<0>(w); - llvm::outs() << "(" << V->getNameAsString() << ", " << std::get<1>(w) << ", " << std::get<2>(w) << "), "; + 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"; + llvm::outs() << "\n"; } } @@ -398,17 +430,17 @@ void BorrowRuleChecker::BuildBorrowTargetMap() { void BorrowRuleChecker::CheckBorrowNLLShorterThanTarget() { for (auto BorrowAndTarget : BorrowTargetMap) { VarDecl* TargetVD = BorrowAndTarget.first; - unsigned TargetVDBegin = NLLForAllVars[TargetVD][0].Range.first; - unsigned TargetVDEnd = NLLForAllVars[TargetVD][0].Range.second; for (auto Borrow : BorrowAndTarget.second) { - VarDecl* BorrowVD = std::get<0>(Borrow); - QualType BorrowQT = BorrowVD->getType(); - if (BorrowQT.isBorrowQualified()) { // Naked pointer also have target, but we don't need to consider it. - unsigned BorrowVDBegin = std::get<1>(Borrow); - unsigned BorrowVDEnd = std::get<2>(Borrow); - if (BorrowVDBegin <= TargetVDBegin || BorrowVDEnd >= TargetVDEnd) { - BorrowCheckDiagInfo DI(BorrowVD->getNameAsString(), LiveLonger, BorrowVD->getLocation()); - reporter.addDiagInfo(DI); + 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); + } } } } @@ -430,11 +462,11 @@ void BorrowRuleChecker::CheckLifetimeOfBorrowReturnValue(unsigned NumElements) { for (auto NLLOfVar : NLLForAllVars) { if (NLLOfVar.first->getNameAsString() == "_ReturnVar") { for (NonLexicalLifetimeRange& Range : NLLOfVar.second) { - NonLexicalLifetimeRange TargetNLLRange = NLLForAllVars[Range.Target][0]; - unsigned TargetNLLBegin = TargetNLLRange.Range.first; - unsigned TargetNLLEnd = TargetNLLRange.Range.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->getNameAsString(), ReturnLocal, NLLOfVar.first->getLocation()); + BorrowCheckDiagInfo DI(Range.Target.TargetVD->getNameAsString(), ReturnLocal, NLLOfVar.first->getLocation()); reporter.addDiagInfo(DI); } } @@ -448,21 +480,42 @@ void NLLCalculator::VisitBinaryOperator(BinaryOperator *BO) { // int* borrow p = &mut local1; // p = &mut local2; // We handle it like `int* borrow p = &mut local2;` if (BO->isAssignmentOp()) { - if (DeclRefExpr * DRE = dyn_cast(BO->getLHS())) { - if (VarDecl* VD = dyn_cast(DRE->getDecl())) { - if (VD->getType().isBorrowQualified()) { + if (BO->getLHS()->getType().isBorrowQualified()) { + BorrowKind BK = BO->getLHS()->getType().isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + // p = &mut local; + if (DeclRefExpr * DRE = dyn_cast(BO->getLHS())) { + if (VarDecl* VD = dyn_cast(DRE->getDecl())) { Expr *Init = BO->getRHS(); - llvm::SmallVector Targets; + llvm::SmallVector Targets; VisitInitForTargets(Init, Targets); // When we find the target of a borrow, // we should update previous target in NLLForAllVars. - UpdateNLLWhenTargetFound(VD, Targets); - for (VarDecl* Target : Targets) + UpdateNLLWhenTargetFound(TargetInfo(VD), Targets); + for (TargetInfo Target : Targets) NLLForAllVars[VD].push_back( - NonLexicalLifetimeRange(CurElemID, FoundVars.count(VD) ? FoundVars[VD] : CurElemID, Target)); + 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; + VisitMEForBorrowFieldPath(ME, BorrowWithFieldPath); + Expr *Init = BO->getRHS(); + llvm::SmallVector Targets; + VisitInitForTargets(Init, Targets); + 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 { + CurrOpIsBorrowUse = true; + Visit(BO->getRHS()); + CurrOpIsBorrowUse = false; } } } @@ -474,6 +527,23 @@ void NLLCalculator::VisitDeclRefExpr(DeclRefExpr *DRE) { 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; + } + } + } + } } } @@ -481,11 +551,9 @@ void NLLCalculator::VisitReturnStmt(ReturnStmt *RS) { Expr *RV = RS->getRetValue(); if (!RV) return; - // For return stmt `return p;` or `return &mut* p`, - // we think borrow pointer `p` is used. - MarkBorrowLastUseLoc(RV); 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); @@ -493,76 +561,282 @@ void NLLCalculator::VisitReturnStmt(ReturnStmt *RS) { 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; + llvm::SmallVector Targets; VisitInitForTargets(RV, Targets); - for (VarDecl* Target : Targets) - NLLForAllVars[ReturnVD].push_back(NonLexicalLifetimeRange(CurElemID, CurElemID, Target)); + 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; + VisitMEForBorrowFieldPath(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; + VisitMEForBorrowFieldPath(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)` or `use(&mut* p)`, + // 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) - MarkBorrowLastUseLoc(*it); + Visit(*it); + CurrOpIsBorrowUse = false; +} + +void NLLCalculator::VisitImplicitCastExpr(ImplicitCastExpr *ICE) { + if (CurrOpIsBorrowUse) + Visit(ICE->getSubExpr()); } void NLLCalculator::VisitUnaryOperator(UnaryOperator *UO) { - // For pointer deref `*p`, + // For pointer deref `*p`,`&mut *p`, `&const *p` // we think borrow pointer `p` is used here. - if (UO->getOpcode() == UO_Deref) - MarkBorrowLastUseLoc(UO->getSubExpr()); + 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)) { - if (VD->getType().isBorrowQualified()) { + 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; Expr *Init = VD->getInit(); - llvm::SmallVector Targets; + llvm::SmallVector Targets; VisitInitForTargets(Init, Targets); // When we find the target of a borrow, // we should update previous target in NLLForAllVars. - UpdateNLLWhenTargetFound(VD, Targets); - for (VarDecl* Target : Targets) + UpdateNLLWhenTargetFound(TargetInfo(VD), Targets); + for (TargetInfo Target : Targets) NLLForAllVars[VD].push_back( - NonLexicalLifetimeRange(CurElemID, FoundVars.count(VD) ? FoundVars[VD] : CurElemID, Target)); + NonLexicalLifetimeRange(CurElemID, FoundVars.count(VD) ? FoundVars[VD] : CurElemID, BK, Target)); FoundVars.erase(VD); + } else if (VQT->hasBorrowFields()) { + if (auto RT = dyn_cast(VQT)) { + RecordDecl* RD = RT->getDecl(); + if (auto InitListE = dyn_cast(VD->getInit())) { + Expr **Inits = InitListE->getInits(); + for (auto FD : RD->fields()) { + QualType FQT = FD->getType().getCanonicalType(); + if (FQT.isBorrowQualified()) { + // VD has direct borrow fields, struct S s = { .p = &mut local }; + BorrowKind BK = FQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + Expr *Init = Inits[FD->getFieldIndex()]; + llvm::SmallVector Targets; + VisitInitForTargets(Init, 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()) { + // VD has indirect borrow fields, `struct S s = { .g = g1 };`, `g` has borrow fields. + Expr *Init = Inits[FD->getFieldIndex()]; + llvm::SmallVector Targets; + VisitInitForTargets(Init, Targets); + if (auto RT = dyn_cast(FQT)) { + llvm::SmallVector> BorrowFieldsOfStruct; + FindBorrowFieldsOfStruct(RT->getDecl(), BorrowFieldsOfStruct); + for (auto FieldPath : BorrowFieldsOfStruct) { + std::pair BorrowWithFieldPath; + BorrowWithFieldPath.first = VD; + BorrowWithFieldPath.second = "." + FD->getNameAsString() + FieldPath.first; + llvm::SmallVector TargetsCopy(Targets); + for (TargetInfo& Target : TargetsCopy) { + Target.TargetFieldPath = Target.TargetFieldPath + FieldPath.first; + NLLForAllVars[VD].push_back( + NonLexicalLifetimeRange(CurElemID, + FoundBorrowFields.count(BorrowWithFieldPath) ? FoundBorrowFields[BorrowWithFieldPath] : CurElemID, + FieldPath.second, Target)); + } + UpdateNLLWhenTargetFound(TargetInfo(BorrowWithFieldPath.first, BorrowWithFieldPath.second), TargetsCopy); + FoundBorrowFields.erase(BorrowWithFieldPath); + } + } + } + } + } + } + } else if (VD->getInit()) { + CurrOpIsBorrowUse = true; + Visit(VD->getInit()); + CurrOpIsBorrowUse = false; } } } } void NLLCalculator::MarkBorrowLastUseLoc(Expr* E) { - DeclRefExpr* DRE = nullptr; - if (auto ICE = dyn_cast(E)) { - DRE = dyn_cast(ICE->getSubExpr()); - } else if (auto UO = dyn_cast(E)) { - if (UO->getOpcode() == UO_AddrMutDeref || UO->getOpcode() == UO_AddrConstDeref) - DRE = dyn_cast(UO->getSubExpr()); + if (E->getType().isBorrowQualified()) { + // Use borrow pointer directly. + if (auto ICE = dyn_cast(E)) { + if (auto ME = dyn_cast(ICE->getSubExpr())) { + // `use(s.p)`, `s.p` is MemberExpr + std::pair BorrowWithFieldPath; + VisitMEForBorrowFieldPath(ME, BorrowWithFieldPath); + if (!FoundBorrowFields.count(BorrowWithFieldPath)) + FoundBorrowFields[BorrowWithFieldPath] = CurElemID; + } else if (auto DRE = dyn_cast(ICE->getSubExpr())) { + // `use(p)`, `p` is DeclRefExpr + if (VarDecl* VD = dyn_cast(DRE->getDecl())) + if (!FoundVars.count(VD)) + FoundVars[VD] = CurElemID; + } + } else if (auto UO = dyn_cast(E)) { + if (UO->getOpcode() == UO_AddrMutDeref || UO->getOpcode() == UO_AddrConstDeref) + // `use(&mut *p)` or `use(&mut *s.p)` + MarkBorrowLastUseLoc(UO->getSubExpr()); + } + } else if (E->getType()->hasBorrowFields()) { + // Use struct which has borrow fields. + if (auto ICE = dyn_cast(E)) { + if (auto ME = dyn_cast(ICE->getSubExpr())) { + // `use(s.a)`, `s.a` has borrow fields + std::pair BorrowWithFieldPath; + VisitMEForBorrowFieldPath(ME, BorrowWithFieldPath); + if (!FoundBorrowFields.count(BorrowWithFieldPath)) + FoundBorrowFields[BorrowWithFieldPath] = CurElemID; + } else if (auto DRE = dyn_cast(ICE->getSubExpr())) { + // `use(s)`, `s` has borrow fields + if (VarDecl* VD = dyn_cast(DRE->getDecl())) { + std::pair BorrowWithFieldPath; + BorrowWithFieldPath.first = VD; + if (!FoundBorrowFields.count(BorrowWithFieldPath)) + FoundBorrowFields[BorrowWithFieldPath] = CurElemID; + } + } + } + } else if (auto ICE = dyn_cast(E)) { + // `use(*p)`, `p` is borrow pointer + if (auto UO = dyn_cast(ICE->getSubExpr())) { + if (UO->getOpcode() == UO_Deref) + MarkBorrowLastUseLoc(UO->getSubExpr()); + } } +} - if (DRE) - if (VarDecl* VD = dyn_cast(DRE->getDecl())) - if (VD->getType().isBorrowQualified() && !FoundVars.count(VD)) - FoundVars[VD] = CurElemID; +void NLLCalculator::MarkBorrowFieldsLastUseLoc(Expr* E) { + if (auto ME = dyn_cast(E)) { + // `use(s.a)`, `s.a` is MemberExpr, if `s.a` has borrow fields, add its borrow fields. + // if (!FoundBorrowMembers.count(ME)) + // FoundBorrowMembers[ME] = CurElemID; + } else if (auto DRE = dyn_cast(E)) { + // `use(s)`, `s` is DeclRefExpr, if `s` has borrow fields + // if (VarDecl* VD = dyn_cast(DRE->getDecl())) + // if (!FoundVars.count(VD)) + // FoundVars[VD] = CurElemID; + } } +void NLLCalculator::VisitMEForBorrowFieldPath(MemberExpr *ME, std::pair& BorrowWithFieldPath) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + BorrowWithFieldPath.second = "." + FD->getNameAsString() + BorrowWithFieldPath.second; + if (auto BaseME = dyn_cast(ME->getBase())) + VisitMEForBorrowFieldPath(BaseME, BorrowWithFieldPath); + else if (auto BaseDRE = dyn_cast(ME->getBase())) { + VarDecl* BaseVD = dyn_cast(BaseDRE->getDecl()); + BorrowWithFieldPath.first = BaseVD; + } + } +} -void NLLCalculator::VisitInitForTargets(Expr *InitE, llvm::SmallVector& Targets) { +void NLLCalculator::VisitMEForTargetFieldPath(MemberExpr *ME, TargetInfo& TargetWithFieldPath) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + TargetWithFieldPath.TargetFieldPath = "." + FD->getNameAsString() + TargetWithFieldPath.TargetFieldPath; + if (auto BaseME = dyn_cast(ME->getBase())) + VisitMEForTargetFieldPath(BaseME, TargetWithFieldPath); + else if (auto BaseDRE = dyn_cast(ME->getBase())) { + VarDecl* BaseVD = dyn_cast(BaseDRE->getDecl()); + TargetWithFieldPath.TargetVD = BaseVD; + } + } +} + +void NLLCalculator::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::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); + } + } +} + +void NLLCalculator::VisitInitForTargets(Expr *InitE, llvm::SmallVector& Targets) { if (auto UO = dyn_cast(InitE)) { if (UO->getOpcode() == UO_AddrMut || UO->getOpcode() == UO_AddrConst || UO->getOpcode() == UO_AddrMutDeref || UO->getOpcode() == UO_AddrConstDeref) { if (auto* DRE = dyn_cast(UO->getSubExpr())) { // int* borrow p = &mut local; local is DeclRefExpr. VarDecl* IVD = dyn_cast(DRE->getDecl()); - Targets.push_back(IVD); + Targets.push_back(TargetInfo(IVD)); } else if (auto ASE = dyn_cast(UO->getSubExpr())) { // int* borrow p = &mut arr[0]; arr[0] is ArraySubscriptExpr. VisitInitForTargets(ASE->getBase(), Targets); } else if (auto ICE = dyn_cast(UO->getSubExpr())) { // int* borrow p = &mut *q; q is ImplicitCastExpr. VisitInitForTargets(ICE, Targets); + } else if (auto* ME = dyn_cast(UO->getSubExpr())) { + // int* borrow p = &mut s.a.b; s.a.b is MemberExpr. + TargetInfo TargetWithFieldPath; + VisitMEForTargetFieldPath(ME, TargetWithFieldPath); + Targets.push_back(TargetWithFieldPath); } } } else if (auto ICE = dyn_cast(InitE)) { @@ -571,6 +845,11 @@ void NLLCalculator::VisitInitForTargets(Expr *InitE, llvm::SmallVector if (auto* DRE = dyn_cast(ICE->getSubExpr())) { VarDecl* IVD = dyn_cast(DRE->getDecl()); Targets.push_back(IVD); + } else if (auto* ME = dyn_cast(ICE->getSubExpr())) { + // int* borrow p = s.a.b; s.a.b is MemberExpr. + TargetInfo TargetWithFieldPath; + VisitMEForTargetFieldPath(ME, TargetWithFieldPath); + Targets.push_back(TargetWithFieldPath); } } else if (auto CE = dyn_cast(InitE)) { // CallExpr: int* borrow p3 = foo(&mut local1, &mut local2); @@ -593,9 +872,9 @@ void NLLCalculator::VisitScopeBegin(VarDecl *VD) { 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(VirtualVD); - UpdateNLLWhenTargetFound(VD, VirtualTarget); + 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)); } @@ -632,22 +911,23 @@ void NLLCalculator::VisitScopeEnd(VarDecl *VD) { // 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(VarDecl* OldTarget, const llvm::SmallVector& NewTargets) { +void NLLCalculator::UpdateNLLWhenTargetFound(TargetInfo OldTarget, const llvm::SmallVector& NewTargets) { for (auto& NLLOfVar : NLLForAllVars) { NonLexicalLifetimeOfVar& NLLRanges = NLLOfVar.second; - llvm::SmallVector> RangesNeedUpdate; + llvm::SmallVector> RangesNeedUpdate; // Remove ranges whose target is OldTarget for (auto it = NLLRanges.begin(); it != NLLRanges.end();) { if (it->Target == OldTarget) { - RangesNeedUpdate.push_back(it->Range); + 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::pair RangeNeedUpdate : RangesNeedUpdate) { - for (VarDecl* NewTarget : NewTargets) { - NonLexicalLifetimeRange NewRange(RangeNeedUpdate, NewTarget); + 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); } @@ -663,7 +943,7 @@ void NLLCalculator::UpdateNLLWhenTargetFound(VarDecl* OldTarget, const llvm::Sma // 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 llvm::DenseMap& ParamTarget) { +void NLLCalculator::HandleNLLAfterTraversing(const llvm::DenseMap& ParamTarget) { // Handle parameter for (auto Param : ParamTarget) { ParmVarDecl* PVD = Param.first; @@ -671,18 +951,19 @@ void NLLCalculator::HandleNLLAfterTraversing(const llvm::DenseMapisPointerType() && !PQT.isOwnedQualified()) { // Update previous NLL whose target is PVD to virtual ParentVar - llvm::SmallVector VirtualTarget; + llvm::SmallVector VirtualTarget; VirtualTarget.push_back(Param.second); - UpdateNLLWhenTargetFound(PVD, VirtualTarget); + UpdateNLLWhenTargetFound(TargetInfo(PVD), VirtualTarget); // Add NLL for virtual ParentVar. - NLLForAllVars[Param.second].push_back(NonLexicalLifetimeRange(0, NumElements + 3)); + NLLForAllVars[Param.second.TargetVD].push_back(NonLexicalLifetimeRange(0, NumElements + 3)); // Add NLL for param with target. - if (PQT.isBorrowQualified()) // PVD is borrow pointer + 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, Param.second)); - else // PVD is naked pointer + NonLexicalLifetimeRange(1, FoundVars.count(PVD) ? FoundVars[PVD] : 1, BK, Param.second)); + } else // PVD is naked pointer NLLForAllVars[PVD].push_back( - NonLexicalLifetimeRange(1, NumElements + 2, Param.second)); + NonLexicalLifetimeRange(1, NumElements + 2)); } else if (PQT.isOwnedQualified()) { // Add NLL for owned param. NLLForAllVars[PVD].push_back(NonLexicalLifetimeRange(1, FoundVars[PVD])); @@ -698,7 +979,8 @@ void NLLCalculator::HandleNLLAfterTraversing(const llvm::DenseMap& ParamTarget, +NonLexicalLifetime BorrowCheckImpl::CalculateNLLForPath(const CFGPath &Path, + const llvm::DenseMap& ParamTarget, unsigned NumElements) { NLLCalculator Calculator(Ctx, const_cast(fd.getDeclContext()), NumElements); @@ -742,11 +1024,15 @@ NonLexicalLifetime BorrowCheckImpl::CalculateNLLForPath(const CFGPath &Path, con for (auto u : Calculator.NLLForAllVars) { llvm::outs() << u.first->getNameAsString() << "\n"; // VarDecl for (auto v : u.second) { // NLL - llvm::outs() << " " << v.Range.first << "->" << v.Range.second; - if (v.Target) - llvm::outs() << " " << v.Target->getNameAsString() << "\n"; + llvm::outs() << " "; + if (v.BorrowFieldPath.length()) + llvm::outs() << v.BorrowFieldPath; else - llvm::outs() << "\n"; + llvm::outs() << "--"; + llvm::outs() << " " << v.Begin << " " << v.End; + if (v.Target.TargetVD) + llvm::outs() << " " << v.Target.TargetVD->getNameAsString() << " " << v.Target.TargetFieldPath; + llvm::outs() << "\n"; } } llvm::outs() << "\n"; @@ -756,7 +1042,7 @@ NonLexicalLifetime BorrowCheckImpl::CalculateNLLForPath(const CFGPath &Path, con // For borrow or naked pointer parameter, we create virtual ParentVar as target. // Other kind of parameter don't have target. -void BorrowCheckImpl::BuildParamTarget(llvm::DenseMap& ParamTarget) { +void BorrowCheckImpl::BuildParamTarget(llvm::DenseMap& ParamTarget) { for (ParmVarDecl *PVD : fd.parameters()) { if (PVD->getType()->isPointerType() && !PVD->getType().isOwnedQualified()) { std::string Name = "_ParentVar_" + PVD->getNameAsString(); @@ -764,9 +1050,8 @@ void BorrowCheckImpl::BuildParamTarget(llvm::DenseMap& P VarDecl *VD = VarDecl::Create(Ctx, const_cast(fd.getDeclContext()), PVD->getBeginLoc(), PVD->getLocation(), ID, QualType(), nullptr, SC_None); - ParamTarget[PVD] = VD; - } else - ParamTarget[PVD] = nullptr; + ParamTarget[PVD] = TargetInfo(VD); + } } } @@ -789,7 +1074,7 @@ void clang::runBorrowCheck(const FunctionDecl &fd, const CFG &cfg, BorrowCheckImpl BC(Ctx, fd); - llvm::DenseMap ParamTarget; + llvm::DenseMap ParamTarget; BC.BuildParamTarget(ParamTarget); // First we find all paths from entry block to exit block of a cfg. -- Gitee From 58f24353f96ea28fd5a4e6f05d4c43d6e6f7898e Mon Sep 17 00:00:00 2001 From: wuhuiquan Date: Tue, 30 Jul 2024 21:27:48 +0800 Subject: [PATCH 4/6] [Huawei] Check be borrowed point type --- .../clang/Analysis/Analyses/BSCBorrowCheck.h | 72 +-- clang/lib/Analysis/BSCBorrowCheck.cpp | 417 +++++++++++------- .../borrow_arr_freeze/borrow_arr_freeze.cbs | 2 + .../borrow_star_freeze/borrow_star_freeze.cbs | 30 ++ .../virtual_target_live_longer.cbs | 6 +- 5 files changed, 329 insertions(+), 198 deletions(-) create mode 100644 clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_star_freeze/borrow_star_freeze.cbs diff --git a/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h b/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h index 1fe821d7a655..3be5bc0fae00 100644 --- a/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h +++ b/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h @@ -31,15 +31,17 @@ enum BorrowKind : char { }; struct TargetInfo { - VarDecl* TargetVD = nullptr; + VarDecl *TargetVD = nullptr; std::string TargetFieldPath; - + TargetInfo() {} - TargetInfo(VarDecl* targetVD) : TargetVD(targetVD) {} - TargetInfo(VarDecl* targetVD, std::string targetFieldPath) : TargetVD(targetVD), TargetFieldPath(targetFieldPath) {} + 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; + bool operator==(const TargetInfo &Other) const { + return TargetVD == Other.TargetVD && + TargetFieldPath == Other.TargetFieldPath; } }; @@ -49,34 +51,40 @@ struct NonLexicalLifetimeRange { // For example, // a.b.c = &mut local; // BorrowFieldPath : ".b.c" // p = &mut local; // BorrowFieldPath : "" - std::string 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: + // 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" + // 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) + // 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) + 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) {} + 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. + // This constructor is for no-borrow variables, which don't have binding + // targets. NonLexicalLifetimeRange(unsigned begin, unsigned end) : Begin(begin), End(end) {} @@ -101,22 +109,28 @@ using NonLexicalLifetime = llvm::DenseMap; struct BorrowInfo { std::string TargetFieldPath; - VarDecl* BorrowVD; + VarDecl *BorrowVD; std::string BorrowFieldPath; unsigned Begin; - unsigned End; + 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) {} + 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>; +using BorrowTargetMapInfo = + llvm::DenseMap>; enum BorrowCheckDiagKind { LiveLonger, diff --git a/clang/lib/Analysis/BSCBorrowCheck.cpp b/clang/lib/Analysis/BSCBorrowCheck.cpp index 61ac05cd8681..02f656c7aff7 100644 --- a/clang/lib/Analysis/BSCBorrowCheck.cpp +++ b/clang/lib/Analysis/BSCBorrowCheck.cpp @@ -31,13 +31,14 @@ public: ASTContext& Ctx; const FunctionDecl& fd; - NonLexicalLifetime CalculateNLLForPath(const CFGPath &Path, - const llvm::DenseMap& ParamTarget, - unsigned NumElements); + NonLexicalLifetime CalculateNLLForPath( + const CFGPath &Path, + const llvm::DenseMap &ParamTarget, + unsigned NumElements); void BorrowCheckForPath(const CFGPath &Path, BorrowCheckDiagReporter &reporter, const NonLexicalLifetime& NLLForAllVars, unsigned NumElements); - void BuildParamTarget(llvm::DenseMap& ParamTarget); - + void BuildParamTarget(llvm::DenseMap &ParamTarget); + BorrowCheckImpl(ASTContext& ASTCtx, const FunctionDecl& FD) : Ctx(ASTCtx), fd(FD) {} }; @@ -97,17 +98,19 @@ class NLLCalculator : public StmtVisitor { // 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. + // Only record borrow struct VarDecl and FieldPath when we traverse path in + // reverse order. // 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; + std::map, unsigned> FoundBorrowFields; unsigned NumElements; bool CurrOpIsBorrowUse = false; + public: // Record current CFGElement index we are visiting in the path. unsigned CurElemID; @@ -127,19 +130,28 @@ public: void VisitImplicitCastExpr(ImplicitCastExpr *ICE); void VisitScopeEnd(VarDecl *VD); void VisitScopeBegin(VarDecl *VD); - void HandleNLLAfterTraversing(const llvm::DenseMap& ParamTarget); + void HandleNLLAfterTraversing( + const llvm::DenseMap &ParamTarget); private: void MarkBorrowLastUseLoc(Expr* E); - void MarkBorrowFieldsLastUseLoc(Expr* E); - void VisitInitForTargets(Expr *InitE, llvm::SmallVector& Targets); - void VisitMEForBorrowFieldPath(MemberExpr *ME, std::pair& BorrowWithFieldPath); - void VisitMEForTargetFieldPath(MemberExpr *ME, TargetInfo& TargetWithFieldPath); - void UpdateNLLWhenTargetFound(TargetInfo OldTarget, const llvm::SmallVector& NewTargets); - void FindBorrowFieldsOfStruct(RecordDecl* RD, - llvm::SmallVector>& BorrowFieldsOfStruct); - void FindBorrowFieldsOfStructDFS(FieldDecl* CurrFD, std::string FP, - llvm::SmallVector>& BorrowFieldsOfStruct); + void MarkBorrowFieldsLastUseLoc(Expr *E); + void VisitInitForTargets(Expr *InitE, llvm::SmallVector &Targets); + void VisitMEForBorrowFieldPath( + MemberExpr *ME, std::pair &BorrowWithFieldPath); + void VisitMEForTargetFieldPath(MemberExpr *ME, + TargetInfo &TargetWithFieldPath); + void + UpdateNLLWhenTargetFound(TargetInfo OldTarget, + const llvm::SmallVector &NewTargets); + void + FindBorrowFieldsOfStruct(RecordDecl *RD, + llvm::SmallVector> + &BorrowFieldsOfStruct); + void FindBorrowFieldsOfStructDFS( + FieldDecl *CurrFD, std::string FP, + llvm::SmallVector> + &BorrowFieldsOfStruct); }; class BorrowRuleChecker : public StmtVisitor { @@ -153,23 +165,27 @@ public: 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 ] } + // 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); @@ -183,7 +199,6 @@ public: void VisitCastExpr(CastExpr *E); void VisitDeclRefExpr(DeclRefExpr *DRE); void VisitDeclStmt(DeclStmt *DS); - void VisitImplicitCastExpr(ImplicitCastExpr *E); void VisitParenExpr(ParenExpr *Node); void VisitUnaryOperator(UnaryOperator *UO); void PushActiveBorrowTargetMap(); @@ -196,16 +211,25 @@ VarDecl *BorrowRuleChecker::FindTargetFromActiveBorrowTargetMap(VarDecl *VD) { for (auto v : ActiveBorrowTargetMap) { auto it = v.second.rbegin(); while (it != v.second.rend()) { - if (std::get<0>(*it) == VD) { + if (it->BorrowVD == VD) { return v.first; } it++; } } } else { - for (auto v : ActiveBorrowTargetMap) { - if (v.first == VD) { - return v.first; + 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; + } } } } @@ -219,14 +243,14 @@ void BorrowRuleChecker::CheckValIsLegalUse(VarDecl *VD, SourceLocation Loc) { auto it = ActiveBorrowTargetMap[TVD].rbegin(); if (VD->getType().isConstBorrow()) { while (it != ActiveBorrowTargetMap[TVD].rend()) { - if (std::get<0>(*it) == VD) { + if (it->BorrowVD == VD) { return; } // immut and mut borrow variables are not allowed to exist // simultaneously. - if (!std::get<0>(*it)->getType().isConstBorrow()) { + if (!it->BorrowVD->getType().isConstBorrow()) { BorrowCheckDiagInfo DI(VD->getNameAsString(), AtMostOneMutBorrow, - Loc, std::get<0>(*it)->getLocation()); + Loc, it->BorrowVD->getLocation()); reporter.addDiagInfo(DI); return; } @@ -234,12 +258,12 @@ void BorrowRuleChecker::CheckValIsLegalUse(VarDecl *VD, SourceLocation Loc) { } } else { assert(it != ActiveBorrowTargetMap[TVD].rend()); - if (std::get<0>(*it) == VD) { + if (it->BorrowVD == VD) { return; } else { // Only allow used the last alive mut borrow variable. BorrowCheckDiagInfo DI(VD->getNameAsString(), AtMostOneMutBorrow, Loc, - std::get<0>(*it)->getLocation()); + it->BorrowVD->getLocation()); reporter.addDiagInfo(DI); return; } @@ -249,15 +273,15 @@ void BorrowRuleChecker::CheckValIsLegalUse(VarDecl *VD, SourceLocation Loc) { if (Op == Write) { // Variables cannot be modified when be borrowed BorrowCheckDiagInfo DI(VD->getNameAsString(), ModifyAfterBeBorrowed, - Loc, std::get<0>(*it)->getLocation()); + Loc, it->BorrowVD->getLocation()); reporter.addDiagInfo(DI); } else { while (it != ActiveBorrowTargetMap[TVD].rend()) { // Variables cannot be read when be mut borrowed - if (!std::get<0>(*it)->getType().isConstBorrow()) { + if (!it->BorrowVD->getType().isConstBorrow()) { BorrowCheckDiagInfo DI(VD->getNameAsString(), ReadAfterBeMutBorrowed, Loc, - std::get<0>(*it)->getLocation()); + it->BorrowVD->getLocation()); reporter.addDiagInfo(DI); return; } @@ -305,12 +329,8 @@ void BorrowRuleChecker::VisitCastExpr(CastExpr *E) { Visit(E->getSubExpr()); } void BorrowRuleChecker::VisitParenExpr(ParenExpr *E) { Visit(E->getSubExpr()); } -void BorrowRuleChecker::VisitImplicitCastExpr(ImplicitCastExpr *E) { - Visit(E->getSubExpr()); -} void BorrowRuleChecker::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { Visit(E->getLHS()); - Visit(E->getRHS()); } void BorrowRuleChecker::VisitBinaryOperator(BinaryOperator *BO) { @@ -354,7 +374,7 @@ void BorrowRuleChecker::VisitDeclStmt(DeclStmt *DS) { void BorrowRuleChecker::PushActiveBorrowTargetMap() { for (auto v : BorrowTargetMap) { for (auto w : v.second) { - unsigned firstElemId = std::get<1>(w); + unsigned firstElemId = w.Begin; if (CurElemID == firstElemId) { ActiveBorrowTargetMap[v.first].push_back(w); } @@ -363,9 +383,9 @@ void BorrowRuleChecker::PushActiveBorrowTargetMap() { } void BorrowRuleChecker::PopActiveBorrowTargetMap() { - for (auto v : BorrowTargetMap) { + for (auto v : ActiveBorrowTargetMap) { for (auto w : v.second) { - unsigned secondElemId = std::get<2>(w); + unsigned secondElemId = w.End; if (CurElemID == secondElemId) { ActiveBorrowTargetMap[v.first].remove(w); } @@ -401,12 +421,13 @@ void BorrowRuleChecker::CheckBeBorrowedTarget(const CFGPath &Path) { void BorrowRuleChecker::BuildBorrowTargetMap() { for (auto NLLOfVar : NLLForAllVars) { - VarDecl* BorrowVD = NLLOfVar.first; + 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)); + BorrowTargetMap[NLLRange.Target.TargetVD].push_back(BorrowInfo( + NLLRange.Target.TargetFieldPath, BorrowVD, NLLRange.BorrowFieldPath, + NLLRange.Begin, NLLRange.End, NLLRange.Kind)); } } // DEBUG PRINT @@ -417,9 +438,11 @@ void BorrowRuleChecker::BuildBorrowTargetMap() { llvm::outs() << " "; if (w.TargetFieldPath.length()) llvm::outs() << w.TargetFieldPath; - else + else llvm::outs() << "--"; - llvm::outs() << " " << w.BorrowVD->getNameAsString() << " " << w.BorrowFieldPath << " " << w.Begin << " " << w.End << " " << w.Kind << "\n" ; + llvm::outs() << " " << w.BorrowVD->getNameAsString() << " " + << w.BorrowFieldPath << " " << w.Begin << " " << w.End + << " " << w.Kind << "\n"; } llvm::outs() << "\n"; } @@ -438,8 +461,9 @@ void BorrowRuleChecker::CheckBorrowNLLShorterThanTarget() { 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); + BorrowCheckDiagInfo DI(Borrow.BorrowVD->getNameAsString(), + LiveLonger, Borrow.BorrowVD->getLocation()); + reporter.addDiagInfo(DI); } } } @@ -462,11 +486,13 @@ 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]; + 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()); + BorrowCheckDiagInfo DI(Range.Target.TargetVD->getNameAsString(), + ReturnLocal, NLLOfVar.first->getLocation()); reporter.addDiagInfo(DI); } } @@ -481,10 +507,12 @@ void NLLCalculator::VisitBinaryOperator(BinaryOperator *BO) { // p = &mut local2; // We handle it like `int* borrow p = &mut local2;` if (BO->isAssignmentOp()) { if (BO->getLHS()->getType().isBorrowQualified()) { - BorrowKind BK = BO->getLHS()->getType().isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + BorrowKind BK = BO->getLHS()->getType().isConstBorrow() + ? BorrowKind::Immut + : BorrowKind::Mut; // p = &mut local; - if (DeclRefExpr * DRE = dyn_cast(BO->getLHS())) { - if (VarDecl* VD = dyn_cast(DRE->getDecl())) { + if (DeclRefExpr *DRE = dyn_cast(BO->getLHS())) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { Expr *Init = BO->getRHS(); llvm::SmallVector Targets; VisitInitForTargets(Init, Targets); @@ -492,25 +520,30 @@ void NLLCalculator::VisitBinaryOperator(BinaryOperator *BO) { // 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)); + 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())) { + } else if (MemberExpr *ME = dyn_cast(BO->getLHS())) { // s.a.b = &mut local; - std::pair BorrowWithFieldPath; - VisitMEForBorrowFieldPath(ME, BorrowWithFieldPath); + std::pair BorrowWithFieldPath; + VisitMEForBorrowFieldPath(ME, BorrowWithFieldPath); Expr *Init = BO->getRHS(); llvm::SmallVector Targets; VisitInitForTargets(Init, Targets); - UpdateNLLWhenTargetFound(TargetInfo(BorrowWithFieldPath.first, BorrowWithFieldPath.second), Targets); + 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); + NonLexicalLifetimeRange( + CurElemID, + FoundBorrowFields.count(BorrowWithFieldPath) + ? FoundBorrowFields[BorrowWithFieldPath] + : CurElemID, + BK, Target, BorrowWithFieldPath.second)); + FoundBorrowFields.erase(BorrowWithFieldPath); } } else { CurrOpIsBorrowUse = true; @@ -534,10 +567,12 @@ void NLLCalculator::VisitDeclRefExpr(DeclRefExpr *DRE) { FoundVars[VD] = CurElemID; else if (VQT->hasBorrowFields()) { if (auto RT = dyn_cast(VQT)) { - llvm::SmallVector> BorrowFieldsOfStruct; + llvm::SmallVector> + BorrowFieldsOfStruct; FindBorrowFieldsOfStruct(RT->getDecl(), BorrowFieldsOfStruct); for (auto FieldPath : BorrowFieldsOfStruct) { - std::pair BorrowWithFieldPath(VD, FieldPath.first); + std::pair BorrowWithFieldPath( + VD, FieldPath.first); if (!FoundBorrowFields.count(BorrowWithFieldPath)) FoundBorrowFields[BorrowWithFieldPath] = CurElemID; } @@ -553,22 +588,25 @@ void NLLCalculator::VisitReturnStmt(ReturnStmt *RS) { return; QualType ReturnQT = RV->getType(); if (ReturnQT.isBorrowQualified()) { - BorrowKind BK = ReturnQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + 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. + // 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)); + 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. + // 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; @@ -579,20 +617,24 @@ void NLLCalculator::VisitMemberExpr(MemberExpr *ME) { QualType MQT = ME->getType().getCanonicalType(); if (MQT.isBorrowQualified()) { // Use borrow pointer directly, such as `s.p` - std::pair BorrowWithFieldPath; + std::pair BorrowWithFieldPath; VisitMEForBorrowFieldPath(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; + // Use borrow pointer indirectly, such as `s.a`, a is a struct type and + // has borrow fields. + std::pair BorrowWithFieldPath; VisitMEForBorrowFieldPath(ME, BorrowWithFieldPath); if (auto RT = dyn_cast(MQT)) { - llvm::SmallVector> BorrowFieldsOfStruct; + llvm::SmallVector> + BorrowFieldsOfStruct; FindBorrowFieldsOfStruct(RT->getDecl(), BorrowFieldsOfStruct); for (auto FieldPath : BorrowFieldsOfStruct) { - std::pair BorrowWithFieldPathCopy(BorrowWithFieldPath); - BorrowWithFieldPathCopy.second = BorrowWithFieldPathCopy.second + FieldPath.first; + std::pair BorrowWithFieldPathCopy( + BorrowWithFieldPath); + BorrowWithFieldPathCopy.second = + BorrowWithFieldPathCopy.second + FieldPath.first; if (!FoundBorrowFields.count(BorrowWithFieldPathCopy)) FoundBorrowFields[BorrowWithFieldPathCopy] = CurElemID; } @@ -605,7 +647,7 @@ void NLLCalculator::VisitMemberExpr(MemberExpr *ME) { 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. + // 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); @@ -619,9 +661,11 @@ void NLLCalculator::VisitImplicitCastExpr(ImplicitCastExpr *ICE) { void NLLCalculator::VisitUnaryOperator(UnaryOperator *UO) { // For pointer deref `*p`,`&mut *p`, `&const *p` - // we think borrow pointer `p` is used here. - if (CurrOpIsBorrowUse && - (UO->getOpcode() == UO_Deref || UO->getOpcode() == UO_AddrMutDeref || UO->getOpcode() == UO_AddrConstDeref)) + // 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()); } @@ -630,62 +674,79 @@ void NLLCalculator::VisitDeclStmt(DeclStmt *DS) { if (VarDecl *VD = dyn_cast(D)) { QualType VQT = VD->getType().getCanonicalType(); if (VQT.isBorrowQualified()) { - BorrowKind BK = VQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + BorrowKind BK = + VQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; // VD itself is a borrow, int* borrow p = &mut local; Expr *Init = VD->getInit(); llvm::SmallVector Targets; VisitInitForTargets(Init, Targets); // When we find the target of a borrow, // we should update previous target in NLLForAllVars. - UpdateNLLWhenTargetFound(TargetInfo(VD), Targets); + UpdateNLLWhenTargetFound(TargetInfo(VD), Targets); for (TargetInfo Target : Targets) - NLLForAllVars[VD].push_back( - NonLexicalLifetimeRange(CurElemID, FoundVars.count(VD) ? FoundVars[VD] : CurElemID, BK, Target)); + NLLForAllVars[VD].push_back(NonLexicalLifetimeRange( + CurElemID, FoundVars.count(VD) ? FoundVars[VD] : CurElemID, BK, + Target)); FoundVars.erase(VD); } else if (VQT->hasBorrowFields()) { if (auto RT = dyn_cast(VQT)) { - RecordDecl* RD = RT->getDecl(); + RecordDecl *RD = RT->getDecl(); if (auto InitListE = dyn_cast(VD->getInit())) { Expr **Inits = InitListE->getInits(); for (auto FD : RD->fields()) { QualType FQT = FD->getType().getCanonicalType(); if (FQT.isBorrowQualified()) { - // VD has direct borrow fields, struct S s = { .p = &mut local }; - BorrowKind BK = FQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; + // VD has direct borrow fields, struct S s = { .p = &mut local + // }; + BorrowKind BK = + FQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; Expr *Init = Inits[FD->getFieldIndex()]; llvm::SmallVector Targets; VisitInitForTargets(Init, Targets); - std::pair BorrowWithFieldPath; + std::pair BorrowWithFieldPath; BorrowWithFieldPath.first = VD; BorrowWithFieldPath.second = "." + FD->getNameAsString(); - UpdateNLLWhenTargetFound(TargetInfo(BorrowWithFieldPath.first, BorrowWithFieldPath.second), Targets); + 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)); + NLLForAllVars[VD].push_back(NonLexicalLifetimeRange( + CurElemID, + FoundBorrowFields.count(BorrowWithFieldPath) + ? FoundBorrowFields[BorrowWithFieldPath] + : CurElemID, + BK, Target, BorrowWithFieldPath.second)); FoundBorrowFields.erase(BorrowWithFieldPath); } else if (FQT->hasBorrowFields()) { - // VD has indirect borrow fields, `struct S s = { .g = g1 };`, `g` has borrow fields. + // VD has indirect borrow fields, `struct S s = { .g = g1 };`, + // `g` has borrow fields. Expr *Init = Inits[FD->getFieldIndex()]; llvm::SmallVector Targets; VisitInitForTargets(Init, Targets); if (auto RT = dyn_cast(FQT)) { - llvm::SmallVector> BorrowFieldsOfStruct; + llvm::SmallVector> + BorrowFieldsOfStruct; FindBorrowFieldsOfStruct(RT->getDecl(), BorrowFieldsOfStruct); for (auto FieldPath : BorrowFieldsOfStruct) { - std::pair BorrowWithFieldPath; + std::pair BorrowWithFieldPath; BorrowWithFieldPath.first = VD; - BorrowWithFieldPath.second = "." + FD->getNameAsString() + FieldPath.first; + BorrowWithFieldPath.second = + "." + FD->getNameAsString() + FieldPath.first; llvm::SmallVector TargetsCopy(Targets); - for (TargetInfo& Target : TargetsCopy) { - Target.TargetFieldPath = Target.TargetFieldPath + FieldPath.first; - NLLForAllVars[VD].push_back( - NonLexicalLifetimeRange(CurElemID, - FoundBorrowFields.count(BorrowWithFieldPath) ? FoundBorrowFields[BorrowWithFieldPath] : CurElemID, - FieldPath.second, Target)); + for (TargetInfo &Target : TargetsCopy) { + Target.TargetFieldPath = + Target.TargetFieldPath + FieldPath.first; + NLLForAllVars[VD].push_back(NonLexicalLifetimeRange( + CurElemID, + FoundBorrowFields.count(BorrowWithFieldPath) + ? FoundBorrowFields[BorrowWithFieldPath] + : CurElemID, + FieldPath.second, Target)); } - UpdateNLLWhenTargetFound(TargetInfo(BorrowWithFieldPath.first, BorrowWithFieldPath.second), TargetsCopy); + UpdateNLLWhenTargetFound( + TargetInfo(BorrowWithFieldPath.first, + BorrowWithFieldPath.second), + TargetsCopy); FoundBorrowFields.erase(BorrowWithFieldPath); } } @@ -696,7 +757,7 @@ void NLLCalculator::VisitDeclStmt(DeclStmt *DS) { } else if (VD->getInit()) { CurrOpIsBorrowUse = true; Visit(VD->getInit()); - CurrOpIsBorrowUse = false; + CurrOpIsBorrowUse = false; } } } @@ -708,18 +769,19 @@ void NLLCalculator::MarkBorrowLastUseLoc(Expr* E) { if (auto ICE = dyn_cast(E)) { if (auto ME = dyn_cast(ICE->getSubExpr())) { // `use(s.p)`, `s.p` is MemberExpr - std::pair BorrowWithFieldPath; + std::pair BorrowWithFieldPath; VisitMEForBorrowFieldPath(ME, BorrowWithFieldPath); if (!FoundBorrowFields.count(BorrowWithFieldPath)) FoundBorrowFields[BorrowWithFieldPath] = CurElemID; } else if (auto DRE = dyn_cast(ICE->getSubExpr())) { // `use(p)`, `p` is DeclRefExpr - if (VarDecl* VD = dyn_cast(DRE->getDecl())) + if (VarDecl *VD = dyn_cast(DRE->getDecl())) if (!FoundVars.count(VD)) FoundVars[VD] = CurElemID; } } else if (auto UO = dyn_cast(E)) { - if (UO->getOpcode() == UO_AddrMutDeref || UO->getOpcode() == UO_AddrConstDeref) + if (UO->getOpcode() == UO_AddrMutDeref || + UO->getOpcode() == UO_AddrConstDeref) // `use(&mut *p)` or `use(&mut *s.p)` MarkBorrowLastUseLoc(UO->getSubExpr()); } @@ -728,14 +790,14 @@ void NLLCalculator::MarkBorrowLastUseLoc(Expr* E) { if (auto ICE = dyn_cast(E)) { if (auto ME = dyn_cast(ICE->getSubExpr())) { // `use(s.a)`, `s.a` has borrow fields - std::pair BorrowWithFieldPath; + std::pair BorrowWithFieldPath; VisitMEForBorrowFieldPath(ME, BorrowWithFieldPath); if (!FoundBorrowFields.count(BorrowWithFieldPath)) FoundBorrowFields[BorrowWithFieldPath] = CurElemID; } else if (auto DRE = dyn_cast(ICE->getSubExpr())) { // `use(s)`, `s` has borrow fields - if (VarDecl* VD = dyn_cast(DRE->getDecl())) { - std::pair BorrowWithFieldPath; + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { + std::pair BorrowWithFieldPath; BorrowWithFieldPath.first = VD; if (!FoundBorrowFields.count(BorrowWithFieldPath)) FoundBorrowFields[BorrowWithFieldPath] = CurElemID; @@ -751,10 +813,10 @@ void NLLCalculator::MarkBorrowLastUseLoc(Expr* E) { } } -void NLLCalculator::MarkBorrowFieldsLastUseLoc(Expr* E) { +void NLLCalculator::MarkBorrowFieldsLastUseLoc(Expr *E) { if (auto ME = dyn_cast(E)) { - // `use(s.a)`, `s.a` is MemberExpr, if `s.a` has borrow fields, add its borrow fields. - // if (!FoundBorrowMembers.count(ME)) + // `use(s.a)`, `s.a` is MemberExpr, if `s.a` has borrow fields, add its + // borrow fields. if (!FoundBorrowMembers.count(ME)) // FoundBorrowMembers[ME] = CurElemID; } else if (auto DRE = dyn_cast(E)) { // `use(s)`, `s` is DeclRefExpr, if `s` has borrow fields @@ -764,46 +826,54 @@ void NLLCalculator::MarkBorrowFieldsLastUseLoc(Expr* E) { } } -void NLLCalculator::VisitMEForBorrowFieldPath(MemberExpr *ME, std::pair& BorrowWithFieldPath) { +void NLLCalculator::VisitMEForBorrowFieldPath( + MemberExpr *ME, std::pair &BorrowWithFieldPath) { if (auto FD = dyn_cast(ME->getMemberDecl())) { - BorrowWithFieldPath.second = "." + FD->getNameAsString() + BorrowWithFieldPath.second; + BorrowWithFieldPath.second = + "." + FD->getNameAsString() + BorrowWithFieldPath.second; if (auto BaseME = dyn_cast(ME->getBase())) VisitMEForBorrowFieldPath(BaseME, BorrowWithFieldPath); else if (auto BaseDRE = dyn_cast(ME->getBase())) { - VarDecl* BaseVD = dyn_cast(BaseDRE->getDecl()); + VarDecl *BaseVD = dyn_cast(BaseDRE->getDecl()); BorrowWithFieldPath.first = BaseVD; } } } -void NLLCalculator::VisitMEForTargetFieldPath(MemberExpr *ME, TargetInfo& TargetWithFieldPath) { +void NLLCalculator::VisitMEForTargetFieldPath(MemberExpr *ME, + TargetInfo &TargetWithFieldPath) { if (auto FD = dyn_cast(ME->getMemberDecl())) { - TargetWithFieldPath.TargetFieldPath = "." + FD->getNameAsString() + TargetWithFieldPath.TargetFieldPath; + TargetWithFieldPath.TargetFieldPath = + "." + FD->getNameAsString() + TargetWithFieldPath.TargetFieldPath; if (auto BaseME = dyn_cast(ME->getBase())) VisitMEForTargetFieldPath(BaseME, TargetWithFieldPath); else if (auto BaseDRE = dyn_cast(ME->getBase())) { - VarDecl* BaseVD = dyn_cast(BaseDRE->getDecl()); + VarDecl *BaseVD = dyn_cast(BaseDRE->getDecl()); TargetWithFieldPath.TargetVD = BaseVD; } } } -void NLLCalculator::FindBorrowFieldsOfStruct(RecordDecl* RD, - llvm::SmallVector>& BorrowFieldsOfStruct) { - for (FieldDecl* FD : RD->fields()) { +void NLLCalculator::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)); + BorrowFieldsOfStruct.push_back( + std::pair(FP, BK)); } else if (FQT->hasBorrowFields()) { FindBorrowFieldsOfStructDFS(FD, "", BorrowFieldsOfStruct); } } } -void NLLCalculator::FindBorrowFieldsOfStructDFS(FieldDecl* CurrFD, std::string FP, - llvm::SmallVector>& BorrowFieldsOfStruct) { +void NLLCalculator::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; @@ -812,13 +882,14 @@ void NLLCalculator::FindBorrowFieldsOfStructDFS(FieldDecl* CurrFD, std::string F } else if (FQT->hasBorrowFields()) { FP = FP + "." + CurrFD->getNameAsString(); if (auto RT = dyn_cast(FQT)) { - for (FieldDecl* FD : RT->getDecl()->fields()) + for (FieldDecl *FD : RT->getDecl()->fields()) FindBorrowFieldsOfStructDFS(FD, FP, BorrowFieldsOfStruct); } } } -void NLLCalculator::VisitInitForTargets(Expr *InitE, llvm::SmallVector& Targets) { +void NLLCalculator::VisitInitForTargets( + Expr *InitE, llvm::SmallVector &Targets) { if (auto UO = dyn_cast(InitE)) { if (UO->getOpcode() == UO_AddrMut || UO->getOpcode() == UO_AddrConst || UO->getOpcode() == UO_AddrMutDeref || UO->getOpcode() == UO_AddrConstDeref) { @@ -832,8 +903,8 @@ void NLLCalculator::VisitInitForTargets(Expr *InitE, llvm::SmallVector(UO->getSubExpr())) { // int* borrow p = &mut *q; q is ImplicitCastExpr. VisitInitForTargets(ICE, Targets); - } else if (auto* ME = dyn_cast(UO->getSubExpr())) { - // int* borrow p = &mut s.a.b; s.a.b is MemberExpr. + } else if (auto *ME = dyn_cast(UO->getSubExpr())) { + // int* borrow p = &mut s.a.b; s.a.b is MemberExpr. TargetInfo TargetWithFieldPath; VisitMEForTargetFieldPath(ME, TargetWithFieldPath); Targets.push_back(TargetWithFieldPath); @@ -845,8 +916,8 @@ void NLLCalculator::VisitInitForTargets(Expr *InitE, llvm::SmallVector(ICE->getSubExpr())) { VarDecl* IVD = dyn_cast(DRE->getDecl()); Targets.push_back(IVD); - } else if (auto* ME = dyn_cast(ICE->getSubExpr())) { - // int* borrow p = s.a.b; s.a.b is MemberExpr. + } else if (auto *ME = dyn_cast(ICE->getSubExpr())) { + // int* borrow p = s.a.b; s.a.b is MemberExpr. TargetInfo TargetWithFieldPath; VisitMEForTargetFieldPath(ME, TargetWithFieldPath); Targets.push_back(TargetWithFieldPath); @@ -871,7 +942,8 @@ void NLLCalculator::VisitScopeBegin(VarDecl *VD) { 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 + // 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); @@ -911,23 +983,30 @@ void NLLCalculator::VisitScopeEnd(VarDecl *VD) { // 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) { +void NLLCalculator::UpdateNLLWhenTargetFound( + TargetInfo OldTarget, const llvm::SmallVector &NewTargets) { for (auto& NLLOfVar : NLLForAllVars) { NonLexicalLifetimeOfVar& NLLRanges = NLLOfVar.second; - llvm::SmallVector> RangesNeedUpdate; + 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)); + 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 (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)); + 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); } @@ -943,27 +1022,30 @@ void NLLCalculator::UpdateNLLWhenTargetFound(TargetInfo OldTarget, const llvm::S // 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 llvm::DenseMap& ParamTarget) { +void NLLCalculator::HandleNLLAfterTraversing( + const llvm::DenseMap &ParamTarget) { // Handle parameter for (auto Param : ParamTarget) { ParmVarDecl* PVD = Param.first; QualType PQT = PVD->getType(); // Handle borrow or naked pointer parameter which have virtual ParentVar. if (PQT->isPointerType() && !PQT.isOwnedQualified()) { - // Update previous NLL whose target is PVD to 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)); + 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 + 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)); + NonLexicalLifetimeRange(1, NumElements + 2)); } else if (PQT.isOwnedQualified()) { // Add NLL for owned param. NLLForAllVars[PVD].push_back(NonLexicalLifetimeRange(1, FoundVars[PVD])); @@ -979,9 +1061,10 @@ void NLLCalculator::HandleNLLAfterTraversing(const llvm::DenseMap& ParamTarget, - unsigned NumElements) { +NonLexicalLifetime BorrowCheckImpl::CalculateNLLForPath( + const CFGPath &Path, + const llvm::DenseMap &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, @@ -1031,7 +1114,8 @@ NonLexicalLifetime BorrowCheckImpl::CalculateNLLForPath(const CFGPath &Path, llvm::outs() << "--"; llvm::outs() << " " << v.Begin << " " << v.End; if (v.Target.TargetVD) - llvm::outs() << " " << v.Target.TargetVD->getNameAsString() << " " << v.Target.TargetFieldPath; + llvm::outs() << " " << v.Target.TargetVD->getNameAsString() << " " + << v.Target.TargetFieldPath; llvm::outs() << "\n"; } } @@ -1041,8 +1125,9 @@ NonLexicalLifetime BorrowCheckImpl::CalculateNLLForPath(const CFGPath &Path, } // For borrow or naked pointer parameter, we create virtual ParentVar as target. -// Other kind of parameter don't have target. -void BorrowCheckImpl::BuildParamTarget(llvm::DenseMap& ParamTarget) { +// Other kind of parameter don't have target. +void BorrowCheckImpl::BuildParamTarget( + llvm::DenseMap &ParamTarget) { for (ParmVarDecl *PVD : fd.parameters()) { if (PVD->getType()->isPointerType() && !PVD->getType().isOwnedQualified()) { std::string Name = "_ParentVar_" + PVD->getNameAsString(); @@ -1074,7 +1159,7 @@ void clang::runBorrowCheck(const FunctionDecl &fd, const CFG &cfg, BorrowCheckImpl BC(Ctx, fd); - llvm::DenseMap ParamTarget; + llvm::DenseMap ParamTarget; BC.BuildParamTarget(ParamTarget); // First we find all paths from entry block to exit block of a cfg. 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 index ddaa35e3b125..df64e655a277 100644 --- 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 @@ -14,6 +14,8 @@ void test_freeze_arrow_var1() { 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; } 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 000000000000..3a9e53bc508e --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_star_freeze/borrow_star_freeze.cbs @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -ast-dump -verify %s + +// &mut *e e被冻结 不允许读写e以及它的成员 +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/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 index 65413a6a5cca..5d67f51b5d9b 100644 --- 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 @@ -7,17 +7,17 @@ void use_mut(int *borrow p) {} void test1() { int *a = (int *)malloc(sizeof(int)); int *borrow p = &mut * a; - free(a); // We assume the virtual target of naked pointer live longer than all local variables use_mut(p); + free(a); } void test2(int* a) { int *borrow p = &mut * a; - free(a); use_mut(p); + free(a); } -void test3(int *borrow p) { // We assume the virtual target of borrow parameter live longer than all local variables +void test3(int *borrow p) { int *borrow p1 = &mut * p; use_mut(p1); } -- Gitee From c4016167c9f1f03622d10220d99e228ff3a73e4e Mon Sep 17 00:00:00 2001 From: wuhuiquan Date: Thu, 1 Aug 2024 16:22:22 +0800 Subject: [PATCH 5/6] [Huawei] check conditional operator be borrowed type --- clang/lib/Analysis/BSCBorrowCheck.cpp | 29 +++++++++- .../borrow_ident_freeze.cbs | 56 +++++++++++++++++-- .../borrow_star_freeze/borrow_star_freeze.cbs | 1 - 3 files changed, 76 insertions(+), 10 deletions(-) diff --git a/clang/lib/Analysis/BSCBorrowCheck.cpp b/clang/lib/Analysis/BSCBorrowCheck.cpp index 02f656c7aff7..951f8ce43cfa 100644 --- a/clang/lib/Analysis/BSCBorrowCheck.cpp +++ b/clang/lib/Analysis/BSCBorrowCheck.cpp @@ -196,9 +196,11 @@ public: 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(); @@ -310,6 +312,11 @@ void BorrowRuleChecker::VisitUnaryOperator(UnaryOperator *UO) { case UO_PreDec: Op = Write; break; + case UO_AddrMut: + case UO_AddrConst: + case UO_AddrMutDeref: + case UO_AddrConstDeref: + return; default: break; } @@ -318,11 +325,25 @@ void BorrowRuleChecker::VisitUnaryOperator(UnaryOperator *UO) { } 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 = None; + 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()); } @@ -335,6 +356,7 @@ void BorrowRuleChecker::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { 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()) { @@ -351,11 +373,12 @@ void BorrowRuleChecker::VisitBinaryOperator(BinaryOperator *BO) { Visit(BO->getLHS()); Op = Read; Visit(BO->getRHS()); + Op = TempOp; } - Op = None; } void BorrowRuleChecker::VisitDeclStmt(DeclStmt *DS) { + Operation TempOp = Op; for (auto *D : DS->decls()) { if (VarDecl *VD = dyn_cast(D)) { if (VD->getType().isBorrowQualified()) { @@ -368,7 +391,7 @@ void BorrowRuleChecker::VisitDeclStmt(DeclStmt *DS) { } } } - Op = None; + Op = TempOp; } void BorrowRuleChecker::PushActiveBorrowTargetMap() { 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 index 612293fd83f6..38cfd8fbe8a2 100644 --- 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 @@ -58,24 +58,30 @@ void test_freeze_ident6_0() { int f = *c; } -void use(int * borrow a) {} - 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}} - use(d); - use(c); + 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); - use(c); } void test_freeze_ident7() { @@ -114,7 +120,6 @@ void test_freeze_ident11() { 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); - use(c); } void test_freeze_ident12() { @@ -131,3 +136,42 @@ void test_freeze_ident13() { 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 index 3a9e53bc508e..b450b3017def 100644 --- 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 @@ -1,6 +1,5 @@ // RUN: %clang_cc1 -ast-dump -verify %s -// &mut *e e被冻结 不允许读写e以及它的成员 void test1(int *p) {} void test_freeze_star_var1() { -- Gitee From b4b0eac9ed2f47710a282af6def5bebcf81f6cd3 Mon Sep 17 00:00:00 2001 From: liuxinyi Date: Mon, 5 Aug 2024 10:54:39 +0800 Subject: [PATCH 6/6] borrow struct --- .../clang/Analysis/Analyses/BSCBorrowCheck.h | 5 +- clang/lib/Analysis/BSCBorrowCheck.cpp | 646 ++++++++++-------- 2 files changed, 366 insertions(+), 285 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h b/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h index 3be5bc0fae00..d296706afa9b 100644 --- a/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h +++ b/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h @@ -64,9 +64,8 @@ struct NonLexicalLifetimeRange { // 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" + // 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() {} diff --git a/clang/lib/Analysis/BSCBorrowCheck.cpp b/clang/lib/Analysis/BSCBorrowCheck.cpp index 951f8ce43cfa..ea812ae85424 100644 --- a/clang/lib/Analysis/BSCBorrowCheck.cpp +++ b/clang/lib/Analysis/BSCBorrowCheck.cpp @@ -32,13 +32,13 @@ public: const FunctionDecl& fd; NonLexicalLifetime CalculateNLLForPath( - const CFGPath &Path, - const llvm::DenseMap &ParamTarget, - unsigned NumElements); + const CFGPath &Path, + const std::map, TargetInfo> &ParamTarget, + unsigned NumElements); void BorrowCheckForPath(const CFGPath &Path, BorrowCheckDiagReporter &reporter, const NonLexicalLifetime& NLLForAllVars, unsigned NumElements); - void BuildParamTarget(llvm::DenseMap &ParamTarget); - + void BuildParamTarget(std::map, TargetInfo>& ParamTarget); + BorrowCheckImpl(ASTContext& ASTCtx, const FunctionDecl& FD) : Ctx(ASTCtx), fd(FD) {} }; @@ -99,7 +99,7 @@ class NLLCalculator : public StmtVisitor { llvm::DenseMap FoundVars; // Only record borrow struct VarDecl and FieldPath when we traverse path in - // reverse order. + // reverse order. For example: // struct S { int* borrow p; }; // void test() { // int local = 5; @@ -131,27 +131,18 @@ public: void VisitScopeEnd(VarDecl *VD); void VisitScopeBegin(VarDecl *VD); void HandleNLLAfterTraversing( - const llvm::DenseMap &ParamTarget); + const std::map, TargetInfo> &ParamTarget); private: - void MarkBorrowLastUseLoc(Expr* E); - void MarkBorrowFieldsLastUseLoc(Expr *E); void VisitInitForTargets(Expr *InitE, llvm::SmallVector &Targets); - void VisitMEForBorrowFieldPath( - MemberExpr *ME, std::pair &BorrowWithFieldPath); - void VisitMEForTargetFieldPath(MemberExpr *ME, - TargetInfo &TargetWithFieldPath); + 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); - void - FindBorrowFieldsOfStruct(RecordDecl *RD, - llvm::SmallVector> - &BorrowFieldsOfStruct); - void FindBorrowFieldsOfStructDFS( - FieldDecl *CurrFD, std::string FP, - llvm::SmallVector> - &BorrowFieldsOfStruct); }; class BorrowRuleChecker : public StmtVisitor { @@ -171,10 +162,9 @@ public: // 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. + // `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 ] } @@ -454,7 +444,7 @@ void BorrowRuleChecker::BuildBorrowTargetMap() { } } // DEBUG PRINT - llvm::outs() << "------print BorrowTargetMap-----\n"; + llvm::outs() << "-----------------print BorrowTargetMap--------------------\n"; for (auto v : BorrowTargetMap) { llvm::outs() << v.first->getNameAsString() << "\n"; for (auto w : v.second) { @@ -523,41 +513,65 @@ void BorrowRuleChecker::CheckLifetimeOfBorrowReturnValue(unsigned NumElements) { } } +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()) { - if (BO->getLHS()->getType().isBorrowQualified()) { - BorrowKind BK = BO->getLHS()->getType().isConstBorrow() - ? BorrowKind::Immut - : BorrowKind::Mut; - // p = &mut local; - if (DeclRefExpr *DRE = dyn_cast(BO->getLHS())) { - if (VarDecl *VD = dyn_cast(DRE->getDecl())) { - Expr *Init = BO->getRHS(); - llvm::SmallVector Targets; - VisitInitForTargets(Init, Targets); - // When we find the target of a borrow, - // we should update previous target in NLLForAllVars. + 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)); + 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; - VisitMEForBorrowFieldPath(ME, BorrowWithFieldPath); - Expr *Init = BO->getRHS(); - llvm::SmallVector Targets; - VisitInitForTargets(Init, Targets); - UpdateNLLWhenTargetFound( - TargetInfo(BorrowWithFieldPath.first, BorrowWithFieldPath.second), - Targets); + VisitMEForFieldPath(ME, BorrowWithFieldPath); + UpdateNLLWhenTargetFound(TargetInfo(BorrowWithFieldPath.first, BorrowWithFieldPath.second), Targets); for (TargetInfo Target : Targets) NLLForAllVars[BorrowWithFieldPath.first].push_back( NonLexicalLifetimeRange( @@ -568,6 +582,19 @@ void NLLCalculator::VisitBinaryOperator(BinaryOperator *BO) { 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()); @@ -641,14 +668,14 @@ void NLLCalculator::VisitMemberExpr(MemberExpr *ME) { if (MQT.isBorrowQualified()) { // Use borrow pointer directly, such as `s.p` std::pair BorrowWithFieldPath; - VisitMEForBorrowFieldPath(ME, 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; - VisitMEForBorrowFieldPath(ME, BorrowWithFieldPath); + VisitMEForFieldPath(ME, BorrowWithFieldPath); if (auto RT = dyn_cast(MQT)) { llvm::SmallVector> BorrowFieldsOfStruct; @@ -700,9 +727,8 @@ void NLLCalculator::VisitDeclStmt(DeclStmt *DS) { BorrowKind BK = VQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; // VD itself is a borrow, int* borrow p = &mut local; - Expr *Init = VD->getInit(); llvm::SmallVector Targets; - VisitInitForTargets(Init, Targets); + VisitInitForTargets(VD->getInit(), Targets); // When we find the target of a borrow, // we should update previous target in NLLForAllVars. UpdateNLLWhenTargetFound(TargetInfo(VD), Targets); @@ -712,71 +738,8 @@ void NLLCalculator::VisitDeclStmt(DeclStmt *DS) { Target)); FoundVars.erase(VD); } else if (VQT->hasBorrowFields()) { - if (auto RT = dyn_cast(VQT)) { - RecordDecl *RD = RT->getDecl(); - if (auto InitListE = dyn_cast(VD->getInit())) { - Expr **Inits = InitListE->getInits(); - for (auto FD : RD->fields()) { - QualType FQT = FD->getType().getCanonicalType(); - if (FQT.isBorrowQualified()) { - // VD has direct borrow fields, struct S s = { .p = &mut local - // }; - BorrowKind BK = - FQT.isConstBorrow() ? BorrowKind::Immut : BorrowKind::Mut; - Expr *Init = Inits[FD->getFieldIndex()]; - llvm::SmallVector Targets; - VisitInitForTargets(Init, 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()) { - // VD has indirect borrow fields, `struct S s = { .g = g1 };`, - // `g` has borrow fields. - Expr *Init = Inits[FD->getFieldIndex()]; - llvm::SmallVector Targets; - VisitInitForTargets(Init, Targets); - if (auto RT = dyn_cast(FQT)) { - llvm::SmallVector> - BorrowFieldsOfStruct; - FindBorrowFieldsOfStruct(RT->getDecl(), BorrowFieldsOfStruct); - for (auto FieldPath : BorrowFieldsOfStruct) { - std::pair BorrowWithFieldPath; - BorrowWithFieldPath.first = VD; - BorrowWithFieldPath.second = - "." + FD->getNameAsString() + FieldPath.first; - llvm::SmallVector TargetsCopy(Targets); - for (TargetInfo &Target : TargetsCopy) { - Target.TargetFieldPath = - Target.TargetFieldPath + FieldPath.first; - NLLForAllVars[VD].push_back(NonLexicalLifetimeRange( - CurElemID, - FoundBorrowFields.count(BorrowWithFieldPath) - ? FoundBorrowFields[BorrowWithFieldPath] - : CurElemID, - FieldPath.second, Target)); - } - UpdateNLLWhenTargetFound( - TargetInfo(BorrowWithFieldPath.first, - BorrowWithFieldPath.second), - TargetsCopy); - FoundBorrowFields.erase(BorrowWithFieldPath); - } - } - } - } - } - } + if (RecordDecl* RD = dyn_cast(VQT)->getDecl()) + VisitInitForBorrowFieldTargets(VD, "", RD, VD->getInit()); } else if (VD->getInit()) { CurrOpIsBorrowUse = true; Visit(VD->getInit()); @@ -786,173 +749,250 @@ void NLLCalculator::VisitDeclStmt(DeclStmt *DS) { } } -void NLLCalculator::MarkBorrowLastUseLoc(Expr* E) { - if (E->getType().isBorrowQualified()) { - // Use borrow pointer directly. - if (auto ICE = dyn_cast(E)) { - if (auto ME = dyn_cast(ICE->getSubExpr())) { - // `use(s.p)`, `s.p` is MemberExpr - std::pair BorrowWithFieldPath; - VisitMEForBorrowFieldPath(ME, BorrowWithFieldPath); - if (!FoundBorrowFields.count(BorrowWithFieldPath)) - FoundBorrowFields[BorrowWithFieldPath] = CurElemID; - } else if (auto DRE = dyn_cast(ICE->getSubExpr())) { - // `use(p)`, `p` is DeclRefExpr - if (VarDecl *VD = dyn_cast(DRE->getDecl())) - if (!FoundVars.count(VD)) - FoundVars[VD] = CurElemID; - } - } else if (auto UO = dyn_cast(E)) { - if (UO->getOpcode() == UO_AddrMutDeref || - UO->getOpcode() == UO_AddrConstDeref) - // `use(&mut *p)` or `use(&mut *s.p)` - MarkBorrowLastUseLoc(UO->getSubExpr()); +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 (E->getType()->hasBorrowFields()) { - // Use struct which has borrow fields. - if (auto ICE = dyn_cast(E)) { - if (auto ME = dyn_cast(ICE->getSubExpr())) { - // `use(s.a)`, `s.a` has borrow fields - std::pair BorrowWithFieldPath; - VisitMEForBorrowFieldPath(ME, BorrowWithFieldPath); - if (!FoundBorrowFields.count(BorrowWithFieldPath)) - FoundBorrowFields[BorrowWithFieldPath] = CurElemID; - } else if (auto DRE = dyn_cast(ICE->getSubExpr())) { - // `use(s)`, `s` has borrow fields - if (VarDecl *VD = dyn_cast(DRE->getDecl())) { - std::pair BorrowWithFieldPath; - BorrowWithFieldPath.first = VD; - if (!FoundBorrowFields.count(BorrowWithFieldPath)) - FoundBorrowFields[BorrowWithFieldPath] = CurElemID; + } 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 ICE = dyn_cast(E)) { - // `use(*p)`, `p` is borrow pointer - if (auto UO = dyn_cast(ICE->getSubExpr())) { - if (UO->getOpcode() == UO_Deref) - MarkBorrowLastUseLoc(UO->getSubExpr()); + } 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::MarkBorrowFieldsLastUseLoc(Expr *E) { - if (auto ME = dyn_cast(E)) { - // `use(s.a)`, `s.a` is MemberExpr, if `s.a` has borrow fields, add its - // borrow fields. if (!FoundBorrowMembers.count(ME)) - // FoundBorrowMembers[ME] = CurElemID; - } else if (auto DRE = dyn_cast(E)) { - // `use(s)`, `s` is DeclRefExpr, if `s` has borrow fields - // if (VarDecl* VD = dyn_cast(DRE->getDecl())) - // if (!FoundVars.count(VD)) - // FoundVars[VD] = CurElemID; +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::VisitMEForBorrowFieldPath( - MemberExpr *ME, std::pair &BorrowWithFieldPath) { - if (auto FD = dyn_cast(ME->getMemberDecl())) { - BorrowWithFieldPath.second = - "." + FD->getNameAsString() + BorrowWithFieldPath.second; - if (auto BaseME = dyn_cast(ME->getBase())) - VisitMEForBorrowFieldPath(BaseME, BorrowWithFieldPath); - else if (auto BaseDRE = dyn_cast(ME->getBase())) { - VarDecl *BaseVD = dyn_cast(BaseDRE->getDecl()); - BorrowWithFieldPath.first = BaseVD; +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::VisitMEForTargetFieldPath(MemberExpr *ME, - TargetInfo &TargetWithFieldPath) { +void NLLCalculator::VisitMEForFieldPath( + MemberExpr *ME, std::pair &VDAndFP) { if (auto FD = dyn_cast(ME->getMemberDecl())) { - TargetWithFieldPath.TargetFieldPath = - "." + FD->getNameAsString() + TargetWithFieldPath.TargetFieldPath; + VDAndFP.second = "." + FD->getNameAsString() + VDAndFP.second; if (auto BaseME = dyn_cast(ME->getBase())) - VisitMEForTargetFieldPath(BaseME, TargetWithFieldPath); + VisitMEForFieldPath(BaseME, VDAndFP); else if (auto BaseDRE = dyn_cast(ME->getBase())) { VarDecl *BaseVD = dyn_cast(BaseDRE->getDecl()); - TargetWithFieldPath.TargetVD = BaseVD; + VDAndFP.first = BaseVD; } } } -void NLLCalculator::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::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); +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; } -} -void NLLCalculator::VisitInitForTargets( - Expr *InitE, llvm::SmallVector &Targets) { if (auto UO = dyn_cast(InitE)) { if (UO->getOpcode() == UO_AddrMut || UO->getOpcode() == UO_AddrConst - || UO->getOpcode() == UO_AddrMutDeref || UO->getOpcode() == UO_AddrConstDeref) { - if (auto* DRE = dyn_cast(UO->getSubExpr())) { - // int* borrow p = &mut local; local is DeclRefExpr. - VarDecl* IVD = dyn_cast(DRE->getDecl()); - Targets.push_back(TargetInfo(IVD)); - } else if (auto ASE = dyn_cast(UO->getSubExpr())) { - // int* borrow p = &mut arr[0]; arr[0] is ArraySubscriptExpr. - VisitInitForTargets(ASE->getBase(), Targets); - } else if (auto ICE = dyn_cast(UO->getSubExpr())) { - // int* borrow p = &mut *q; q is ImplicitCastExpr. - VisitInitForTargets(ICE, Targets); - } else if (auto *ME = dyn_cast(UO->getSubExpr())) { - // int* borrow p = &mut s.a.b; s.a.b is MemberExpr. - TargetInfo TargetWithFieldPath; - VisitMEForTargetFieldPath(ME, TargetWithFieldPath); - Targets.push_back(TargetWithFieldPath); - } - } + || UO->getOpcode() == UO_AddrMutDeref || UO->getOpcode() == UO_AddrConstDeref) + VisitInitForTargets(UO->getSubExpr(), Targets); } else if (auto ICE = dyn_cast(InitE)) { - // ImplicitCastExpr: int* borrow p2 = p1; - // p2 refers to p1, if p1 is borrow variable, we add p1 to Targets; - if (auto* DRE = dyn_cast(ICE->getSubExpr())) { - VarDecl* IVD = dyn_cast(DRE->getDecl()); - Targets.push_back(IVD); - } else if (auto *ME = dyn_cast(ICE->getSubExpr())) { - // int* borrow p = s.a.b; s.a.b is MemberExpr. - TargetInfo TargetWithFieldPath; - VisitMEForTargetFieldPath(ME, TargetWithFieldPath); - Targets.push_back(TargetWithFieldPath); - } + // 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)) { - // CallExpr: int* borrow p3 = foo(&mut local1, &mut local2); - // the lifetime of p3 should be smaller than the lifetime intersection of local1 and local2. + // 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) { + 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); } } @@ -1046,36 +1086,52 @@ void NLLCalculator::UpdateNLLWhenTargetFound( // NLL end of no-borrow-no-owned parameter :NumElements + 2 // NLL end of global/virtual ParentVar :NumElements + 3 void NLLCalculator::HandleNLLAfterTraversing( - const llvm::DenseMap &ParamTarget) { + const std::map, TargetInfo> &ParamTarget) { // Handle parameter for (auto Param : ParamTarget) { - ParmVarDecl* PVD = Param.first; + 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(); - // Handle borrow or naked pointer parameter which have virtual ParentVar. - if (PQT->isPointerType() && !PQT.isOwnedQualified()) { - // Update previous NLL whose target is PVD to virtual ParentVar + if (PFP.size() > 0) { llvm::SmallVector VirtualTarget; VirtualTarget.push_back(Param.second); - UpdateNLLWhenTargetFound(TargetInfo(PVD), VirtualTarget); + UpdateNLLWhenTargetFound(TargetInfo(PVD, PFP), 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); + // 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. @@ -1086,7 +1142,7 @@ void NLLCalculator::HandleNLLAfterTraversing( // Compute non-lexical Lifetime for all variables in a certain path. NonLexicalLifetime BorrowCheckImpl::CalculateNLLForPath( const CFGPath &Path, - const llvm::DenseMap &ParamTarget, + const std::map, TargetInfo> &ParamTarget, unsigned NumElements) { NLLCalculator Calculator(Ctx, const_cast(fd.getDeclContext()), NumElements); @@ -1126,7 +1182,7 @@ NonLexicalLifetime BorrowCheckImpl::CalculateNLLForPath( Calculator.HandleNLLAfterTraversing(ParamTarget); // DEBUG PRINT - llvm::outs() << "---------print NLL-----------\n"; + llvm::outs() << "----------------print NLL------------------\n"; for (auto u : Calculator.NLLForAllVars) { llvm::outs() << u.first->getNameAsString() << "\n"; // VarDecl for (auto v : u.second) { // NLL @@ -1139,7 +1195,7 @@ NonLexicalLifetime BorrowCheckImpl::CalculateNLLForPath( if (v.Target.TargetVD) llvm::outs() << " " << v.Target.TargetVD->getNameAsString() << " " << v.Target.TargetFieldPath; - llvm::outs() << "\n"; + llvm::outs() << " " << v.Kind << "\n"; } } llvm::outs() << "\n"; @@ -1148,18 +1204,44 @@ NonLexicalLifetime BorrowCheckImpl::CalculateNLLForPath( } // For borrow or naked pointer parameter, we create virtual ParentVar as target. -// Other kind of parameter don't have target. -void BorrowCheckImpl::BuildParamTarget( - llvm::DenseMap &ParamTarget) { +// Other kind of parameter don't have target. +void BorrowCheckImpl::BuildParamTarget(std::map, TargetInfo>& ParamTarget) { for (ParmVarDecl *PVD : fd.parameters()) { - if (PVD->getType()->isPointerType() && !PVD->getType().isOwnedQualified()) { + 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); - ParamTarget[PVD] = TargetInfo(VD); + 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(); } } @@ -1182,7 +1264,7 @@ void clang::runBorrowCheck(const FunctionDecl &fd, const CFG &cfg, BorrowCheckImpl BC(Ctx, fd); - llvm::DenseMap ParamTarget; + std::map, TargetInfo> ParamTarget; BC.BuildParamTarget(ParamTarget); // First we find all paths from entry block to exit block of a cfg. @@ -1190,7 +1272,7 @@ void clang::runBorrowCheck(const FunctionDecl &fd, const CFG &cfg, CFGPathVec CFGAllPaths = PathFinder.FindPathOfCFG(cfg); // DEBUG PRINT - llvm::outs() << "----------print PathSet----------" << "\n"; + llvm::outs() << "------------------print PathSet----------------" << "\n"; for (CFGPath Path : CFGAllPaths) { for (const CFGBlock* bb : Path) { llvm::outs() << bb->getBlockID() << "->"; -- Gitee