diff --git a/clang/docs/BiShengCLanguageUserManual.md b/clang/docs/BiShengCLanguageUserManual.md index 536aa83772fe55421055aa31d97c03049e71b2cc..373069651ffea9e4c16892a2368691482debaa55 100644 --- a/clang/docs/BiShengCLanguageUserManual.md +++ b/clang/docs/BiShengCLanguageUserManual.md @@ -4020,6 +4020,185 @@ int main() { 注:运算符重载当前未支持源源变换、将在后续版本中支持。 +## 非空指针 +为了提高指针使用的安全性,BSC 为指针引入了可空(Nullable)和非空(Nonnull)属性: +1. 使用`_Nullable`关键字修饰任意指针类型(包括裸指针、owned和borrow指针),表明该指针是可空指针。 +2. 使用`_Nonnull`关键字修饰任意指针类型,表明该指针是非空指针。 +在使用指针时,BSC 编译器会(在编译期)对指针的 nullability 进行检查,避免出现解引用空指针、通过空指针访问成员等不安全行为。 + +BSC 不允许在安全区内使用`NULL`,但是在日常开发中,空指针的使用是不可避免的,例如: +1. 一个指针的指向需要在运行时才能确定,那么我们可以将指针初始化为空指针,在后续再根据运行状态来修改指向 +2. 对于可空指针,在使用前需要判断该指针是否为空指针 +为此 BSC 引入了可以在安全区使用的`nullptr`关键字来替代`NULL`,用户可以定义 Nullable 指针,并将其初始化为`nullptr`,在解引用 Nullable 指针前,也可以使用`nullptr`对指针判空。 + +我们用一个简单的例子来学习如何定义指针的 nullabiliy 并使用它: +```C +#include "bishengc_safety.hbs" + +safe int get_current_status(void); // 获取当前运行状态的函数 + +struct Data { + int value; +}; + +// 如果 init 成功,返回具体的地址,否则返回 nullptr +safe struct Data *owned _Nullable init_data(void) { + if (get_current_status() == 1) { + struct Data data = { 10 }; + struct Data *owned p = safe_malloc(data); + return p; + } + return nullptr; +} + +safe void read_data(struct Data *borrow p); + +safe int main(void) { + // 使用 _Nullable 修饰指针类型: + struct Data *owned _Nullable p = init_data(); + // init 后 p 可能为空指针,因此需要先判空再使用: + if (p != nullptr) { + read_data(&mut *p); // 如果没有对 p 做判空,编译器会报 error! + safe_free((void *owned)p); + } + return 0; +} +``` + +### 指针变量的 Nullability +指针变量的默认 Nullability 跟 qualifier 和指针类型有关: +1. 如果有 qualifier(`_Nonnull`或`_Nullable`)修饰,以这个为准 +2. 如果没有 qualifier 修饰,裸指针默认是 Nullable 的,owned 和 borrow 指针默认是 Nonnull 的 +```C +// Nullable 指针: +int *_Nullable p1 = nullptr; +int *borrow _Nullable p2 = nullptr; +int *owned _Nullable p3 = nullptr; +int * p4 = nullptr; +// Nonnull 指针: +int *_Nonnull p5 = &a; +int *borrow _Nonnull p6 = &mut a; +int *owned _Nonnull p7 = safe_malloc(5); +int *borrow p8 = &mut a; +int *owned p9 = safe_malloc(5); +``` + +对于 Nonnull 指针,它的 Nullability 一定是 Nonnull 的。 +对于 Nullable 指针,它的 Nullability 可能会发生变化: +1. 如果用非空表达式对其进行了赋值,那么在这条赋值语句后面可以把它当做 Nonnull 指针使用 +2. 如果在控制流语句中对其做了判空,那么在非空的分支中,可以把它当做 Nonnull 指针使用 + +```C +safe int *borrow _Nullable nullable(int* borrow p); //返回值类型为 Nullable 的 +safe int *borrow nonnull(int* borrow p); //返回值类型为 Nonnull 的 + +safe void test(void) { + int local = 10; + int *borrow _Nullable p1 = nullptr; // p1 初始化后 Nullability 为 Nullable + *p1 = 10; // error + p1 = &mut local; // p1 被再赋值后 Nullability 变为 Nonnull + *p1 = 20; // ok + p1 = nullable(&mut local); // p1 被再赋值后 Nullability 变为 Nullable + *p1 = 20; // error + p1 = nonnull(&mut local); // p1 被再赋值后 Nullability 变为 Nonnull + *p1 = 20; // ok + + int *borrow _Nullable p2 = nullable(&mut local); // Nullable + if (p2 != nullptr) // if 分支中 p2 的 Nullability 为 Nonnull + *p2 = 10; // ok + else // else 分支中 p2 的 Nullability 为 Nullable + *p2 = 20; // error +} +``` + +### 指针的赋值、传参和返回 +1. 不允许用可空表达式给 Nonnull 指针赋值: +```C +safe int *borrow _Nullable nullable(int* borrow p); //返回值类型为 Nullable +safe int *borrow nonnull(int* borrow p); //返回值类型为 Nonnull + +safe void test(void) { + int local = 10; + int *borrow p1 = nullptr; // error + int *borrow p2 = nullable(&mut local); // error + int *borrow p3 = nonnull(&mut local); // ok + + int *borrow _Nullable p4 = nullable(&mut local); // p4 的 Nullability 是 Nullable + int *borrow p5 = p4; // error + + int *borrow _Nullable p6 = nonnull(&mut local); // p6 的 Nullability 是 Nonnull + int *borrow p7 = p6; // ok +} +``` + +2. 函数调用时,如果形参为 Nonnull 类型,那么不能使用用可空表达式作为实参: +```C +safe void take_nonnull(int *borrow p); // 接收 Nonnull 类型的指针作为参数 +safe int *borrow _Nullable nullable(int* borrow p); // 返回值类型为 Nullable + +safe void test(void) { + int local = 10; + int *borrow _Nullable p = nullable(&mut local); + take_nonnull(p); // error +} +``` + +3. 函数返回值类型如果是 Nonnull 类型,也不能使用可空表达式作为返回值: +```C +safe int *borrow return_nonnull(int *borrow p) { + int *borrow _Nullable q = nullptr; + return q; // error +} +``` + +### 指针的解引用、成员访问 +被定义为 Nullable 的指针的 Nullability 会随赋值和控制流发生变化,BSC 编译器会跟踪这些变换,保证指针解引用、成员访问等操作的安全性。 +```C +struct Data { + int value; +}; +safe struct Data *borrow _Nullable nullable(struct Data *borrow); + +safe void test(void) { + struct Data data = { .value = 10 }; + struct Data *borrow _Nullable p = nullable(&mut data); + if (p != nullptr) { + p->value = 10; // ok,如果没有判空操作,编译器会报 error + } +} +``` + +### 结构体成员是 Nullable 指针 +初始化有 Nullable 指针成员的结构体变量时: +1. 如果是通过初始化列表进行初始化,BSC 编译器会根据初始化表达式来初始化 Nullable 指针成员的 Nullability +2. 对于其它初始化方式,直接认为 Nullable 指针成员的 Nullability 为 Nullable 的,这可能会导致本身没有问题的代码无法通过编译,此时需要对 Nullable 指针成员作再赋值,或使用判空语句,即可改变 Nullability。 +```C +struct Data { int *borrow _Nullable value; }; + +safe struct Data init_data(int* borrow p); +safe int *borrow nonnull(int* borrow p); + +safe void test(void) { + int local = 10; + // 使用初始化列表做初始化: + struct Data data1 = { .value = nonnull(&mut local) };// data1.value 的 Nullability 是 Nonnull + *data1.value = 10; // ok + // 使用函数返回值做初始化: + struct Data data2 = init_data(&mut local); // data2.value 的 Nullability 是 Nullable + *data2.value = 10; // error + // 使用变量赋值做初始化: + struct Data data3 = data1; // data3.value 的 Nullability 是 Nullable + *data3.value = 10; // error + + // 对 Nullable 指针成员作再赋值,可以改变 Nullability: + data2.value = nonnull(&mut local); + *data2.value = 10; // ok + // 通过指针判空语句,也可以改变 Nullability: + if (data3.value != nullptr) + *data2.value = 10; // ok +} +``` + ## 标准库 ### 安全 API diff --git a/clang/include/clang/Analysis/Analyses/BSCNullabilityCheck.h b/clang/include/clang/Analysis/Analyses/BSCNullabilityCheck.h new file mode 100644 index 0000000000000000000000000000000000000000..301900e468bb05cd31421cd7075066ee9de4c69b --- /dev/null +++ b/clang/include/clang/Analysis/Analyses/BSCNullabilityCheck.h @@ -0,0 +1,113 @@ +//===- BSCNullabilityCheck.h - Nullability 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 Pointer Nullability Check for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_BSCNULLABILITYCHECK_H +#define LLVM_CLANG_ANALYSIS_ANALYSES_BSCNULLABILITYCHECK_H + +#if ENABLE_BSC + +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Sema/Sema.h" + +namespace clang { +enum NullabilityCheckDiagKind { + NonnullAssignedByNullable, + PassNullableArgument, + ReturnNullable, + NullablePointerDereference, + NullablePointerAccessMember, +}; + +struct NullabilityCheckDiagInfo { + SourceLocation Loc; + NullabilityCheckDiagKind Kind; + + NullabilityCheckDiagInfo(SourceLocation Loc, NullabilityCheckDiagKind Kind) + : Loc(Loc), Kind(Kind) {} + + bool operator==(const NullabilityCheckDiagInfo &other) const { + return Loc == other.Loc && Kind == other.Kind; + } +}; + +class NullabilityCheckDiagReporter { + Sema &S; + std::vector DIV; + +public: + NullabilityCheckDiagReporter(Sema &S) : S(S) {} + + void addDiags(llvm::SmallVector &diags) { + for (auto it = diags.begin(), ei = diags.end(); it != ei; ++it) { + addDiagInfo(*it); + } + } + + void addDiagInfo(NullabilityCheckDiagInfo &DI) { + for (auto it = DIV.begin(), ei = DIV.end(); it != ei; ++it) { + if (DI == *it) + return; + } + DIV.push_back(DI); + } + + void flushDiagnostics() { + // Sort the diag info by SourceLocation. 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 NullabilityCheckDiagInfo &a, + const NullabilityCheckDiagInfo &b) { + return S.getSourceManager().isBeforeInTranslationUnit(a.Loc, + b.Loc); + }); + + for (const NullabilityCheckDiagInfo &DI : DIV) { + switch (DI.Kind) { + case NonnullAssignedByNullable: + S.Diag(DI.Loc, diag::err_nonnull_assigned_by_nullable); + break; + case PassNullableArgument: + S.Diag(DI.Loc, diag::err_pass_nullable_argument); + break; + case ReturnNullable: + S.Diag(DI.Loc, diag::err_return_nullable); + break; + case NullablePointerDereference: + S.Diag(DI.Loc, diag::err_nullable_pointer_dereference); + break; + case NullablePointerAccessMember: + S.Diag(DI.Loc, diag::err_nullable_pointer_access_member); + break; + default: + llvm_unreachable("Unknown error type"); + break; + } + S.getDiagnostics().increaseNullabilityCheckErrors(); + } + } + + unsigned getNumErrors() { return DIV.size(); } +}; + +void runNullabilityCheck(const FunctionDecl &fd, const CFG &cfg, + AnalysisDeclContext &ac, + NullabilityCheckDiagReporter &reporter, + ASTContext &ctx); + +} // end namespace clang + +#endif // ENABLE_BSC + +#endif // LLVM_CLANG_ANALYSIS_ANALYSES_BSCNULLABILITYCHECK_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 48ce42cf23827b77eb625d5b96de991a165aa877..1443a0ba1362a5eec32eb89b718d44ef8ca73c49 100644 --- a/clang/include/clang/Analysis/Analyses/BSCOwnership.h +++ b/clang/include/clang/Analysis/Analyses/BSCOwnership.h @@ -31,7 +31,7 @@ class CFG; class CFGBlock; class Stmt; class DeclRefExpr; -class DiagInfo; +class OwnershipDiagInfo; class SourceManager; class Ownership : public ManagedAnalysis { @@ -81,57 +81,51 @@ public: void setToOwned(const VarDecl *VD); void setToMoved(const Expr *E); - llvm::SmallVector checkOPSUse(const VarDecl *VD, - const SourceLocation &Loc, - bool isGetAddr, - bool isStar = false); - llvm::SmallVector checkOPSFieldUse(const VarDecl *VD, - const SourceLocation &Loc, - std::string fullFieldName, - bool isGetAddr); - llvm::SmallVector checkOPSAssign(const VarDecl *VD, - const SourceLocation &Loc); - llvm::SmallVector checkOPSAssignStar(const VarDecl *VD, - const SourceLocation &Loc); - llvm::SmallVector + llvm::SmallVector + checkOPSUse(const VarDecl *VD, const SourceLocation &Loc, bool isGetAddr, + bool isStar = false); + llvm::SmallVector + checkOPSFieldUse(const VarDecl *VD, const SourceLocation &Loc, + std::string fullFieldName, bool isGetAddr); + llvm::SmallVector + checkOPSAssign(const VarDecl *VD, const SourceLocation &Loc); + llvm::SmallVector + checkOPSAssignStar(const VarDecl *VD, const SourceLocation &Loc); + llvm::SmallVector checkOPSFieldAssign(const VarDecl *VD, const SourceLocation &Loc, std::string fullFieldName); - llvm::SmallVector checkSUse(const VarDecl *VD, - const SourceLocation &Loc, - bool isGetAddr); - llvm::SmallVector checkSFieldUse(const VarDecl *VD, - const SourceLocation &Loc, - std::string fullFieldName, - bool isGetAddr); - llvm::SmallVector checkSAssign(const VarDecl *VD, - const SourceLocation &Loc); - llvm::SmallVector checkSFieldAssign(const VarDecl *VD, - const SourceLocation &Loc, - std::string fullFieldName); - - llvm::SmallVector checkBOPUse(const VarDecl *VD, - const SourceLocation &Loc, - bool isGetAddr); - llvm::SmallVector checkBOPFieldUse(const VarDecl *VD, - const SourceLocation &Loc, - std::string fullFieldName, - bool isGetAddr); - llvm::SmallVector checkBOPAssign(const VarDecl *VD, - const SourceLocation &Loc); - llvm::SmallVector + llvm::SmallVector + checkSUse(const VarDecl *VD, const SourceLocation &Loc, bool isGetAddr); + llvm::SmallVector + checkSFieldUse(const VarDecl *VD, const SourceLocation &Loc, + std::string fullFieldName, bool isGetAddr); + llvm::SmallVector + checkSAssign(const VarDecl *VD, const SourceLocation &Loc); + llvm::SmallVector + checkSFieldAssign(const VarDecl *VD, const SourceLocation &Loc, + std::string fullFieldName); + + llvm::SmallVector + checkBOPUse(const VarDecl *VD, const SourceLocation &Loc, bool isGetAddr); + llvm::SmallVector + checkBOPFieldUse(const VarDecl *VD, const SourceLocation &Loc, + std::string fullFieldName, bool isGetAddr); + llvm::SmallVector + checkBOPAssign(const VarDecl *VD, const SourceLocation &Loc); + llvm::SmallVector checkBOPFieldAssign(const VarDecl *VD, const SourceLocation &Loc, std::string fullFieldName); - llvm::SmallVector checkCastOPS(const VarDecl *VD, - const SourceLocation &Loc); - llvm::SmallVector checkCastBOP(const VarDecl *VD, - const SourceLocation &Loc); - llvm::SmallVector checkCastField(const VarDecl *VD, - const SourceLocation &Loc, - std::string fullFieldName); - llvm::SmallVector checkMemoryLeak(const VarDecl *VD, - const SourceLocation &Loc); + llvm::SmallVector + checkCastOPS(const VarDecl *VD, const SourceLocation &Loc); + llvm::SmallVector + checkCastBOP(const VarDecl *VD, const SourceLocation &Loc); + llvm::SmallVector + checkCastField(const VarDecl *VD, const SourceLocation &Loc, + std::string fullFieldName); + llvm::SmallVector + checkMemoryLeak(const VarDecl *VD, const SourceLocation &Loc); OwnershipStatus() : OPSStatus(0), OPSAllOwnedFields(0), OPSOwnedOwnedFields(0), @@ -165,7 +159,7 @@ public: }; }; -enum DiagKind { +enum OwnershipDiagKind { InvalidUseOfMoved, InvalidUseOfPartiallyMoved, InvalidUseOfAllMoved, @@ -187,28 +181,30 @@ enum DiagKind { MemoryLeak, }; -class DiagInfo { +class OwnershipDiagInfo { public: SourceLocation Loc; - DiagKind Kind; + OwnershipDiagKind Kind; std::string Name; std::string Fields; SourceLocation Location; - DiagInfo(SourceLocation Loc, DiagKind Kind, std::string Name) + OwnershipDiagInfo(SourceLocation Loc, OwnershipDiagKind Kind, + std::string Name) : Loc(Loc), Kind(Kind), Name(Name), Fields(""), Location(SourceLocation()) {} - DiagInfo(SourceLocation Loc, DiagKind Kind, std::string Name, - std::string fields) + OwnershipDiagInfo(SourceLocation Loc, OwnershipDiagKind Kind, + std::string Name, std::string fields) : Loc(Loc), Kind(Kind), Name(Name), Fields(fields), Location(SourceLocation()) {} - DiagInfo(SourceLocation Loc, DiagKind Kind, std::string Name, - std::string fields, SourceLocation location) + OwnershipDiagInfo(SourceLocation Loc, OwnershipDiagKind Kind, + std::string Name, std::string fields, + SourceLocation location) : Loc(Loc), Kind(Kind), Name(Name), Fields(fields), Location(location) {} - bool operator==(const DiagInfo &other) const { + bool operator==(const OwnershipDiagInfo &other) const { return Loc == other.Loc && Kind == other.Kind && Name == other.Name && Fields == other.Fields && Location == other.Location; } @@ -216,18 +212,18 @@ public: class OwnershipDiagReporter { Sema &S; - std::vector DIV; + std::vector DIV; public: OwnershipDiagReporter(Sema &S) : S(S) {} - void addDiags(llvm::SmallVector &diags) { + void addDiags(llvm::SmallVector &diags) { for (auto it = diags.begin(), ei = diags.end(); it != ei; ++it) { addDiagInfo(*it); } } - void addDiagInfo(DiagInfo &DI) { + void addDiagInfo(OwnershipDiagInfo &DI) { for (auto it = DIV.begin(), ei = DIV.end(); it != ei; ++it) { if (DI == *it) return; @@ -239,12 +235,13 @@ public: // Sort the diag info by SourceLocation. 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 DiagInfo &a, const DiagInfo &b) { - return S.getSourceManager().isBeforeInTranslationUnit(a.Loc, b.Loc); - }); + std::sort(DIV.begin(), DIV.end(), + [this](const OwnershipDiagInfo &a, const OwnershipDiagInfo &b) { + return S.getSourceManager().isBeforeInTranslationUnit(a.Loc, + b.Loc); + }); - for (const DiagInfo &DI : DIV) { + for (const OwnershipDiagInfo &DI : DIV) { switch (DI.Kind) { case InvalidUseOfMoved: S.Diag(DI.Loc, diag::err_ownership_use_moved) << DI.Name; diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td index 06596c8367305565921260c8a4921d3f3a603e75..7eff2d0fa29da3abcd669a7ae89a823c59fdae05 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -191,3 +191,15 @@ def err_use_expired_borrow_var : Error< // BSC operator overload errors. def err_unsupport_overload_fun : Error<"function unsupport overload">; + +// BSC Pointer Nullability Check errors. +def err_nullable_pointer_dereference : Error< + "nullable pointer cannot be dereferenced">; +def err_pass_nullable_argument : Error< + "cannot pass nullable pointer argument">; +def err_return_nullable : Error< + "cannot return nullable pointer type">; +def err_nullable_pointer_access_member : Error< + "cannot access member througn nullable pointer">; +def err_nonnull_assigned_by_nullable : Error< + "nonnull pointer cannot be assigned by nullable pointer">; \ No newline at end of file diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index bf96300ec317bd57ba0eb0efc818b3fbbb2379f3..aacc32861294bec50eb61dc34e8640bdd41427ef 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -498,6 +498,7 @@ private: #if ENABLE_BSC unsigned NumOwnershipErrors; unsigned NumBorrowCheckErrors; + unsigned NumNullabilityCheckErrors; #endif /// A function pointer that converts an opaque diagnostic @@ -871,6 +872,10 @@ public: void increaseBorrowCheckErrors() { NumBorrowCheckErrors++; } + unsigned getNumNullabilityCheckErrors() const { + return NumNullabilityCheckErrors; + } + void increaseNullabilityCheckErrors() { NumNullabilityCheckErrors++; } #endif /// Return an ID for a diagnostic with the specified format string and diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 998fb3ffee492506567cf102758df7b7983fd241..f4d6c978086cb57835692116d937a70b1b9ae180 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -261,6 +261,7 @@ LANGOPT(HLSL, 1, 0, "HLSL") #if ENABLE_BSC LANGOPT(BSC, 1, 0, "BSC") LANGOPT(DisableOwnershipCheck, 1, 0, "disable ownership check") +LANGOPT(DisableNullabilityCheck, 1, 0, "disable nullability check") #endif ENUM_LANGOPT(HLSLVersion, HLSLLangStd, 16, HLSL_Unset, "HLSL Version") diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index bcdd3c6aaca5021bbac95e9269d5bf6064f642ae..0def6903efe1341709f6d73ebc912c2d1a8af691 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -401,12 +401,13 @@ CXX11_KEYWORD(char16_t , KEYNOMS18) CXX11_KEYWORD(char32_t , KEYNOMS18) #if ENABLE_BSC CXX11_KEYWORD(constexpr , KEYBSC) +CXX11_KEYWORD(nullptr , KEYBSC) #else CXX11_KEYWORD(constexpr , 0) +CXX11_KEYWORD(nullptr , 0) #endif CXX11_KEYWORD(decltype , 0) CXX11_KEYWORD(noexcept , 0) -CXX11_KEYWORD(nullptr , 0) CXX11_KEYWORD(static_assert , KEYMSCOMPAT) CXX11_KEYWORD(thread_local , 0) diff --git a/clang/include/clang/Driver/BSC/BSCOptions.td b/clang/include/clang/Driver/BSC/BSCOptions.td index f29c8576c242e68f3a667194635d53ef270e93c1..88151b78ade3aa9ac7a52aecf0e6515736cd87dd 100644 --- a/clang/include/clang/Driver/BSC/BSCOptions.td +++ b/clang/include/clang/Driver/BSC/BSCOptions.td @@ -6,3 +6,5 @@ def rewrite_bsc_line : Flag<["-"], "line">, Flags<[CC1Option]>, HelpText<"Insert line info when rewrite BSC">; def disable_ownership_check : Flag<["-"], "disable-ownership-check">, Flags<[CC1Option]>, HelpText<"Disable ownership check">, MarshallingInfoFlag>; +def disable_nullability_check : Flag<["-"], "disable-nullability-check">, Flags<[CC1Option]>, + HelpText<"Disable Nullability Check">, MarshallingInfoFlag>; \ No newline at end of file diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h index a6266c2c70b10fbbe2f721334ae659437cbf49e5..f5f7f704f943a8b1c5e8404faf4fac03aa39acfd 100644 --- a/clang/include/clang/Sema/Initialization.h +++ b/clang/include/clang/Sema/Initialization.h @@ -266,6 +266,12 @@ public: Entity.Kind = EK_Parameter; Entity.Type = Context.getVariableArrayDecayedType(Type.getUnqualifiedType()); +#if ENABLE_BSC + if (Context.getLangOpts().BSC) + if (const AttributedType *AT = Type->getAs()) + Entity.Type = Context.getAttributedType(AT->getAttrKind(), Entity.Type, + Entity.Type); +#endif Entity.Parent = nullptr; Entity.Parameter = {Parm, Consumed}; return Entity; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index f1ce081b980c28115e386c90c297ebd1f0100e59..efa6a48afe4ad187adf1ff7852cbba63b201da86 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12385,7 +12385,8 @@ public: void CheckOwnedOrIndirectOwnedType(SourceLocation ErrLoc, QualType T, StringRef Env); bool CheckOwnedDecl(SourceLocation ErrLoc, QualType T); bool CheckTemporaryVarMemoryLeak(Expr* E); - void CheckBSCOwnership(const Decl *D); + void BSCDataflowAnalysis(const Decl *D, bool DisableOwnershipCheck = false, + bool DisableNullabilityCheck = false); bool IsInSafeZone(); bool IsSafeBuiltinTypeConversion(BuiltinType::Kind SourceType, BuiltinType::Kind DestType); diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 9303dc60cbeb519f2206a6467374f97830244013..1aea4e166576c434f2cababaec592974fd07a8e4 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2301,7 +2301,12 @@ void StmtPrinter::VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *Node) { } void StmtPrinter::VisitCXXNullPtrLiteralExpr(CXXNullPtrLiteralExpr *Node) { - OS << "nullptr"; +#if ENABLE_BSC + if (Policy.RewriteBSC) + OS << "0"; + else +#endif + OS << "nullptr"; } void StmtPrinter::VisitCXXThisExpr(CXXThisExpr *Node) { diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 05da1652a71770acf0ca02e0fe3682de3cdb3636..5a1f820f107c108f3122364fdd22f4d7b8314e6c 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3222,8 +3222,16 @@ QualType QualType::getNonLValueExprType(const ASTContext &Context) const { // // See also C99 6.3.2.1p2. if (!Context.getLangOpts().CPlusPlus || - (!getTypePtr()->isDependentType() && !getTypePtr()->isRecordType())) + (!getTypePtr()->isDependentType() && !getTypePtr()->isRecordType())) { +#if ENABLE_BSC + if (Context.getLangOpts().BSC) + if (const AttributedType *AT = + dyn_cast(this->getTypePtr())) + return Context.getAttributedType( + AT->getAttrKind(), getUnqualifiedType(), getUnqualifiedType()); +#endif return getUnqualifiedType(); + } return *this; } diff --git a/clang/lib/Analysis/BSCBorrowCheck.cpp b/clang/lib/Analysis/BSCBorrowCheck.cpp index d607c6b82e183068bb1e7d240e522c5cee7aa230..bc9243999a9c8dceb779aaa1aa5a2568c14eb098 100644 --- a/clang/lib/Analysis/BSCBorrowCheck.cpp +++ b/clang/lib/Analysis/BSCBorrowCheck.cpp @@ -1309,7 +1309,9 @@ void NLLCalculator::VisitDeclStmt(DeclStmt *DS) { // @endcode if (isa(VQT->getPointeeType())) { // If InitExpr is not CallExpr Or ConditionalOperator, this borrow pointer must have only one target. - if (!(isa(VD->getInit()) || isa(VD->getInit()))) + if (!(isa(VD->getInit()) || + isa(VD->getInit())) && + Targets.size() == 1) UpdateTargetOfFieldsOfBorrowStruct(BorrowTargetInfo(VD), Targets[0]); } } else if (IsStructAndHasBorrowFields(VQT)) { diff --git a/clang/lib/Analysis/BSCNullabilityCheck.cpp b/clang/lib/Analysis/BSCNullabilityCheck.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f9744c2327b08f31a070e2ee5724b7816dacdb3 --- /dev/null +++ b/clang/lib/Analysis/BSCNullabilityCheck.cpp @@ -0,0 +1,695 @@ +//===- BSCNullabilityCheck.cpp - Nullability 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 Pointer Nullability Check for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#if ENABLE_BSC + +#include "clang/Analysis/Analyses/BSCNullabilityCheck.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/FlowSensitive/DataflowWorklist.h" +#include "llvm/ADT/DenseMap.h" + +using namespace clang; +using namespace std; + +// DefNullability is determined when a pointer is declared: +// 1. has nullability specifier, DefNullability depends on nullability +// specifier. +// 2. has no nullability specifier, DefNullability depends on type: +// 1) raw pointer is nullable by default, +// 2) owned or borrow pointer is nonnull by default. +// Pointer with nullable DefNullability have PathNullability, +// which will change with control flow. +using StatusVD = llvm::DenseMap; +using FieldPath = std::pair; +using StatusFP = std::map; + +class NullabilityCheckImpl { +public: + llvm::DenseMap BlocksBeginStatusVD; + llvm::DenseMap BlocksEndStatusVD; + + llvm::DenseMap BlocksBeginStatusFP; + llvm::DenseMap BlocksEndStatusFP; + // For branch statement with condition, such as IfStmt, WhileStmt, + // true branch and else branch may have different status. + // For example: + // @code + // int *p = nullptr; + // if (p != nullptr) { + // *p = 5; // p is NonNull, so deref p is Ok + // } else { + // *p = 10; // p is Nullable, so deref p is forbidden + // } + // @endcode + // CFG is: + // B4(has condition as terminitor) + // true / \ false + // B3 B2 + // \ / + // B1 + // BlocksConditionStatus records condition status: + // Key is current BB, value is condition status passed from pred BB to current + // BB, for this example, BlocksConditionStatusVD will be: + // B3 : { B4 : p NonNull } B2 : { B4 : p Nullable } + llvm::DenseMap< + const CFGBlock *, + llvm::DenseMap>> + BlocksConditionStatusVD; + llvm::DenseMap< + const CFGBlock *, + llvm::DenseMap>> + BlocksConditionStatusFP; + + StatusVD mergeVD(StatusVD statusA, StatusVD statusB); + StatusFP mergeFP(StatusFP statusA, StatusFP statusB); + + std::pair + runOnBlock(const CFGBlock *block, StatusVD statusVD, StatusFP statusFP, + NullabilityCheckDiagReporter &reporter, ASTContext &ctx, + const FunctionDecl &fd); + void initStatus(const CFG &cfg, ASTContext &ctx); + + NullabilityCheckImpl() + : BlocksBeginStatusVD(0), BlocksEndStatusVD(0), BlocksBeginStatusFP(0), + BlocksEndStatusFP(0), BlocksConditionStatusVD(0), + BlocksConditionStatusFP(0) {} +}; + +//===----------------------------------------------------------------------===// +// Dataflow computation. +//===----------------------------------------------------------------------===// +namespace { +class TransferFunctions : public StmtVisitor { + NullabilityCheckImpl &NCI; + const CFGBlock *Block; + StatusVD &CurrStatusVD; + StatusFP &CurrStatusFP; + NullabilityCheckDiagReporter &Reporter; + ASTContext &Ctx; + const FunctionDecl &Fd; + +public: + TransferFunctions(NullabilityCheckImpl &nci, const CFGBlock *block, + StatusVD &statusVD, StatusFP &statusFP, + NullabilityCheckDiagReporter &reporter, ASTContext &ctx, + const FunctionDecl &fd) + : NCI(nci), Block(block), CurrStatusVD(statusVD), CurrStatusFP(statusFP), + Reporter(reporter), Ctx(ctx), Fd(fd) {} + + void VisitDeclStmt(DeclStmt *S); + void HandleVarDeclInit(VarDecl *VD); + void HandlePointerInit(VarDecl *VD); + void HandleStructInit(VarDecl *VD); + void HandleFieldInit(VarDecl *VD, FieldDecl *FD, Expr *FieldInit); + void VisitBinaryOperator(BinaryOperator *BO); + void VisitUnaryOperator(UnaryOperator *UO); + void VisitMemberExpr(MemberExpr *ME); + void VisitCallExpr(CallExpr *CE); + void VisitReturnStmt(ReturnStmt *RS); + NullabilityKind getExprPathNullability(Expr *E); + void PassConditionStatusToSuccBlocks(Stmt *Cond); +}; +} // namespace + +static NullabilityKind getDefNullability(QualType QT, ASTContext &Ctx) { + QualType CanQT = QT.getCanonicalType(); + if (CanQT->isPointerType()) { + Optional Kind = QT->getNullability(Ctx); + if (Kind && (*Kind == NullabilityKind::NonNull || + *Kind == NullabilityKind::Nullable)) { + return *Kind; + } else if (CanQT.isOwnedQualified() || CanQT.isBorrowQualified()) { + return NullabilityKind::NonNull; + } else // Raw Pointer is nullable by default. + return NullabilityKind::Nullable; + } + return NullabilityKind::Unspecified; +} + +static void VisitMEForFieldPath(Expr *E, FieldPath &FP) { + if (auto ME = dyn_cast(E)) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + FP.second = "." + FD->getNameAsString() + FP.second; + VisitMEForFieldPath(ME->getBase(), FP); + } + } else if (auto DRE = dyn_cast(E)) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) + FP.first = VD; + } else if (auto ICE = dyn_cast(E)) { + VisitMEForFieldPath(ICE->getSubExpr(), FP); + } else if (auto PE = dyn_cast(E)) { + VisitMEForFieldPath(PE->getSubExpr(), FP); + } +} + +static VarDecl *getVarDeclFromExpr(Expr *E) { + if (auto DRE = dyn_cast(E)) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) + return VD; + } else if (auto ICE = dyn_cast(E)) { + return getVarDeclFromExpr(ICE->getSubExpr()); + } else if (auto PE = dyn_cast(E)) { + return getVarDeclFromExpr(PE->getSubExpr()); + } + return nullptr; +} + +static MemberExpr *getMemberExprFromExpr(Expr *E) { + if (auto ME = dyn_cast(E)) { + return ME; + } else if (auto ICE = dyn_cast(E)) { + return getMemberExprFromExpr(ICE->getSubExpr()); + } else if (auto PE = dyn_cast(E)) { + return getMemberExprFromExpr(PE->getSubExpr()); + } + return nullptr; +} + +// If current expr is equal to 0, return true. +// Such as : nullptr, 0, (void*)0, ((void*)0), (int*)0, +// (int* borrow)(void*)0, (int* owned)(void*)0, +// also include constant value which equals to 0 +static bool isNullExpr(const Expr *E, ASTContext &Ctx) { + if (isa(E)) { + return true; + } else if (const IntegerLiteral *IL = dyn_cast(E)) { + if (IL->getValue().getZExtValue() == 0) + return true; + } else if (const CStyleCastExpr *CSCE = dyn_cast(E)) { + return isNullExpr(CSCE->getSubExpr(), Ctx); + } else if (const ImplicitCastExpr *ICE = dyn_cast(E)) { + return isNullExpr(ICE->getSubExpr(), Ctx); + } else if (const ParenExpr *PE = dyn_cast(E)) { + return isNullExpr(PE->getSubExpr(), Ctx); + } else if (Optional I = E->getIntegerConstantExpr(Ctx)) { + if (*I == 0) + return true; + } + return false; +} + +// We can get PathNullability for these exprs: +// 1. int *p = nullptr; // nullptr is NullExpr +// 2. int *p = foo(); // foo() is CallExpr +// 3. int *p = &a; // &a is UnaryOperator +// 4. int *p = p1; // p1 is VarDecl +// 5. int *p = s.p; // s.p is MemberExpr +// 6. int *p = a == 1 ? nullptr : &a; // ConditionOperator +NullabilityKind TransferFunctions::getExprPathNullability(Expr *E) { + QualType QT = E->getType(); + QualType CanQT = QT.getCanonicalType(); + if (CanQT->isPointerType()) { + if (isNullExpr(E, Ctx)) + return NullabilityKind::Nullable; + if (isa(E)) + return getDefNullability(QT, Ctx); + if (auto CSCE = dyn_cast(E)) + return getDefNullability(CSCE->getTypeAsWritten(), Ctx); + if (auto UO = dyn_cast(E)) { + UnaryOperator::Opcode Op = UO->getOpcode(); + if (Op == UO_AddrOf || Op == UO_AddrMut || Op == UO_AddrConst || + Op == UO_AddrMutDeref || Op == UO_AddrConstDeref) + return NullabilityKind::NonNull; + } + if (VarDecl *VD = getVarDeclFromExpr(E)) { + NullabilityKind NK = getDefNullability(VD->getType(), Ctx); + if (NK == NullabilityKind::NonNull) + return NullabilityKind::NonNull; + else if (NK == NullabilityKind::Nullable && CurrStatusVD.count(VD)) + return CurrStatusVD[VD]; + } + if (MemberExpr *ME = getMemberExprFromExpr(E)) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + NullabilityKind NK = getDefNullability(FD->getType(), Ctx); + if (NK == NullabilityKind::NonNull) + return NullabilityKind::NonNull; + else if (NK == NullabilityKind::Nullable) { + FieldPath FP; + VisitMEForFieldPath(ME, FP); + if (CurrStatusFP.count(FP)) + return CurrStatusFP[FP]; + } + } + } + if (ConditionalOperator *CO = dyn_cast(E)) { + NullabilityKind LHSNK = getExprPathNullability(CO->getLHS()); + NullabilityKind RHSNK = getExprPathNullability(CO->getRHS()); + if (LHSNK == NullabilityKind::Nullable || + RHSNK == NullabilityKind::Nullable) + return NullabilityKind::Nullable; + else if (LHSNK == NullabilityKind::NonNull || + RHSNK == NullabilityKind::NonNull) + return NullabilityKind::NonNull; + } + } + // For no-pointer type, we treat it as Unspecified. + return NullabilityKind::Unspecified; +} + +void TransferFunctions::VisitDeclStmt(DeclStmt *DS) { + for (Decl *D : DS->decls()) + if (VarDecl *VD = dyn_cast(D)) + if (VD->getInit()) + HandleVarDeclInit(VD); +} + +void TransferFunctions::HandleVarDeclInit(VarDecl *VD) { + QualType VQT = VD->getType(); + if (VQT->isPointerType()) + HandlePointerInit(VD); + else if (VQT->isRecordType()) + HandleStructInit(VD); +} + +// Init a pointer variable +void TransferFunctions::HandlePointerInit(VarDecl *VD) { + NullabilityKind LHSKind = getDefNullability(VD->getType(), Ctx); + NullabilityKind RHSKind = getExprPathNullability(VD->getInit()); + if (LHSKind == NullabilityKind::NonNull) { + // NonNull pointer cannot be assigned by expr + // whose PathNullability is nullable. + if (RHSKind == NullabilityKind::Nullable) { + NullabilityCheckDiagInfo DI(VD->getLocation(), + NonnullAssignedByNullable); + Reporter.addDiagInfo(DI); + } + } else if (CurrStatusVD.count(VD)) + // Here we update PathNullability of nullable pointer. + CurrStatusVD[VD] = RHSKind; +} + +// Init a struct variable with pointer fields. +void TransferFunctions::HandleStructInit(VarDecl *VD) { + auto RecTy = dyn_cast(VD->getType().getCanonicalType()); + if (RecordDecl *RD = RecTy->getDecl()) { + if (auto InitListE = dyn_cast(VD->getInit())) { + // Init by InitListExpr, such as `struct S s = { .a = &local };` + Expr **Inits = InitListE->getInits(); + for (FieldDecl *FD : RD->fields()) + if (FD->getType()->isPointerType()) + HandleFieldInit(VD, FD, Inits[FD->getFieldIndex()]); + } + } +} + +void TransferFunctions::HandleFieldInit(VarDecl *VD, FieldDecl *FD, Expr *FieldInit) { + NullabilityKind LHSKind = getDefNullability(FD->getType(), Ctx); + NullabilityKind RHSKind = getExprPathNullability(FieldInit); + if (LHSKind == NullabilityKind::NonNull) { + if (RHSKind == NullabilityKind::Nullable) { + NullabilityCheckDiagInfo DI(FieldInit->getBeginLoc(), NonnullAssignedByNullable); + Reporter.addDiagInfo(DI); + } + } else { + FieldPath FP(VD, "." + FD->getNameAsString()); + if (CurrStatusFP.count(FP)) + CurrStatusFP[FP] = RHSKind; + } +} + +void TransferFunctions::VisitBinaryOperator(BinaryOperator *BO) { + if (BO->isAssignmentOp()) { + Expr *LHS = BO->getLHS(); + QualType LHSQT = LHS->getType(); + if (LHSQT.getCanonicalType()->isPointerType()) { + NullabilityKind RHSKind = getExprPathNullability(BO->getRHS()); + if (VarDecl *VD = getVarDeclFromExpr(LHS)) { + NullabilityKind LHSKind = getDefNullability(VD->getType(), Ctx); + if (LHSKind == NullabilityKind::NonNull) { + // NonNull pointer cannot be assigned by expr + // whose PathNullability is nullable. + if (RHSKind == NullabilityKind::Nullable) { + NullabilityCheckDiagInfo DI(BO->getBeginLoc(), + NonnullAssignedByNullable); + Reporter.addDiagInfo(DI); + } + } else if (CurrStatusVD.count(VD)) { + // Here we update PathNullability of nullable pointer. + CurrStatusVD[VD] = RHSKind; + } + } else if (MemberExpr *ME = getMemberExprFromExpr(LHS)) { + if (FieldDecl *FD = dyn_cast(ME->getMemberDecl())) { + NullabilityKind LHSKind = getDefNullability(FD->getType(), Ctx); + if (LHSKind == NullabilityKind::NonNull) { + if (RHSKind == NullabilityKind::Nullable) { + NullabilityCheckDiagInfo DI(ME->getBeginLoc(), + NonnullAssignedByNullable); + Reporter.addDiagInfo(DI); + } + } else { + FieldPath FP; + VisitMEForFieldPath(ME, FP); + if (CurrStatusFP.count(FP)) + CurrStatusFP[FP] = RHSKind; + } + } + } + } + } +} + +// NonNull parameter cannot take Nullable pointer as argument. +void TransferFunctions::VisitCallExpr(CallExpr *CE) { + if (FunctionDecl *FD = CE->getDirectCallee()) { + for (unsigned i = 0; i < FD->getNumParams(); i++) { + ParmVarDecl *PVD = FD->getParamDecl(i); + if (getDefNullability(PVD->getType(), Ctx) == NullabilityKind::NonNull) { + Expr *ArgE = CE->getArg(i); + if (getExprPathNullability(ArgE) == NullabilityKind::Nullable) { + NullabilityCheckDiagInfo DI(ArgE->getBeginLoc(), + PassNullableArgument); + Reporter.addDiagInfo(DI); + } + } + } + } +} + +// *p, &mut *p, &const *p is not allowed when p has nullable PathNullability. +void TransferFunctions::VisitUnaryOperator(UnaryOperator *UO) { + UnaryOperator::Opcode Op = UO->getOpcode(); + if (Op == UO_Deref || Op == UO_AddrMutDeref || Op == UO_AddrConstDeref) { + if (getExprPathNullability(UO->getSubExpr()) == NullabilityKind::Nullable) { + NullabilityCheckDiagInfo DI(UO->getBeginLoc(), + NullablePointerDereference); + Reporter.addDiagInfo(DI); + } + } +} + +// p->a is not allowed when p has nullable PathNullability. +void TransferFunctions::VisitMemberExpr(MemberExpr *ME) { + if (ME->isArrow()) { + if (getExprPathNullability(ME->getBase()) == NullabilityKind::Nullable) { + NullabilityCheckDiagInfo DI(ME->getBeginLoc(), + NullablePointerAccessMember); + Reporter.addDiagInfo(DI); + } + } +} + +// If function return type is NonNull, cannot return pointer +// which has nullable PathNullability. +void TransferFunctions::VisitReturnStmt(ReturnStmt *RS) { + Expr *RV = RS->getRetValue(); + if (!RV) + return; + if (getDefNullability(Fd.getReturnType(), Ctx) == NullabilityKind::NonNull) { + if (getExprPathNullability(RV) == NullabilityKind::Nullable) { + NullabilityCheckDiagInfo DI(RV->getBeginLoc(), ReturnNullable); + Reporter.addDiagInfo(DI); + } + } +} + +// This function handles condition expression and +// pass the conditions to successor block. +void TransferFunctions::PassConditionStatusToSuccBlocks(Stmt *Cond) { + CFGBlock::const_succ_iterator it = Block->succ_begin(); + if (const CFGBlock *TrueBlock = *it) { + it++; + if (const CFGBlock *FalseBlock = *it) { + // When building CFG, if a block has a condition as terminitor, + // the successors has certain order, for example: + // B4(has condition as terminitor) + // / \ + // B3 B2 + // The first successor of B4 must be true branch, + // and the second successor of B4 must be false branch. + // Here we only handle terminators with two successors. + if (auto BO = dyn_cast(Cond)) { + // Condition expr is BinaryOperator, such as: + // if (p != nullptr), if (s.p != nullptr), if (p == nullptr), if (s.p != + // nullptr), ... + if (BO->isEqualityOp() && isNullExpr(BO->getRHS(), Ctx)) { + Expr *LHS = BO->getLHS(); + if (VarDecl *VD = getVarDeclFromExpr(LHS)) { + if (getDefNullability(VD->getType(), Ctx) == + NullabilityKind::Nullable) { + NullabilityKind TrueKind = BO->getOpcode() == BO_EQ + ? NullabilityKind::Nullable + : NullabilityKind::NonNull; + NullabilityKind FalseKind = BO->getOpcode() == BO_EQ + ? NullabilityKind::NonNull + : NullabilityKind::Nullable; + NCI.BlocksConditionStatusVD[TrueBlock][Block] = + std::pair(VD, TrueKind); + NCI.BlocksConditionStatusVD[FalseBlock][Block] = + std::pair(VD, FalseKind); + } + } else if (MemberExpr *ME = getMemberExprFromExpr(LHS)) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + if (getDefNullability(FD->getType(), Ctx) == + NullabilityKind::Nullable) { + NullabilityKind TrueKind = BO->getOpcode() == BO_EQ + ? NullabilityKind::Nullable + : NullabilityKind::NonNull; + NullabilityKind FalseKind = BO->getOpcode() == BO_EQ + ? NullabilityKind::NonNull + : NullabilityKind::Nullable; + FieldPath FP; + VisitMEForFieldPath(ME, FP); + NCI.BlocksConditionStatusFP[TrueBlock][Block] = + std::pair(FP, TrueKind); + NCI.BlocksConditionStatusFP[FalseBlock][Block] = + std::pair(FP, FalseKind); + } + } + } + } + } else if (auto ICE = dyn_cast(Cond)) { + // Condition expr is ImplicitCastExpr, such as: if (p), if (s.p), ... + if (VarDecl *VD = getVarDeclFromExpr(ICE)) { + if (getDefNullability(VD->getType(), Ctx) == + NullabilityKind::Nullable) { + NCI.BlocksConditionStatusVD[TrueBlock][Block] = + std::pair(VD, + NullabilityKind::NonNull); + NCI.BlocksConditionStatusVD[FalseBlock][Block] = + std::pair( + VD, NullabilityKind::Nullable); + } + } else if (MemberExpr *ME = getMemberExprFromExpr(ICE)) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + if (getDefNullability(FD->getType(), Ctx) == + NullabilityKind::Nullable) { + FieldPath FP; + VisitMEForFieldPath(ME, FP); + NCI.BlocksConditionStatusFP[TrueBlock][Block] = + std::pair( + FP, NullabilityKind::NonNull); + NCI.BlocksConditionStatusFP[FalseBlock][Block] = + std::pair( + FP, NullabilityKind::Nullable); + } + } + } + } else if (auto UO = dyn_cast(Cond)) { + // Condition expr is UnaryOperator, such as: + // if (!p), if (!s.p), ... + if (UO->getOpcode() == UO_LNot) { + if (VarDecl *VD = getVarDeclFromExpr(UO->getSubExpr())) { + if (getDefNullability(VD->getType(), Ctx) == + NullabilityKind::Nullable) { + NCI.BlocksConditionStatusVD[TrueBlock][Block] = + std::pair( + VD, NullabilityKind::Nullable); + NCI.BlocksConditionStatusVD[FalseBlock][Block] = + std::pair( + VD, NullabilityKind::NonNull); + } + } else if (MemberExpr *ME = getMemberExprFromExpr(UO->getSubExpr())) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + if (getDefNullability(FD->getType(), Ctx) == + NullabilityKind::Nullable) { + FieldPath FP; + VisitMEForFieldPath(ME, FP); + NCI.BlocksConditionStatusFP[TrueBlock][Block] = + std::pair( + FP, NullabilityKind::Nullable); + NCI.BlocksConditionStatusFP[FalseBlock][Block] = + std::pair( + FP, NullabilityKind::NonNull); + } + } + } + } + } + } + } +} + +// Traverse all blocks of cfg to collect all nullable pointers used, +// including local and global variable and parameters. +// Init PathNullability of these pointers by Nullable. +void NullabilityCheckImpl::initStatus(const CFG &cfg, ASTContext &ctx) { + const CFGBlock *entry = &cfg.getEntry(); + for (const CFGBlock *B : cfg.const_nodes()) { + if (B != entry && B != &cfg.getExit() && !B->succ_empty() && + !B->pred_empty()) { + for (CFGBlock::const_iterator it = B->begin(), ei = B->end(); it != ei; + ++it) { + const CFGElement &elem = *it; + if (elem.getAs()) { + Stmt *S = const_cast(elem.castAs().getStmt()); + if (auto DRE = dyn_cast(S)) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) + if (getDefNullability(VD->getType(), ctx) == + NullabilityKind::Nullable) + BlocksEndStatusVD[entry][VD] = NullabilityKind::Nullable; + } else if (auto ME = dyn_cast(S)) { + if (FieldDecl *FD = dyn_cast(ME->getMemberDecl())) { + if (getDefNullability(FD->getType(), ctx) == + NullabilityKind::Nullable) { + FieldPath FP; + VisitMEForFieldPath(ME, FP); + BlocksEndStatusFP[entry][FP] = NullabilityKind::Nullable; + } + } + } + } + } + } + } +} + +StatusVD NullabilityCheckImpl::mergeVD(StatusVD statusA, StatusVD statusB) { + if (statusA.empty()) + return statusB; + for (auto NullabilityOfVD : statusB) { + VarDecl *VD = NullabilityOfVD.first; + NullabilityKind NK = NullabilityOfVD.second; + if (statusA.count(VD)) { + statusA[VD] = NK == NullabilityKind::Nullable ? NullabilityKind::Nullable + : statusA[VD]; + } else { + statusA[VD] = NK; + } + } + return statusA; +} + +StatusFP NullabilityCheckImpl::mergeFP(StatusFP statusA, StatusFP statusB) { + if (statusA.empty()) + return statusB; + for (auto NullabilityOfFP : statusB) { + FieldPath FP = NullabilityOfFP.first; + NullabilityKind NK = NullabilityOfFP.second; + if (statusA.count(FP)) { + statusA[FP] = NK == NullabilityKind::Nullable ? NullabilityKind::Nullable + : statusA[FP]; + } else { + statusA[FP] = NK; + } + } + return statusA; +} + +std::pair +NullabilityCheckImpl::runOnBlock(const CFGBlock *block, StatusVD statusVD, + StatusFP statusFP, + NullabilityCheckDiagReporter &reporter, + ASTContext &ctx, const FunctionDecl &fd) { + TransferFunctions TF(*this, block, statusVD, statusFP, reporter, ctx, fd); + + for (CFGBlock::const_iterator it = block->begin(), ei = block->end(); + it != ei; ++it) { + const CFGElement &elem = *it; + + if (elem.getAs()) { + const Stmt *S = elem.castAs().getStmt(); + TF.Visit(const_cast(S)); + } + } + + // Here we will handle the condition in IfStmt, or other branch stmts + // which will change the nullability of VarDecl or FiledPath. + if (const Stmt *TS = block->getTerminatorStmt()) { + if (isa(TS) || isa(TS) || isa(TS) || + isa(TS)) { + const CFGElement &elem = *(block->rbegin()); + if (elem.getAs()) { + const Stmt *S = elem.castAs().getStmt(); + TF.PassConditionStatusToSuccBlocks(const_cast(S)); + } + } + } + return std::make_pair(statusVD, statusFP); +} + +void clang::runNullabilityCheck(const FunctionDecl &fd, const CFG &cfg, + AnalysisDeclContext &ac, + NullabilityCheckDiagReporter &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; + + NullabilityCheckImpl NCI; + NCI.initStatus(cfg, ctx); + + // Proceed with the worklist. + ForwardDataflowWorklist worklist(cfg, ac); + const CFGBlock *entry = &cfg.getEntry(); + for (const CFGBlock *B : cfg.const_reverse_nodes()) + if (B != entry && !B->pred_empty()) + worklist.enqueueBlock(B); + + while (const CFGBlock *block = worklist.dequeue()) { + StatusVD &preValVD = NCI.BlocksBeginStatusVD[block]; + StatusFP &preValFP = NCI.BlocksBeginStatusFP[block]; + StatusVD valVD; + StatusFP valFP; + for (CFGBlock::const_pred_iterator it = block->pred_begin(), + ei = block->pred_end(); + it != ei; ++it) { + if (const CFGBlock *pred = *it) { + StatusVD predValVD = NCI.BlocksEndStatusVD[pred]; + if (NCI.BlocksConditionStatusVD.count(block)) { + if (NCI.BlocksConditionStatusVD[block].count(pred)) { + std::pair condition = + NCI.BlocksConditionStatusVD[block][pred]; + predValVD[condition.first] = condition.second; + } + } + valVD = NCI.mergeVD(valVD, predValVD); + + StatusFP predValFP = NCI.BlocksEndStatusFP[pred]; + if (NCI.BlocksConditionStatusFP.count(block)) { + if (NCI.BlocksConditionStatusFP[block].count(pred)) { + std::pair condition = + NCI.BlocksConditionStatusFP[block][pred]; + predValFP[condition.first] = condition.second; + } + } + valFP = NCI.mergeFP(valFP, predValFP); + } + } + + std::pair val = + NCI.runOnBlock(block, valVD, valFP, reporter, ctx, fd); + NCI.BlocksEndStatusVD[block] = val.first; + NCI.BlocksEndStatusFP[block] = val.second; + if (preValVD == val.first && preValFP == val.second) + continue; + + preValVD = val.first; + preValFP = val.second; + + // Enqueue the value to the successors. + worklist.enqueueSuccessors(block); + } +} +#endif // ENABLE_BSC \ No newline at end of file diff --git a/clang/lib/Analysis/BSCOwnership.cpp b/clang/lib/Analysis/BSCOwnership.cpp index fcc8e7600332c9a84c94b7eb875ffe09cb19c912..0a970e3d0cd3d59205c5f2c9b34be8ec020d6c25 100644 --- a/clang/lib/Analysis/BSCOwnership.cpp +++ b/clang/lib/Analysis/BSCOwnership.cpp @@ -794,29 +794,32 @@ string Ownership::OwnershipStatus::collectMovedFields(const VarDecl *VD) { return movedFields; } -SmallVector Ownership::OwnershipStatus::checkOPSUse( +SmallVector Ownership::OwnershipStatus::checkOPSUse( const VarDecl *VD, const SourceLocation &Loc, bool isGetAddr, bool isStar) { - SmallVector diags; + SmallVector diags; // when use ops, we must ensure the variable is owned // check the status of the variable if (!is(VD, Ownership::Status::Owned)) { if (has(VD, Ownership::Status::Moved) || is(VD, Ownership::Status::Moved)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidUseOfMoved, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidUseOfMoved, VD->getNameAsString())); } else if (is(VD, Ownership::Status::Uninitialized)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidUseOfUninit, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidUseOfUninit, VD->getNameAsString())); } else if (has(VD, Ownership::Status::Uninitialized)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfPossiblyUninit, - VD->getNameAsString())); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidUseOfPossiblyUninit, + VD->getNameAsString())); } else if (is(VD, Ownership::Status::PartialMoved)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfPartiallyMoved, - VD->getNameAsString(), collectMovedFields(VD))); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidUseOfPartiallyMoved, + VD->getNameAsString(), collectMovedFields(VD))); } else if (is(VD, Ownership::Status::AllMoved)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfAllMoved, - VD->getNameAsString(), collectMovedFields(VD))); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidUseOfAllMoved, + VD->getNameAsString(), collectMovedFields(VD))); } } if (!isGetAddr) { @@ -832,9 +835,10 @@ SmallVector Ownership::OwnershipStatus::checkOPSUse( return diags; } -SmallVector Ownership::OwnershipStatus::checkOPSFieldUse( - const VarDecl *VD, const SourceLocation &Loc, string fullFieldName, bool isGetAddr) { - SmallVector diags; +SmallVector Ownership::OwnershipStatus::checkOPSFieldUse( + const VarDecl *VD, const SourceLocation &Loc, string fullFieldName, + bool isGetAddr) { + SmallVector diags; // when assign a field, we check three conditions: // 1. the field's parent must be owned @@ -859,8 +863,8 @@ SmallVector Ownership::OwnershipStatus::checkOPSFieldUse( } if (OPSAllOwnedFields[VD].count(current) && !OPSOwnedOwnedFields[VD].count(current)) { - diags.push_back(DiagInfo( - Loc, DiagKind::InvalidUseOfMoved, + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidUseOfMoved, moveAsterisksToFront(VD->getNameAsString() + "." + current))); break; } @@ -868,19 +872,22 @@ SmallVector Ownership::OwnershipStatus::checkOPSFieldUse( // check condition 2 if ((is(VD, Moved) || has(VD, Moved)) && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfMoved, - VD->getNameAsString() + "." + fullFieldName)); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidUseOfMoved, + VD->getNameAsString() + "." + fullFieldName)); } if ((is(VD, Uninitialized) || has(VD, Uninitialized)) && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfUninit, - VD->getNameAsString() + "." + fullFieldName)); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidUseOfUninit, + VD->getNameAsString() + "." + fullFieldName)); } // check condition 3 // if fullFieldName has been moved, report error if (OPSAllOwnedFields[VD].count(fullFieldName) && !OPSOwnedOwnedFields[VD].count(fullFieldName)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfMoved, - VD->getNameAsString() + "." + fullFieldName)); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidUseOfMoved, + VD->getNameAsString() + "." + fullFieldName)); } // calculate the fields with fullFieldName prefix llvm::SmallSet allPrefixStrs; @@ -906,8 +913,9 @@ SmallVector Ownership::OwnershipStatus::checkOPSFieldUse( ownedPrefixStrs.insert(elem); } if (allPrefixStrs.size() != ownedPrefixStrs.size() && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfPartiallyMoved, - VD->getNameAsString(), collectMovedFields(VD))); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidUseOfPartiallyMoved, + VD->getNameAsString(), collectMovedFields(VD))); } if (!isGetAddr) { // change the status of the fields @@ -933,28 +941,30 @@ SmallVector Ownership::OwnershipStatus::checkOPSFieldUse( return diags; } -SmallVector +SmallVector Ownership::OwnershipStatus::checkOPSAssign(const VarDecl *VD, const SourceLocation &Loc) { - SmallVector diags; + SmallVector diags; // when assign ops, we must ensure the variable is uninit or moved // check the status of the variable if (!canAssign(VD)) { if (has(VD, Ownership::Status::Owned) || is(VD, Ownership::Status::Owned)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidAssignOfOwned, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignOfOwned, VD->getNameAsString())); } else if (is(VD, Ownership::Status::PartialMoved)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignOfPartiallyMoved, - VD->getNameAsString(), collectMovedFields(VD))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignOfPartiallyMoved, + VD->getNameAsString(), collectMovedFields(VD))); } else if (has(VD, Ownership::Status::PartialMoved)) { - diags.push_back(DiagInfo(Loc, - DiagKind::InvalidAssignOfPossiblyPartiallyMoved, - VD->getNameAsString(), collectMovedFields(VD))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignOfPossiblyPartiallyMoved, + VD->getNameAsString(), collectMovedFields(VD))); } else if (is(VD, AllMoved)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignOfAllMoved, - VD->getNameAsString())); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidAssignOfAllMoved, + VD->getNameAsString())); } } // change the status to owned @@ -965,22 +975,23 @@ Ownership::OwnershipStatus::checkOPSAssign(const VarDecl *VD, return diags; } -SmallVector +SmallVector Ownership::OwnershipStatus::checkOPSAssignStar(const VarDecl *VD, const SourceLocation &Loc) { - SmallVector diags; + SmallVector diags; if (!is(VD, AllMoved)) { if (has(VD, Ownership::Status::Owned) || is(VD, Ownership::Status::Owned)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidAssignOfOwned, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignOfOwned, VD->getNameAsString())); } else if (is(VD, Ownership::Status::PartialMoved)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignOfPartiallyMoved, - VD->getNameAsString(), collectMovedFields(VD))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignOfPartiallyMoved, + VD->getNameAsString(), collectMovedFields(VD))); } else if (has(VD, Ownership::Status::PartialMoved)) { - diags.push_back(DiagInfo(Loc, - DiagKind::InvalidAssignOfPossiblyPartiallyMoved, - VD->getNameAsString(), collectMovedFields(VD))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignOfPossiblyPartiallyMoved, + VD->getNameAsString(), collectMovedFields(VD))); } } @@ -992,9 +1003,11 @@ Ownership::OwnershipStatus::checkOPSAssignStar(const VarDecl *VD, return diags; } -SmallVector Ownership::OwnershipStatus::checkOPSFieldAssign( - const VarDecl *VD, const SourceLocation &Loc, string fullFieldName) { - SmallVector diags; +SmallVector +Ownership::OwnershipStatus::checkOPSFieldAssign(const VarDecl *VD, + const SourceLocation &Loc, + string fullFieldName) { + SmallVector diags; // when assign a field, we check three conditions: // 1. the field's parent must be owned @@ -1019,8 +1032,8 @@ SmallVector Ownership::OwnershipStatus::checkOPSFieldAssign( } if (OPSAllOwnedFields[VD].count(current) && !OPSOwnedOwnedFields[VD].count(current)) { - diags.push_back(DiagInfo( - Loc, DiagKind::InvalidAssignFieldOfMoved, + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignFieldOfMoved, moveAsterisksToFront(VD->getNameAsString() + "." + current))); return diags; } @@ -1028,18 +1041,21 @@ SmallVector Ownership::OwnershipStatus::checkOPSFieldAssign( // check condition 2 if ((is(VD, Moved) || has(VD, Moved)) && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignFieldOfMoved, - VD->getNameAsString())); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidAssignFieldOfMoved, + VD->getNameAsString())); } if ((is(VD, Uninitialized) || has(VD, Uninitialized)) && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignFieldOfUninit, - VD->getNameAsString())); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidAssignFieldOfUninit, + VD->getNameAsString())); } // check condition 3 if (OPSAllOwnedFields[VD].count(fullFieldName) && OPSOwnedOwnedFields[VD].count(fullFieldName) && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignFieldOfOwned, - VD->getNameAsString() + "." + fullFieldName)); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidAssignFieldOfOwned, + VD->getNameAsString() + "." + fullFieldName)); } // calculate the fields with fullFieldName prefix llvm::SmallSet allPrefixStrs; @@ -1065,9 +1081,9 @@ SmallVector Ownership::OwnershipStatus::checkOPSFieldAssign( ownedPrefixStrs.insert(elem); } if (!ownedPrefixStrs.empty() && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignSubFieldOwned, - VD->getNameAsString(), - concatFields(VD, ownedPrefixStrs))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignSubFieldOwned, + VD->getNameAsString(), concatFields(VD, ownedPrefixStrs))); } if (!is(VD, Ownership::Status::Moved)) { if (OPSAllOwnedFields[VD].count(fullFieldName)) { @@ -1096,19 +1112,19 @@ SmallVector Ownership::OwnershipStatus::checkOPSFieldAssign( return diags; } -SmallVector Ownership::OwnershipStatus::checkSUse( +SmallVector Ownership::OwnershipStatus::checkSUse( const VarDecl *VD, const SourceLocation &Loc, bool isGetAddr) { - SmallVector diags; + SmallVector diags; // owned struct special manipulation if (VD->getType().getTypePtr()->isOwnedStructureType()) { if (!is(VD, Owned)) { if (is(VD, Uninitialized) || has(VD, Uninitialized)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidUseOfUninit, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidUseOfUninit, VD->getNameAsString())); } else if (is(VD, Moved) || has(VD, Moved)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidUseOfMoved, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidUseOfMoved, VD->getNameAsString())); } } if (!isGetAddr) { @@ -1118,32 +1134,35 @@ SmallVector Ownership::OwnershipStatus::checkSUse( } if (SAllOwnedFields[VD].size() != SOwnedOwnedFields[VD].size() && diags.empty()) { - diags.push_back(DiagInfo(Loc, InvalidUseOfPartiallyMoved, - VD->getNameAsString(), collectMovedFields(VD))); + diags.push_back(OwnershipDiagInfo(Loc, InvalidUseOfPartiallyMoved, + VD->getNameAsString(), + collectMovedFields(VD))); } if (!isGetAddr) SOwnedOwnedFields[VD].clear(); return diags; } -SmallVector Ownership::OwnershipStatus::checkSFieldUse( - const VarDecl *VD, const SourceLocation &Loc, string fullFieldName, bool isGetAddr) { - SmallVector diags; +SmallVector Ownership::OwnershipStatus::checkSFieldUse( + const VarDecl *VD, const SourceLocation &Loc, string fullFieldName, + bool isGetAddr) { + SmallVector diags; // owned struct special manipulation if (VD->getType().getTypePtr()->isOwnedStructureType()) { if (is(VD, Uninitialized)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidUseOfUninit, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidUseOfUninit, VD->getNameAsString())); } else if (is(VD, Moved)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidUseOfMoved, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidUseOfMoved, VD->getNameAsString())); } } if (SAllOwnedFields[VD].count(fullFieldName) && !SOwnedOwnedFields[VD].count(fullFieldName) && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfMoved, - VD->getNameAsString() + "." + fullFieldName)); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidUseOfMoved, + VD->getNameAsString() + "." + fullFieldName)); } // calculate the fields with fullFieldName prefix llvm::SmallSet allPrefixStrs; @@ -1169,8 +1188,9 @@ SmallVector Ownership::OwnershipStatus::checkSFieldUse( ownedPrefixStrs.insert(elem); } if (allPrefixStrs.size() != ownedPrefixStrs.size() && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfPartiallyMoved, - VD->getNameAsString() + "." + fullFieldName, collectMovedFields(VD))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidUseOfPartiallyMoved, + VD->getNameAsString() + "." + fullFieldName, collectMovedFields(VD))); } if (!isGetAddr) { SOwnedOwnedFields[VD].erase(fullFieldName); @@ -1182,16 +1202,16 @@ SmallVector Ownership::OwnershipStatus::checkSFieldUse( return diags; } -SmallVector +SmallVector Ownership::OwnershipStatus::checkSAssign(const VarDecl *VD, const SourceLocation &Loc) { - SmallVector diags; + SmallVector diags; // owned struct special manipulation if (VD->getType().getTypePtr()->isOwnedStructureType()) { if (!is(VD, Moved) && !is(VD, Uninitialized)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignOfOwned, - VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignOfOwned, VD->getNameAsString())); } resetAll(VD); set(VD, Owned); @@ -1200,11 +1220,12 @@ Ownership::OwnershipStatus::checkSAssign(const VarDecl *VD, // when assign a struct, all of its owned fields must be MOVED if (!SOwnedOwnedFields[VD].empty() && diags.empty()) { if (SAllOwnedFields[VD].size() == SOwnedOwnedFields[VD].size()) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidAssignOfOwned, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignOfOwned, VD->getNameAsString())); } else { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignOfPartiallyMoved, - VD->getNameAsString(), collectMovedFields(VD))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignOfPartiallyMoved, + VD->getNameAsString(), collectMovedFields(VD))); } } SOwnedOwnedFields[VD] = SAllOwnedFields[VD]; @@ -1212,19 +1233,21 @@ Ownership::OwnershipStatus::checkSAssign(const VarDecl *VD, return diags; } -SmallVector Ownership::OwnershipStatus::checkSFieldAssign( +SmallVector Ownership::OwnershipStatus::checkSFieldAssign( const VarDecl *VD, const SourceLocation &Loc, string fullFieldName) { - SmallVector diags; + SmallVector diags; // owned struct special manipulation if (VD->getType().getTypePtr()->isOwnedStructureType()) { if (!is(VD, Owned)) { if (is(VD, Uninitialized) || has(VD, Uninitialized)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignFieldOfUninit, - VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignFieldOfUninit, + VD->getNameAsString())); } else if (is(VD, Moved) || has(VD, Moved)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignFieldOfMoved, - VD->getNameAsString())); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidAssignFieldOfMoved, + VD->getNameAsString())); } } } @@ -1251,8 +1274,8 @@ SmallVector Ownership::OwnershipStatus::checkSFieldAssign( } if (SAllOwnedFields[VD].count(current) && !SOwnedOwnedFields[VD].count(current) && diags.empty()) { - diags.push_back(DiagInfo( - Loc, DiagKind::InvalidAssignFieldOfMoved, + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignFieldOfMoved, moveAsterisksToFront(VD->getNameAsString() + "." + current))); return diags; } @@ -1260,8 +1283,8 @@ SmallVector Ownership::OwnershipStatus::checkSFieldAssign( // check condition 2 if (SAllOwnedFields[VD].count(fullFieldName) && SOwnedOwnedFields[VD].count(fullFieldName) && diags.empty()) { - diags.push_back(DiagInfo( - Loc, DiagKind::InvalidAssignFieldOfOwned, + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignFieldOfOwned, moveAsterisksToFront(VD->getNameAsString() + "." + fullFieldName))); } // calculate the fields with fullFieldName prefix @@ -1288,9 +1311,9 @@ SmallVector Ownership::OwnershipStatus::checkSFieldAssign( ownedPrefixStrs.insert(elem); } if (!ownedPrefixStrs.empty() && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignSubFieldOwned, - VD->getNameAsString(), - concatFields(VD, ownedPrefixStrs))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignSubFieldOwned, + VD->getNameAsString(), concatFields(VD, ownedPrefixStrs))); } // change the status of the fields if (SAllOwnedFields[VD].count(fullFieldName)) @@ -1303,29 +1326,29 @@ SmallVector Ownership::OwnershipStatus::checkSFieldAssign( return diags; } -SmallVector -Ownership::OwnershipStatus::checkBOPUse(const VarDecl *VD, - const SourceLocation &Loc, - bool isGetAddr) { - SmallVector diags; +SmallVector Ownership::OwnershipStatus::checkBOPUse( + const VarDecl *VD, const SourceLocation &Loc, bool isGetAddr) { + SmallVector diags; // check the status of the variable if (!is(VD, Ownership::Status::Owned)) { if (has(VD, Ownership::Status::Moved) || is(VD, Ownership::Status::Moved)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidUseOfMoved, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidUseOfMoved, VD->getNameAsString())); } else if (is(VD, Ownership::Status::Uninitialized)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidUseOfUninit, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidUseOfUninit, VD->getNameAsString())); } else if (has(VD, Ownership::Status::Uninitialized)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfPossiblyUninit, - VD->getNameAsString())); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidUseOfPossiblyUninit, + VD->getNameAsString())); } else if (is(VD, Ownership::Status::PartialMoved)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfPartiallyMoved, - VD->getNameAsString(), collectMovedFields(VD))); - } else if (is(VD, AllMoved)) { diags.push_back( - DiagInfo(Loc, DiagKind::InvalidUseOfAllMoved, VD->getNameAsString())); + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidUseOfPartiallyMoved, + VD->getNameAsString(), collectMovedFields(VD))); + } else if (is(VD, AllMoved)) { + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidUseOfAllMoved, VD->getNameAsString())); } } if (!isGetAddr) { @@ -1340,32 +1363,35 @@ Ownership::OwnershipStatus::checkBOPUse(const VarDecl *VD, return diags; } -SmallVector Ownership::OwnershipStatus::checkBOPFieldUse( - const VarDecl *VD, const SourceLocation &Loc, string fullFieldName, bool isGetAddr) { - SmallVector diags; +SmallVector Ownership::OwnershipStatus::checkBOPFieldUse( + const VarDecl *VD, const SourceLocation &Loc, string fullFieldName, + bool isGetAddr) { + SmallVector diags; // check field's parent for (int i = fullFieldName.length() - 2; i > 0; i--) { string current = fullFieldName.substr(0, i + 1); if (BOPAllOwnedFields[VD].count(current) && !BOPOwnedOwnedFields[VD].count(current)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfMoved, - fullFieldName + VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo(Loc, + OwnershipDiagKind::InvalidUseOfMoved, + fullFieldName + VD->getNameAsString())); break; } } if ((is(VD, Ownership::Status::Moved) || has(VD, Ownership::Status::Moved)) && diags.empty()) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidUseOfMoved, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidUseOfMoved, + VD->getNameAsString())); } // if the field is owned qualified, check the field and its child if (BOPAllOwnedFields[VD].count(fullFieldName)) { // if fullFieldName has been moved, report error if (!BOPOwnedOwnedFields[VD].count(fullFieldName) && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfMoved, - fullFieldName + VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo(Loc, + OwnershipDiagKind::InvalidUseOfMoved, + fullFieldName + VD->getNameAsString())); } // calculate the fields with fullFieldName prefix auto allPrefixStrs = @@ -1373,9 +1399,9 @@ SmallVector Ownership::OwnershipStatus::checkBOPFieldUse( auto ownedPrefixStrs = findPrefixStrings(BOPOwnedOwnedFields[VD], fullFieldName); if (allPrefixStrs.size() != ownedPrefixStrs.size() && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidUseOfPartiallyMoved, - fullFieldName + VD->getNameAsString(), - collectMovedFields(VD))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidUseOfPartiallyMoved, + fullFieldName + VD->getNameAsString(), collectMovedFields(VD))); } if (!isGetAddr) { // change the status of the fields @@ -1402,26 +1428,28 @@ SmallVector Ownership::OwnershipStatus::checkBOPFieldUse( return diags; } -SmallVector +SmallVector Ownership::OwnershipStatus::checkBOPAssign(const VarDecl *VD, const SourceLocation &Loc) { - SmallVector diags; + SmallVector diags; // check the status of the variable if (!canAssign(VD)) { if (has(VD, Ownership::Status::Owned) || is(VD, Ownership::Status::Owned)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidAssignOfOwned, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignOfOwned, VD->getNameAsString())); } else if (is(VD, Ownership::Status::PartialMoved)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignOfPartiallyMoved, - VD->getNameAsString(), collectMovedFields(VD))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignOfPartiallyMoved, + VD->getNameAsString(), collectMovedFields(VD))); } else if (has(VD, Ownership::Status::PartialMoved)) { - diags.push_back(DiagInfo(Loc, - DiagKind::InvalidAssignOfPossiblyPartiallyMoved, - VD->getNameAsString(), collectMovedFields(VD))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignOfPossiblyPartiallyMoved, + VD->getNameAsString(), collectMovedFields(VD))); } else if (is(VD, AllMoved)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignOfAllMoved, - VD->getNameAsString())); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidAssignOfAllMoved, + VD->getNameAsString())); } } // change the status to owned @@ -1432,9 +1460,11 @@ Ownership::OwnershipStatus::checkBOPAssign(const VarDecl *VD, return diags; } -SmallVector Ownership::OwnershipStatus::checkBOPFieldAssign( - const VarDecl *VD, const SourceLocation &Loc, string fullFieldName) { - SmallVector diags; +SmallVector +Ownership::OwnershipStatus::checkBOPFieldAssign(const VarDecl *VD, + const SourceLocation &Loc, + string fullFieldName) { + SmallVector diags; // for a non owned qualified field, just check its parent // for a owned qualified field, we check three conditions: @@ -1447,23 +1477,25 @@ SmallVector Ownership::OwnershipStatus::checkBOPFieldAssign( string current = fullFieldName.substr(0, i + 1); if (BOPAllOwnedFields[VD].count(current) && !BOPOwnedOwnedFields[VD].count(current)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignFieldOfMoved, - fullFieldName + VD->getNameAsString())); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidAssignFieldOfMoved, + fullFieldName + VD->getNameAsString())); break; } } if ((is(VD, Moved) || has(VD, Moved) || is(VD, Uninitialized) || has(VD, Uninitialized)) && diags.empty()) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidUseOfMoved, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidUseOfMoved, + VD->getNameAsString())); } if (BOPAllOwnedFields[VD].count(fullFieldName)) { // check condition 3 if (BOPOwnedOwnedFields[VD].count(fullFieldName) && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignFieldOfOwned, - VD->getNameAsString() + fullFieldName)); + diags.push_back( + OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidAssignFieldOfOwned, + VD->getNameAsString() + fullFieldName)); } // calculate the fields with fullFieldName prefix auto allPrefixStrs = @@ -1471,9 +1503,9 @@ SmallVector Ownership::OwnershipStatus::checkBOPFieldAssign( auto ownedPrefixStrs = findPrefixStrings(BOPOwnedOwnedFields[VD], fullFieldName); if (!ownedPrefixStrs.empty() && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidAssignSubFieldOwned, - VD->getNameAsString(), - concatFields(VD, ownedPrefixStrs))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidAssignSubFieldOwned, + VD->getNameAsString(), concatFields(VD, ownedPrefixStrs))); } if (!is(VD, Ownership::Status::Moved)) { if (BOPAllOwnedFields[VD].count(fullFieldName)) { @@ -1506,28 +1538,28 @@ SmallVector Ownership::OwnershipStatus::checkBOPFieldAssign( // if cast a `struct S * owned` variable to `void * owned`, we must ensure: // 1. it is not moved or uninit // 2. its owned fields are all moved -SmallVector +SmallVector Ownership::OwnershipStatus::checkCastOPS(const VarDecl *VD, const SourceLocation &Loc) { - SmallVector diags; + SmallVector diags; if (has(VD, Ownership::Status::Moved) || is(VD, Ownership::Status::Moved)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidCastMoved, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidCastMoved, + VD->getNameAsString())); } if (has(VD, Uninitialized) || is(VD, Uninitialized)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidCastUninit, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidCastUninit, + VD->getNameAsString())); } if (is(VD, PartialMoved)) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidCastFieldOwned, - VD->getNameAsString(), - concatFields(VD, OPSOwnedOwnedFields[VD]))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidCastFieldOwned, VD->getNameAsString(), + concatFields(VD, OPSOwnedOwnedFields[VD]))); } if (!OPSOwnedOwnedFields[VD].empty() && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidCastFieldOwned, - VD->getNameAsString(), - concatFields(VD, OPSOwnedOwnedFields[VD]))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidCastFieldOwned, VD->getNameAsString(), + concatFields(VD, OPSOwnedOwnedFields[VD]))); } OPSOwnedOwnedFields[VD].clear(); resetAll(VD); @@ -1539,19 +1571,19 @@ Ownership::OwnershipStatus::checkCastOPS(const VarDecl *VD, // if cast a `int * owned` variable to `void * owned`, we must ensure: // 1. it is not moved // 2. its owned fields are all moved -SmallVector +SmallVector Ownership::OwnershipStatus::checkCastBOP(const VarDecl *VD, const SourceLocation &Loc) { - SmallVector diags; + SmallVector diags; if (has(VD, Ownership::Status::Moved) || is(VD, Ownership::Status::Moved)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidCastMoved, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo(Loc, OwnershipDiagKind::InvalidCastMoved, + VD->getNameAsString())); } if (!BOPOwnedOwnedFields[VD].empty() && diags.empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::InvalidCastFieldOwned, - VD->getNameAsString(), - concatFields(VD, BOPOwnedOwnedFields[VD]))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidCastFieldOwned, VD->getNameAsString(), + concatFields(VD, BOPOwnedOwnedFields[VD]))); } BOPOwnedOwnedFields[VD].clear(); resetAll(VD); @@ -1561,18 +1593,18 @@ Ownership::OwnershipStatus::checkCastBOP(const VarDecl *VD, } // TODO -SmallVector Ownership::OwnershipStatus::checkCastField( +SmallVector Ownership::OwnershipStatus::checkCastField( const VarDecl *VD, const SourceLocation &Loc, string fullFieldName) { - SmallVector diags; + SmallVector diags; if (OPSStatus.count(VD)) { if (is(VD, Ownership::Status::Moved) || has(VD, Ownership::Status::Moved)) { - diags.push_back( - DiagInfo(Loc, DiagKind::InvalidCastMoved, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidCastMoved, VD->getNameAsString())); } if (!OPSOwnedOwnedFields[VD].count(fullFieldName) && diags.empty()) { - diags.push_back(DiagInfo( - Loc, DiagKind::InvalidCastMoved, + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidCastMoved, moveAsterisksToFront(VD->getNameAsString() + "." + fullFieldName))); } // calculate the fields with fullFieldName prefix @@ -1581,8 +1613,8 @@ SmallVector Ownership::OwnershipStatus::checkCastField( auto ownedPrefixStrs = findPrefixStrings(OPSOwnedOwnedFields[VD], fullFieldName + "."); if (!ownedPrefixStrs.empty() && diags.empty()) { - diags.push_back(DiagInfo( - Loc, DiagKind::InvalidCastFieldOwned, + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidCastFieldOwned, moveAsterisksToFront(VD->getNameAsString() + "." + fullFieldName), concatFields(VD, ownedPrefixStrs))); } @@ -1609,8 +1641,8 @@ SmallVector Ownership::OwnershipStatus::checkCastField( if (SStatus.count(VD)) { if (!SOwnedOwnedFields[VD].count(fullFieldName)) { - diags.push_back(DiagInfo( - Loc, DiagKind::InvalidCastMoved, + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidCastMoved, moveAsterisksToFront(VD->getNameAsString() + "." + fullFieldName))); } // calculate the fields with fullFieldName prefix @@ -1619,8 +1651,8 @@ SmallVector Ownership::OwnershipStatus::checkCastField( auto ownedPrefixStrs = findPrefixStrings(SOwnedOwnedFields[VD], fullFieldName + "."); if (ownedPrefixStrs.size() != 0 && diags.empty()) { - diags.push_back(DiagInfo( - Loc, DiagKind::InvalidCastFieldOwned, + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::InvalidCastFieldOwned, moveAsterisksToFront(VD->getNameAsString() + "." + fullFieldName), concatFields(VD, ownedPrefixStrs))); } @@ -1633,10 +1665,10 @@ SmallVector Ownership::OwnershipStatus::checkCastField( return diags; } -SmallVector +SmallVector Ownership::OwnershipStatus::checkMemoryLeak(const VarDecl *VD, const SourceLocation &Loc) { - SmallVector diags; + SmallVector diags; // for a struct pointer owned variable, // the following two situations indicates that a memory leak has occurred: // 1. if OPSStatus[VD] is not MOVED or UNINITIALIZED, VD memory leak @@ -1644,17 +1676,17 @@ Ownership::OwnershipStatus::checkMemoryLeak(const VarDecl *VD, if (OPSStatus.count(VD)) { // situation 1 if (!canAssign(VD)) { - diags.push_back( - DiagInfo(Loc, DiagKind::MemoryLeak, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo(Loc, OwnershipDiagKind::MemoryLeak, + VD->getNameAsString())); // reset the state of VD, it's important in for/while resetAll(VD); set(VD, Ownership::Status::Moved); } // situation 2 if (!OPSOwnedOwnedFields[VD].empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::FieldMemoryLeak, - VD->getNameAsString(), - concatFields(VD, OPSOwnedOwnedFields[VD]))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::FieldMemoryLeak, VD->getNameAsString(), + concatFields(VD, OPSOwnedOwnedFields[VD]))); OPSOwnedOwnedFields[VD].clear(); } } @@ -1664,9 +1696,9 @@ Ownership::OwnershipStatus::checkMemoryLeak(const VarDecl *VD, // 1. if SOwnedOwnedFields[VD] is not empty, VD's owned fields memory leak if (SStatus.count(VD)) { if (!SOwnedOwnedFields[VD].empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::FieldMemoryLeak, - VD->getNameAsString(), - concatFields(VD, SOwnedOwnedFields[VD]))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::FieldMemoryLeak, VD->getNameAsString(), + concatFields(VD, SOwnedOwnedFields[VD]))); SOwnedOwnedFields[VD].clear(); } } @@ -1678,16 +1710,16 @@ Ownership::OwnershipStatus::checkMemoryLeak(const VarDecl *VD, if (BOPStatus.count(VD)) { // situation 1 if (!canAssign(VD)) { - diags.push_back( - DiagInfo(Loc, DiagKind::MemoryLeak, VD->getNameAsString())); + diags.push_back(OwnershipDiagInfo(Loc, OwnershipDiagKind::MemoryLeak, + VD->getNameAsString())); resetAll(VD); set(VD, Ownership::Status::Moved); } // situation 2 if (!BOPOwnedOwnedFields[VD].empty()) { - diags.push_back(DiagInfo(Loc, DiagKind::FieldMemoryLeak, - VD->getNameAsString(), - concatFields(VD, BOPOwnedOwnedFields[VD]))); + diags.push_back(OwnershipDiagInfo( + Loc, OwnershipDiagKind::FieldMemoryLeak, VD->getNameAsString(), + concatFields(VD, BOPOwnedOwnedFields[VD]))); BOPOwnedOwnedFields[VD].clear(); } } @@ -1856,12 +1888,12 @@ void TransferFunctions::VisitCStyleCastExpr(CStyleCastExpr *CSCE) { if (const DeclRefExpr *DRE = dyn_cast(ICE->getSubExpr())) { const VarDecl *VD = dyn_cast(DRE->getDecl()); if (stat.OPSStatus.count(VD)) { - SmallVector diags = + SmallVector diags = stat.checkCastOPS(VD, DRE->getLocation()); reporter.addDiags(diags); } if (stat.BOPStatus.count(VD)) { - SmallVector diags = + SmallVector diags = stat.checkCastBOP(VD, DRE->getLocation()); reporter.addDiags(diags); } @@ -1873,7 +1905,7 @@ void TransferFunctions::VisitCStyleCastExpr(CStyleCastExpr *CSCE) { if (const MemberExpr *ME = dyn_cast(ICE->getSubExpr())) { auto memberField = getMemberFullField(ME); if (const DeclRefExpr *DRE = dyn_cast(memberField.first)) { - SmallVector diags = + SmallVector diags = stat.checkCastField(dyn_cast(DRE->getDecl()), DRE->getLocation(), memberField.second); reporter.addDiags(diags); @@ -1934,7 +1966,7 @@ void TransferFunctions::VisitScopeEnd(VarDecl *VD, SourceLocation SL) { // don't check memory leak of an owned struct type variable if (VD->getType().getTypePtr()->isOwnedStructureType()) return; - SmallVector diags = stat.checkMemoryLeak(VD, SL); + SmallVector diags = stat.checkMemoryLeak(VD, SL); reporter.addDiags(diags); } @@ -1951,24 +1983,24 @@ void TransferFunctions::HandleDREAssign(const DeclRefExpr *DRE, // situation 1 and 2 if (stat.OPSStatus.count(VD)) { if (fullFieldName == "") { - SmallVector diags = + SmallVector diags = stat.checkOPSAssign(VD, DRE->getLocation()); reporter.addDiags(diags); } else if (fullFieldName == "*") { - SmallVector diags = + SmallVector diags = stat.checkOPSAssignStar(VD, DRE->getLocation()); reporter.addDiags(diags); } else { if (fullFieldName[fullFieldName.size() - 1] == '*') { fullFieldName[fullFieldName.size() - 1] = '.'; if (stat.OPSAllOwnedFields[VD].count(fullFieldName.substr(0, fullFieldName.size() - 1))) { - SmallVector diags = + SmallVector diags = stat.checkOPSFieldAssign(VD, DRE->getLocation(), fullFieldName); reporter.addDiags(diags); } fullFieldName[fullFieldName.size() - 1] = '*'; } - SmallVector diags = + SmallVector diags = stat.checkOPSFieldAssign(VD, DRE->getLocation(), fullFieldName); reporter.addDiags(diags); } @@ -1977,20 +2009,20 @@ void TransferFunctions::HandleDREAssign(const DeclRefExpr *DRE, // situation 3 and 4 if (stat.SStatus.count(VD)) { if (fullFieldName == "") { - SmallVector diags = + SmallVector diags = stat.checkSAssign(VD, DRE->getLocation()); reporter.addDiags(diags); } else { if (fullFieldName[fullFieldName.size() - 1] == '*') { fullFieldName[fullFieldName.size() - 1] = '.'; if (stat.SAllOwnedFields[VD].count(fullFieldName.substr(0, fullFieldName.size() - 1))) { - SmallVector diags = + SmallVector diags = stat.checkSFieldAssign(VD, DRE->getLocation(), fullFieldName); reporter.addDiags(diags); } fullFieldName[fullFieldName.size() - 1] = '*'; } - SmallVector diags = + SmallVector diags = stat.checkSFieldAssign(VD, DRE->getLocation(), fullFieldName); reporter.addDiags(diags); } @@ -1999,11 +2031,11 @@ void TransferFunctions::HandleDREAssign(const DeclRefExpr *DRE, // situation 5 and 6 if (stat.BOPStatus.count(VD)) { if (fullFieldName == "") { - SmallVector diags = + SmallVector diags = stat.checkBOPAssign(VD, DRE->getLocation()); reporter.addDiags(diags); } else { - SmallVector diags = + SmallVector diags = stat.checkBOPFieldAssign(VD, DRE->getLocation(), fullFieldName); reporter.addDiags(diags); } @@ -2024,25 +2056,25 @@ void TransferFunctions::HandleDREUse(const DeclRefExpr *DRE, // situation 1 and 2 if (stat.OPSStatus.count(VD)) { if (fullFieldName == "") { - SmallVector diags = + SmallVector diags = stat.checkOPSUse(VD, DRE->getLocation(), op == GetAddr); reporter.addDiags(diags); } else if (fullFieldName == "*") { - SmallVector diags = + SmallVector diags = stat.checkOPSUse(VD, DRE->getLocation(), op == GetAddr, true); reporter.addDiags(diags); } else { if (fullFieldName[fullFieldName.size() - 1] == '*') { fullFieldName[fullFieldName.size() - 1] = '.'; if (stat.OPSAllOwnedFields[VD].count(fullFieldName.substr(0, fullFieldName.size() - 1))) { - SmallVector diags = - stat.checkOPSFieldUse(VD, DRE->getLocation(), fullFieldName, op == GetAddr); + SmallVector diags = stat.checkOPSFieldUse( + VD, DRE->getLocation(), fullFieldName, op == GetAddr); reporter.addDiags(diags); } fullFieldName[fullFieldName.size() - 1] = '*'; } - SmallVector diags = - stat.checkOPSFieldUse(VD, DRE->getLocation(), fullFieldName, op == GetAddr); + SmallVector diags = stat.checkOPSFieldUse( + VD, DRE->getLocation(), fullFieldName, op == GetAddr); reporter.addDiags(diags); } } @@ -2050,20 +2082,21 @@ void TransferFunctions::HandleDREUse(const DeclRefExpr *DRE, // situation 3 and 4 if (stat.SStatus.count(VD)) { if (fullFieldName == "") { - SmallVector diags = stat.checkSUse(VD, DRE->getLocation(), op == GetAddr); + SmallVector diags = + stat.checkSUse(VD, DRE->getLocation(), op == GetAddr); reporter.addDiags(diags); } else { if (fullFieldName[fullFieldName.size() - 1] == '*') { fullFieldName[fullFieldName.size() - 1] = '.'; if (stat.SAllOwnedFields[VD].count(fullFieldName.substr(0, fullFieldName.size() - 1))) { - SmallVector diags = - stat.checkSFieldUse(VD, DRE->getLocation(), fullFieldName, op == GetAddr); + SmallVector diags = stat.checkSFieldUse( + VD, DRE->getLocation(), fullFieldName, op == GetAddr); reporter.addDiags(diags); } fullFieldName[fullFieldName.size() - 1] = '*'; } - SmallVector diags = - stat.checkSFieldUse(VD, DRE->getLocation(), fullFieldName, op == GetAddr); + SmallVector diags = stat.checkSFieldUse( + VD, DRE->getLocation(), fullFieldName, op == GetAddr); reporter.addDiags(diags); } } @@ -2071,12 +2104,12 @@ void TransferFunctions::HandleDREUse(const DeclRefExpr *DRE, // situation 5 and 6 if (stat.BOPStatus.count(VD)) { if (fullFieldName == "") { - SmallVector diags = + SmallVector diags = stat.checkBOPUse(VD, DRE->getLocation(), op == GetAddr); reporter.addDiags(diags); } else { - SmallVector diags = - stat.checkBOPFieldUse(VD, DRE->getLocation(), fullFieldName, op == GetAddr); + SmallVector diags = stat.checkBOPFieldUse( + VD, DRE->getLocation(), fullFieldName, op == GetAddr); reporter.addDiags(diags); } } @@ -2084,6 +2117,8 @@ void TransferFunctions::HandleDREUse(const DeclRefExpr *DRE, } static bool IsNull(const Expr *E) { + if (isa(E)) + return true; if (const ImplicitCastExpr *ICE = dyn_cast(E)) { if (const ParenExpr *PE = dyn_cast(ICE->getSubExpr())) { if (const CStyleCastExpr *CSCE = @@ -2119,16 +2154,16 @@ void OwnershipImpl::MaybeSetNull(const CFGBlock *block, if (pred == nullptr) { return; } - if (*pred->succ_begin() != block) { - return; - } if (const IfStmt *IS = dyn_cast_or_null(pred->getTerminatorStmt())) { if (const BinaryOperator *BO = dyn_cast(IS->getCond())) { - if (BO->getOpcode() == BO_EQ) { - if (IsNull(BO->getRHS())) { - const Expr *LHS = BO->getLHS(); - if (const ImplicitCastExpr *ICE = dyn_cast(LHS)) { - status.setToMoved(ICE->getSubExpr()); + if (IsNull(BO->getRHS())) { + if (const ImplicitCastExpr *ICE = dyn_cast(BO->getLHS())) { + if (BO->getOpcode() == BO_EQ) { + if (*pred->succ_begin() == block) // block is True branch. + status.setToMoved(ICE->getSubExpr()); + } else if (BO->getOpcode() == BO_NE) { + if (*(pred->succ_begin() + 1) == block) // block is False branch. + status.setToMoved(ICE->getSubExpr()); } } } @@ -2237,7 +2272,7 @@ void clang::runOwnershipAnalysis(const FunctionDecl &fd, const CFG &cfg, continue; } - SmallVector diags = + SmallVector diags = OS->blocksEndStatus[exit].checkMemoryLeak( VD, fd.getSourceRange().getEnd()); reporter.addDiags(diags); diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt index 978c1648df8e345b40a9a4799c9c8f650892c1d6..2695d570073142165d8c35c61e1240ea83a46884 100644 --- a/clang/lib/Analysis/CMakeLists.txt +++ b/clang/lib/Analysis/CMakeLists.txt @@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangAnalysis AnalysisDeclContext.cpp BodyFarm.cpp + BSCNullabilityCheck.cpp BSCOwnership.cpp BSCBorrowCheck.cpp CalledOnceCheck.cpp diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp index 1a39cffebf2194a5d991042d0e4d7f8f30f7c542..1d4c0cdb21d67edfbfb5c5d597d4ae1986760572 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -142,6 +142,7 @@ void DiagnosticsEngine::Reset(bool soft /*=false*/) { #if ENABLE_BSC NumOwnershipErrors = 0; NumBorrowCheckErrors = 0; + NumNullabilityCheckErrors = 0; #endif TrapNumErrorsOccurred = 0; TrapNumUnrecoverableErrorsOccurred = 0; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 7e8700e1399b45561da66944bcef69f26858f4af..c51bc098ad6bb413f7a2e2267952285c18e5ec23 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4820,6 +4820,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_opt_string); if (Args.hasArg(options::OPT_disable_ownership_check)) CmdArgs.push_back("-disable-ownership-check"); + if (Args.hasArg(options::OPT_disable_nullability_check)) + CmdArgs.push_back("-disable-nullability-check"); #endif auto *MemProfArg = Args.getLastArg(options::OPT_fmemory_profile, diff --git a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp index 63aefca91a78b6e6da52878b1ef446660a6b6a45..63de3de353227d0a5683ddef034c87a8851c4569 100644 --- a/clang/lib/Sema/BSC/SemaBSCDestructor.cpp +++ b/clang/lib/Sema/BSC/SemaBSCDestructor.cpp @@ -743,7 +743,7 @@ void Sema::DesugarDestructor(RecordDecl *RD) { return; std::stack Fields = CollectInstanceFieldWithDestructor(RD); HandleBSCDestructorBody(RD, Destructor, Fields); - CheckBSCOwnership(Destructor); + BSCDataflowAnalysis(Destructor); } void Sema::CheckBSCDestructorDeclarator(FunctionDecl *NewFD) { diff --git a/clang/lib/Sema/BSC/SemaBSCOwnership.cpp b/clang/lib/Sema/BSC/SemaBSCOwnership.cpp index 3a741edc9112fdb042eb21dc59a260156a50a7fc..e050e6e38ab64428ffb18a43c364a5ec38ec8327 100644 --- a/clang/lib/Sema/BSC/SemaBSCOwnership.cpp +++ b/clang/lib/Sema/BSC/SemaBSCOwnership.cpp @@ -14,9 +14,6 @@ #if ENABLE_BSC #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" @@ -163,6 +160,11 @@ bool Sema::CheckOwnedQualTypeAssignment(QualType LHSType, Expr* RHSExpr) { IsLiteral = true; } SourceLocation ExprLoc = RHSExpr->getBeginLoc(); + // Owned pointer can be inited by nullptr. + if (LHSCanType.isOwnedQualified() && LHSCanType->isPointerType() && + isa(RHSExpr)) + return true; + bool Res = true; // unOwned to owned initialize cases: @@ -227,30 +229,6 @@ bool Sema::CheckTemporaryVarMemoryLeak(Expr* E) { return false; } -void Sema::CheckBSCOwnership(const Decl *D) { - AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, D); - - AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true; - AC.getCFGBuildOptions().AddEHEdges = false; - AC.getCFGBuildOptions().AddInitializers = true; - AC.getCFGBuildOptions().AddImplicitDtors = true; - AC.getCFGBuildOptions().AddTemporaryDtors = true; - AC.getCFGBuildOptions().AddScopes = true; - AC.getCFGBuildOptions().AddAllScopes = true; - AC.getCFGBuildOptions().setAllAlwaysAdd(); - - OwnershipDiagReporter Reporter(*this); - 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) { if (E == nullptr) return; @@ -355,6 +333,11 @@ bool Sema::CheckBorrowQualTypeAssignment(QualType LHSType, Expr* RHSExpr) { } } } + + // Borrow pointer can be inited by nullptr. + if (LHSCanType.isBorrowQualified() && isa(RHSExpr)) + return true; + if (LHSCanType->isVoidPointerType()) { if (LHSCanType->getPointeeType().isConstQualified() == RHSCanType->getPointeeType().isConstQualified()) return true; diff --git a/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp b/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp index 7f71c0de7cf8d8453ffd70d20b8563608f7e6453..cad99765f657745e13adcd00b8d51d23a940798e 100644 --- a/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp +++ b/clang/lib/Sema/BSC/SemaBSCSafeZone.cpp @@ -231,6 +231,13 @@ bool Sema::IsSafeConversion(QualType DestType, Expr *Expr) { return true; } + // Init a owned or borrow pointer by nullptr is allowed in the safe zone + if ((DestType.isOwnedQualified() && DestType->isPointerType()) || + DestType.isBorrowQualified()) { + if (isa(Expr)) + return true; + } + bool IsSafeBehavior = true; QualType SrcType = Expr->getType(); if (IsTraitExpr(Expr)) { diff --git a/clang/lib/Sema/BSC/SemaDeclBSC.cpp b/clang/lib/Sema/BSC/SemaDeclBSC.cpp index 184730d2bd6aa6aebb6e46fbcec62ff7a28ad3a4..f9a75460fc067ca95367efff0bd9b83d3639b93c 100644 --- a/clang/lib/Sema/BSC/SemaDeclBSC.cpp +++ b/clang/lib/Sema/BSC/SemaDeclBSC.cpp @@ -13,9 +13,13 @@ #if ENABLE_BSC +#include "clang/AST/BSC/WalkerBSC.h" +#include "clang/Analysis/Analyses/BSCBorrowCheck.h" +#include "clang/Analysis/Analyses/BSCNullabilityCheck.h" +#include "clang/Analysis/Analyses/BSCOwnership.h" +#include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Sema/Sema.h" #include "clang/Sema/SemaInternal.h" -#include "clang/AST/BSC/WalkerBSC.h" using namespace clang; using namespace sema; @@ -155,4 +159,44 @@ bool Sema::FindSafeFeatures(const FunctionDecl* FnDecl) { } return false; } + +void Sema::BSCDataflowAnalysis(const Decl *D, bool DisableOwnershipCheck, + bool DisableNullabilityCheck) { + AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, D); + + AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true; + AC.getCFGBuildOptions().AddEHEdges = false; + AC.getCFGBuildOptions().AddInitializers = true; + AC.getCFGBuildOptions().AddImplicitDtors = true; + AC.getCFGBuildOptions().AddTemporaryDtors = true; + AC.getCFGBuildOptions().AddScopes = true; + AC.getCFGBuildOptions().AddAllScopes = true; + AC.getCFGBuildOptions().setAllAlwaysAdd(); + + if (AC.getCFG()) { + const FunctionDecl *FD = cast(D); + unsigned NumNullabilityCheckErrorsInCurrFD = 0; + if (!DisableNullabilityCheck && FD->getSafeZoneSpecifier() == SZ_Safe) { + NullabilityCheckDiagReporter NullabilityCheckReporter(*this); + runNullabilityCheck(*FD, *AC.getCFG(), AC, NullabilityCheckReporter, + Context); + NullabilityCheckReporter.flushDiagnostics(); + NumNullabilityCheckErrorsInCurrFD = + NullabilityCheckReporter.getNumErrors(); + } + // Run ownership analysis when there is no nullability errors in current + // function. + if (!DisableOwnershipCheck && !NumNullabilityCheckErrorsInCurrFD) { + OwnershipDiagReporter OwnershipReporter(*this); + runOwnershipAnalysis(*FD, *AC.getCFG(), AC, OwnershipReporter); + OwnershipReporter.flushDiagnostics(); + // Run borrow check when there is no other ownership errors in current + // function. + if (!OwnershipReporter.getNumErrors()) { + BorrowCheckDiagReporter BorrowCheckReporter(*this); + runBorrowCheck(*FD, *AC.getCFG(), BorrowCheckReporter, Context); + } + } + } +} #endif \ No newline at end of file diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 148d167e209c2dbf8147de480e4667970d816a04..cde59826720a4352b60d93cfaae39aaa94a965ee 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -2213,19 +2213,23 @@ Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP, #if ENABLE_BSC bool DisableOwnershipCheck = getLangOpts().DisableOwnershipCheck; + bool DisableNullabilityCheck = getLangOpts().DisableNullabilityCheck; // If D does not use memory safety features like "owned, borrow, &mut, &const", // we do not do borrow checking. bool useSafeFeature = LangOpts.BSC ? FindSafeFeatures(dyn_cast_or_null(D)) : false; - if (!DisableOwnershipCheck && useSafeFeature) { + if (!(DisableNullabilityCheck && DisableOwnershipCheck) && useSafeFeature) { if (!isBSCCoroutine && (getDiagnostics().getNumErrors() == - getDiagnostics().getNumOwnershipErrors() + getDiagnostics().getNumBorrowCheckErrors()) && + getDiagnostics().getNumOwnershipErrors() + + getDiagnostics().getNumBorrowCheckErrors() + + getDiagnostics().getNumNullabilityCheckErrors()) && D) if (const auto *const CastReturn = dyn_cast_or_null(D)) { auto md = dyn_cast_or_null(D); // Don't check ownership rules of destructor parameters if (!md || !md->isDestructor()) - CheckBSCOwnership(D); + BSCDataflowAnalysis(D, DisableOwnershipCheck, + DisableNullabilityCheck); } } #endif diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 52d4f7447800e30e728eb5f11d5769009ac8c17c..02e767acbb4edf92ee3dc182ed4ba8bd57acbfdb 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -13356,6 +13356,11 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, } } +#if ENABLE_BSC + if (getLangOpts().BSC && LHSType->isPointerType() && RHSType->isNullPtrType()) + return computeResultTy(); +#endif + return InvalidOperands(Loc, LHS, RHS); } diff --git a/clang/test/BSC/Negative/Driver/options/disable-nullability-check/disable-nullability-check.cbs b/clang/test/BSC/Negative/Driver/options/disable-nullability-check/disable-nullability-check.cbs new file mode 100644 index 0000000000000000000000000000000000000000..183ccf745f3b9b5f8652f5c3f6352618ec3d8f30 --- /dev/null +++ b/clang/test/BSC/Negative/Driver/options/disable-nullability-check/disable-nullability-check.cbs @@ -0,0 +1,7 @@ +// RUN: %clang -disable-nullability-check %s -o %t.output +// RUN: %t.output + +safe int main(void) { + int *borrow _Nonnull p1 = nullptr; + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/NullabilityCheck/function_paremeter.cbs b/clang/test/BSC/Negative/NullabilityCheck/function_paremeter.cbs new file mode 100644 index 0000000000000000000000000000000000000000..0a67a153312c20b266fc4ad15d5a6ba5a778874a --- /dev/null +++ b/clang/test/BSC/Negative/NullabilityCheck/function_paremeter.cbs @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -verify %s + +safe void foo1(int *borrow a); +safe void test1(void) { + int a = 10; + int *borrow p1 = &mut a; + foo1(p1); + int *borrow _Nullable p2 = &mut a; + foo1(p2); + int *borrow _Nullable p3 = nullptr; + foo1(p3); // expected-error {{cannot pass nullable pointer argument}} + foo1(&mut a); + foo1(nullptr); // expected-error {{cannot pass nullable pointer argument}} +} + +safe int *owned safe_malloc(int a); +safe void foo2(int *owned a); +safe void test2(void) { + int *owned p1 = safe_malloc(5); + foo2(p1); + int *owned _Nullable p2 = safe_malloc(5); + foo2(p2); + int *owned _Nullable p3 = nullptr; + foo2(p3); // expected-error {{cannot pass nullable pointer argument}} + foo2(safe_malloc(5)); + foo2(nullptr); // expected-error {{cannot pass nullable pointer argument}} +} + +safe void foo3(int *borrow _Nullable a); +safe void test3(void) { + int a = 10; + int *borrow p1 = &mut a; + foo3(p1); + int *borrow _Nullable p2 = &mut a; + foo3(p2); + int *borrow _Nullable p3 = nullptr; + foo3(p3); + foo3(nullptr); +} + +safe void foo4(int *owned _Nullable a); +safe void test4(void) { + int *owned p1 = safe_malloc(5); + foo4(p1); + int *owned _Nullable p2 = safe_malloc(5); + foo4(p2); + int *owned _Nullable p3 = nullptr; + foo4(p3); + foo4(nullptr); +} + +safe void test5(void) { + int a = 10; + int *borrow _Nullable p1 = a == 1 ? nullptr : &mut a; + foo1(p1); // expected-error {{cannot pass nullable pointer argument}} + foo1(a == 1 ? nullptr : &mut a); // expected-error {{cannot pass nullable pointer argument}} + + int b = 10; + int *borrow _Nullable p2 = a == 1 && b == 10 ? &mut b : &mut a; + foo1(p2); + foo1(a == 1 && b == 10 ? &mut b : &mut a); +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/NullabilityCheck/function_return.cbs b/clang/test/BSC/Negative/NullabilityCheck/function_return.cbs new file mode 100644 index 0000000000000000000000000000000000000000..da09a91c894cec21dafd804bc5dd935fd586929b --- /dev/null +++ b/clang/test/BSC/Negative/NullabilityCheck/function_return.cbs @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -verify %s + +safe int *borrow foo1(int *borrow a); +safe void test1(void) { + int a = 10; + int *borrow p1 = foo1(&mut a); + int *borrow _Nullable p2 = foo1(&mut a); +} + +safe void safe_free(int *owned _Nullable p); + +safe int *owned foo2(void); +safe void test2(void) { + int *owned p1 = foo2(); + int *owned _Nullable p2 = foo2(); + safe_free(p1); + safe_free(p2); +} + +safe int *borrow _Nullable foo3(int* borrow a); +safe void test3(void) { + int a = 10; + int *borrow p1 = foo3(&mut a); // expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + int *borrow _Nullable p2 = foo3(&mut a); +} + +safe int *owned _Nullable foo4(void); +safe void test4(void) { + int *owned p1 = foo4(); // expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + int *owned _Nullable p2 = foo4(); + safe_free(p1); + safe_free(p2); +} + +safe int *borrow foo5(int *borrow _Nullable a) { + return a; // expected-error {{cannot return nullable pointer type}} +} + +safe int *borrow foo6(int *borrow _Nullable a, int* borrow p) { + a = p; + return a; +} + +safe int *borrow foo7(int *borrow _Nullable a, int* borrow p) { + return foo6(a, p); +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/NullabilityCheck/if_stmt.cbs b/clang/test/BSC/Negative/NullabilityCheck/if_stmt.cbs new file mode 100644 index 0000000000000000000000000000000000000000..7b38218ff2d6c08b890d67da5a2bb3633ea45778 --- /dev/null +++ b/clang/test/BSC/Negative/NullabilityCheck/if_stmt.cbs @@ -0,0 +1,252 @@ +// RUN: %clang_cc1 -verify %s + +safe void test1(void) { + int *borrow _Nullable p = nullptr; + if (p != nullptr) { + *p = 10; + } else { + *p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test2(void) { + int a = 10; + int *borrow _Nullable p = nullptr; + if (p != nullptr) { + *p = 10; + } else { + p = &mut a; + } + *p = 30; +} + +safe void test3(void) { + int *borrow _Nullable p = nullptr; + int *borrow _Nullable q = nullptr; + if (p != nullptr && q != nullptr) { + *p = 10; + *q = 10; + } else { + *p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test4(void) { + int a = 10; + int b = 10; + int *borrow _Nullable p = nullptr; + int *borrow _Nullable q = nullptr; + if (p != nullptr && q != nullptr) { + *p = 10; + *q = 10; + } else { + p = &mut a; + q = &mut b; + } + *p = 30; + *q = 30; +} + +safe void test5(void) { + int *borrow _Nullable p = nullptr; + int *borrow _Nullable q = nullptr; + if (p != nullptr || q != nullptr) { + *p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 10; // expected-error {{nullable pointer cannot be dereferenced}} + } else { + *p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test6(void) { + int a = 10; + int b = 10; + int *borrow _Nullable p = nullptr; + int *borrow _Nullable q = nullptr; + if (p != nullptr) { + *p = 10; + if (q != nullptr) + *q = 10; + else + q = &mut b; + } else { + p = &mut a; + if (q != nullptr) + *q = 10; + else + q = &mut b; + } + *p = 30; + *q = 30; +} + +safe void test7(void) { + int a = 10; + int b = 10; + int c = 10; + int *borrow _Nullable p = nullptr; + int *borrow _Nullable q = nullptr; + int *borrow _Nullable m = nullptr; + if (p != nullptr && q != nullptr && m != nullptr) { + *p = 10; + *q = 10; + *m = 10; + } else { + p = &mut a; + q = &mut b; + m = &mut c; + } + *p = 30; + *q = 30; + *m = 30; +} + +safe void test8(void) { + int a = 10; + int *borrow _Nullable p = nullptr; + int *borrow _Nullable q = nullptr; + int *borrow _Nullable m = nullptr; + if (p != nullptr && q != nullptr && m != nullptr) { + *p = 10; + *q = 10; + *m = 10; + } else { + *p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *m = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *m = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test9(void) { + int a = 10; + int *borrow _Nullable p = nullptr; + int *borrow _Nullable q = nullptr; + int *borrow _Nullable m = nullptr; + if (p != nullptr || q != nullptr || m != nullptr) { + *p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 10; // expected-error {{nullable pointer cannot be dereferenced}} + *m = 10; // expected-error {{nullable pointer cannot be dereferenced}} + } else { + *p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 10; // expected-error {{nullable pointer cannot be dereferenced}} + *m = 10; // expected-error {{nullable pointer cannot be dereferenced}} + } + *p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *m = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test10(void) { + int a = 0; + int *borrow _Nullable p = nullptr; + if (p != nullptr) { + *p = 10; + p = nullptr; + } else { + p = &mut a; + } + *p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test11(void) { + int a = 0; + int *borrow _Nullable p = nullptr; + int *borrow _Nullable q = nullptr; + if (p != nullptr && q != nullptr || a > 0) { + *p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 10; // expected-error {{nullable pointer cannot be dereferenced}} + } else { + *p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test12(void) { + int a = 0; + int *borrow _Nullable p = nullptr; + int *borrow _Nullable q = nullptr; + if (p != nullptr && q != nullptr && a > 0) { + *p = 10; + *q = 10; + } else { + *p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test13(void) { + int a = 0; + int b = 0; + int *borrow _Nullable p = nullptr; + int *borrow _Nullable q = nullptr; + if (p != nullptr && q != nullptr && a > 0) { + *p = 10; + *q = 10; + } else { + p = &mut a; + q = &mut b; + } + *p = 30; + *q = 30; +} + +safe void test14(void) { + int *borrow _Nullable p = nullptr; + if (p) { + *p = 10; + } else { + *p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test15(void) { + int *borrow _Nullable p = nullptr; + if (!p) { + *p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + } else { + *p = 20; + } + *p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test16(void) { + int *borrow _Nullable p = nullptr; + int *borrow _Nullable q = nullptr; + if (p && q) { + *p = 10; + *q = 10; + } else { + *p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +struct S { + int a; +}; +safe void test17(void) { + struct S *borrow _Nullable p = nullptr; + if (p != nullptr) { + p->a = 10; + } else { + p->a = 20; // expected-error {{cannot access member througn nullable pointer}} + } + p->a = 30; // expected-error {{cannot access member througn nullable pointer}} +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/NullabilityCheck/if_stmt_struct.cbs b/clang/test/BSC/Negative/NullabilityCheck/if_stmt_struct.cbs new file mode 100644 index 0000000000000000000000000000000000000000..533df93ed3cc4180113abd2a8357bccead05e4d7 --- /dev/null +++ b/clang/test/BSC/Negative/NullabilityCheck/if_stmt_struct.cbs @@ -0,0 +1,243 @@ +// RUN: %clang_cc1 -verify %s + +struct S { + int *borrow _Nullable p; +}; + +safe void test1(void) { + struct S s = { .p = nullptr }; + if (s.p != nullptr) { + *s.p = 10; + } else { + *s.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *s.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test2(void) { + int a = 10; + struct S s = { .p = nullptr }; + if (s.p != nullptr) { + *s.p = 10; + } else { + s.p = &mut a; + } + *s.p = 30; +} + +safe void test3(void) { + struct S s1 = { .p = nullptr }; + struct S s2 = { .p = nullptr }; + if (s1.p != nullptr && s2.p != nullptr) { + *s1.p = 10; + *s2.p = 10; + } else { + *s1.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *s1.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test4(void) { + int a = 10; + int b = 10; + struct S s1 = { .p = nullptr }; + struct S s2 = { .p = nullptr }; + if (s1.p != nullptr && s2.p != nullptr) { + *s1.p = 10; + *s2.p = 10; + } else { + s1.p = &mut a; + s2.p = &mut b; + } + *s1.p = 30; + *s2.p = 30; +} + +safe void test5(void) { + struct S s1 = { .p = nullptr }; + struct S s2 = { .p = nullptr }; + if (s1.p != nullptr || s2.p != nullptr) { + *s1.p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + } else { + *s1.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *s1.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test6(void) { + int a = 10; + int b = 10; + struct S s1 = { .p = nullptr }; + struct S s2 = { .p = nullptr }; + if (s1.p != nullptr) { + *s1.p = 10; + if (s2.p != nullptr) + *s2.p = 10; + else + s2.p = &mut b; + } else { + s1.p = &mut a; + if (s2.p != nullptr) + *s2.p = 10; + else + s2.p = &mut b; + } + *s1.p = 30; + *s2.p = 30; +} + +safe void test7(void) { + int a = 10; + int b = 10; + int c = 10; + struct S s1 = { .p = nullptr }; + struct S s2 = { .p = nullptr }; + struct S s3 = { .p = nullptr }; + if (s1.p != nullptr && s2.p != nullptr && s3.p != nullptr) { + *s1.p = 10; + *s2.p = 10; + *s3.p = 10; + } else { + s1.p = &mut a; + s2.p = &mut b; + s3.p = &mut c; + } + *s1.p = 30; + *s2.p = 30; + *s3.p = 30; +} + +safe void test8(void) { + int a = 10; + struct S s1 = { .p = nullptr }; + struct S s2 = { .p = nullptr }; + struct S s3 = { .p = nullptr }; + if (s1.p != nullptr && s2.p != nullptr && s3.p != nullptr) { + *s1.p = 10; + *s2.p = 10; + *s3.p = 10; + } else { + *s1.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *s3.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *s1.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *s3.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test9(void) { + int a = 10; + struct S s1 = { .p = nullptr }; + struct S s2 = { .p = nullptr }; + struct S s3 = { .p = nullptr }; + if (s1.p != nullptr || s2.p != nullptr || s3.p != nullptr) { + *s1.p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + *s3.p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + } else { + *s1.p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + *s3.p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + } + *s1.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *s3.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test10(void) { + int a = 0; + struct S s = { .p = nullptr }; + if (s.p != nullptr) { + *s.p = 10; + s.p = nullptr; + } else { + s.p = &mut a; + } + *s.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test11(void) { + int a = 0; + struct S s1 = { .p = nullptr }; + struct S s2 = { .p = nullptr }; + if (s1.p != nullptr && s2.p != nullptr || a > 0) { + *s1.p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + } else { + *s1.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *s1.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test12(void) { + int a = 0; + struct S s1 = { .p = nullptr }; + struct S s2 = { .p = nullptr }; + if (s1.p != nullptr && s2.p != nullptr && a > 0) { + *s1.p = 10; + *s2.p = 10; + } else { + *s1.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *s1.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test13(void) { + int a = 10; + int b = 10; + struct S s1 = { .p = nullptr }; + struct S s2 = { .p = nullptr }; + if (s1.p != nullptr && s2.p != nullptr && a > 0) { + *s1.p = 10; + *s2.p = 10; + } else { + s1.p = &mut a; + s2.p = &mut b; + } + *s1.p = 30; + *s2.p = 30; +} + +safe void test14(void) { + struct S s = { .p = nullptr }; + if (s.p) { + *s.p = 10; + } else { + *s.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *s.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test15(void) { + struct S s = { .p = nullptr }; + if (!s.p) { + *s.p = 10; // expected-error {{nullable pointer cannot be dereferenced}} + } else { + *s.p = 20; + } + *s.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test16(void) { + struct S s1 = { .p = nullptr }; + struct S s2 = { .p = nullptr }; + if (s1.p && s2.p) { + *s1.p = 10; + *s2.p = 10; + } else { + *s1.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *s1.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/NullabilityCheck/nested_struct.cbs b/clang/test/BSC/Negative/NullabilityCheck/nested_struct.cbs new file mode 100644 index 0000000000000000000000000000000000000000..b06640b6fe37c7cddd14147c8beb8fd4b4cf2221 --- /dev/null +++ b/clang/test/BSC/Negative/NullabilityCheck/nested_struct.cbs @@ -0,0 +1,86 @@ +// RUN: %clang_cc1 -verify %s + +struct M { + int *owned _Nullable a; +}; + +struct N { + struct M *borrow _Nullable b; +}; + +safe void test1(void) { + struct N n1 = { .b = nullptr }; + if (n1.b->a != nullptr) // expected-error {{cannot access member througn nullable pointer}} + *n1.b->a = 10; // expected-error {{cannot access member througn nullable pointer}} + struct M m = { .a = nullptr }; + struct N n2 = { .b = &mut m }; + if (n2.b->a != nullptr) + *n2.b->a = 10; +} + +safe void test2(void) { + struct N n1 = { .b = nullptr }; + if (n1.b != nullptr && n1.b->a != nullptr) + *n1.b->a = 10; +} + +safe void test3(void) { + struct N n1 = { .b = nullptr }; + if (n1.b->a != nullptr && n1.b != nullptr) // expected-error {{cannot access member througn nullable pointer}} + *n1.b->a = 10; +} + +safe void test4(void) { + struct N n1 = { .b = nullptr }; + if (n1.b != nullptr) + if (n1.b->a != nullptr) + *n1.b->a = 10; +} + +safe void safe_free (struct N *owned _Nullable p); +safe struct N *owned _Nullable safe_malloc(struct N n); + +safe void test5(void) { + struct N n = { .b = nullptr }; + struct N *owned _Nullable p = safe_malloc(n); + if (p->b->a != nullptr) // expected-error {{cannot access member througn nullable pointer}} + *p->b->a = 10; // expected-error {{cannot access member througn nullable pointer}} + safe_free(p); +} + +safe void test6(void) { + struct N n = { .b = nullptr }; + struct N *owned _Nullable p = safe_malloc(n); + if (p->b != nullptr && p->b->a != nullptr) // expected-error {{cannot access member througn nullable pointer}} expected-error {{cannot access member througn nullable pointer}} + *p->b->a = 10; // expected-error {{cannot access member througn nullable pointer}} + safe_free(p); +} + + +safe void test7(void) { + struct N n = { .b = nullptr }; + struct N *owned _Nullable p = safe_malloc(n); + if (p != nullptr) { + if (p->b != nullptr) + if (p->b->a != nullptr) + *p->b->a = 10; + safe_free(p); + } +} + +safe void test8(void) { + struct N n = { .b = nullptr }; + struct N *owned _Nullable p = safe_malloc(n); + if (p != nullptr && p->b != nullptr && p->b->a != nullptr) + *p->b->a = 10; + safe_free(p); +} + +safe void test9(void) { + struct N n = { .b = nullptr }; + struct N *owned _Nullable p = safe_malloc(n); + if (p != nullptr && p->b != nullptr) + if (p->b->a != nullptr) + *p->b->a = 10; + safe_free(p); +} diff --git a/clang/test/BSC/Negative/NullabilityCheck/nonnull_pointer_assigned_by_nullable_pointer.cbs b/clang/test/BSC/Negative/NullabilityCheck/nonnull_pointer_assigned_by_nullable_pointer.cbs new file mode 100644 index 0000000000000000000000000000000000000000..3f6108a3fe6057f024097be7e6f9ae828a2936cb --- /dev/null +++ b/clang/test/BSC/Negative/NullabilityCheck/nonnull_pointer_assigned_by_nullable_pointer.cbs @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -verify %s + +safe int *owned _Nonnull safe_malloc(int a); + +safe void test1(void) { + int *borrow _Nullable p1 = nullptr; + int *borrow _Nonnull p2 = p1; // expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + int a = 10; + int *borrow _Nullable p3 = &mut a; + int *borrow _Nonnull p4 = p3; + int *borrow _Nonnull p5 = &mut a; + p5 = p1; // expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + p5 = p3; +} + +safe void test2(void) { + int *owned _Nullable p1 = nullptr; + int *owned _Nonnull p2 = p1; // expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + int *owned _Nullable p3 = safe_malloc(5); + int *owned _Nonnull p4 = p3; + int *owned _Nonnull p5 = safe_malloc(5); + p5 = p1; // expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + p5 = p3; +} + +safe void test3(void) { + int *borrow _Nullable p1 = nullptr; + int *borrow p2 = p1; // expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + int a = 10; + int *borrow _Nullable p3 = &mut a; + int *borrow p4 = p3; + int *borrow p5 = &mut a; + p5 = p1; // expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + p5 = p3; +} + +safe void test4(void) { + int *owned _Nullable p1 = nullptr; + int *owned p2 = p1; // expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + int *owned _Nullable p3 = safe_malloc(5); + int *owned p4 = p3; + int *owned p5 = safe_malloc(5); + p5 = p1; // expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + p5 = p3; +} + +safe void test5(void) { + int a = 10; + int *borrow _Nonnull p1 = a == 1 ? nullptr : &mut a; // expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + int *borrow _Nullable p2 = a == 1 ? nullptr : &mut a; + *p2 = 10; // expected-error {{nullable pointer cannot be dereferenced}} + int b = 10; + int *borrow _Nullable p3 = a == 1 && b == 10 ? &mut b : &mut a; + *p3 = 10; + + p1 = a == 1 ? nullptr : &mut a; // expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + p2 = a == 1 ? nullptr : &mut a; + *p2 = 10; // expected-error {{nullable pointer cannot be dereferenced}} + p3 = a == 1 && b == 10 ? &mut b : &mut a; + *p3 = 10; +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/NullabilityCheck/typedef_pointer_type.cbs b/clang/test/BSC/Negative/NullabilityCheck/typedef_pointer_type.cbs new file mode 100644 index 0000000000000000000000000000000000000000..81b6f705fa846045c9abbb768311792e103b98d0 --- /dev/null +++ b/clang/test/BSC/Negative/NullabilityCheck/typedef_pointer_type.cbs @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -verify %s +typedef NullableBorrowInt = int *borrow _Nullable; +typedef NonnullBorrowInt = int *borrow _Nonnull; +typedef NullableOwnedInt = int *owned _Nullable; +typedef NonnullOwnedInt = int *owned _Nonnull; + +safe void foo1(NonnullBorrowInt a); +safe void test1(void) { + int a = 10; + NonnullBorrowInt p1 = &mut a; + foo1(p1); + NullableBorrowInt p2 = &mut a; + foo1(p2); + NullableBorrowInt p3 = nullptr; + foo1(p3); // expected-error {{cannot pass nullable pointer argument}} + foo1(&mut a); + foo1(nullptr); // expected-error {{cannot pass nullable pointer argument}} expected-warning {{null passed to a callee that requires a non-null argument}} +} + +safe NonnullOwnedInt safe_malloc(int a); +safe void foo2(NonnullOwnedInt a); +safe void test2(void) { + NonnullOwnedInt p1 = safe_malloc(5); + foo2(p1); + NullableOwnedInt p2 = safe_malloc(5); + foo2(p2); + NullableOwnedInt p3 = nullptr; + foo2(p3); // expected-error {{cannot pass nullable pointer argument}} + foo2(safe_malloc(5)); + foo2(nullptr); // expected-error {{cannot pass nullable pointer argument}} expected-warning {{null passed to a callee that requires a non-null argument}} +} + +safe void foo3(NullableBorrowInt a); +safe void test3(void) { + int a = 10; + NonnullBorrowInt p1 = &mut a; + foo3(p1); + NullableBorrowInt p2 = &mut a; + foo3(p2); + NullableBorrowInt p3 = nullptr; + foo3(p3); + foo3(nullptr); +} + +safe void foo4(NullableOwnedInt a); +safe void test4(void) { + NonnullOwnedInt p1 = safe_malloc(5); + foo4(p1); + NullableOwnedInt p2 = safe_malloc(5); + foo4(p2); + NullableOwnedInt p3 = nullptr; + foo4(p3); + foo4(nullptr); +} + +safe void test5(void) { + NullableBorrowInt p = nullptr; + if (p != nullptr) { + *p = 10; + } else { + *p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } + *p = 30; // expected-error {{nullable pointer cannot be dereferenced}} +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/NullabilityCheck/while_stmt.cbs b/clang/test/BSC/Negative/NullabilityCheck/while_stmt.cbs new file mode 100644 index 0000000000000000000000000000000000000000..14b930f10504ae3d944f6c410c5c2acaeaaf2663 --- /dev/null +++ b/clang/test/BSC/Negative/NullabilityCheck/while_stmt.cbs @@ -0,0 +1,83 @@ +// RUN: %clang_cc1 -verify %s + +safe int *borrow _Nullable get_random_ptr(int* borrow p); +safe void test1(void) { + int a = 10; + int *borrow _Nullable p = nullptr; + while (p == nullptr) { + p = get_random_ptr(&mut a); + } + *p = 20; +} + +safe void test2(void) { + int a = 10; + int *borrow _Nullable p = get_random_ptr(&mut a); + while (p != nullptr) { + *p = 10; + p = get_random_ptr(&mut a); + } + *p = 20; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test3(void) { + int a = 10; + int *borrow _Nullable p = get_random_ptr(&mut a); + if (p != nullptr) { + do { + *p = 10; + p = get_random_ptr(&mut a); + } while (p != nullptr); + *p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } +} + +safe void test4(void) { + int a = 10; + int b = 10; + int *borrow _Nullable p = nullptr; + int *borrow _Nullable q = nullptr; + while (p == nullptr || q == nullptr) { + p = get_random_ptr(&mut a); + q = get_random_ptr(&mut b); + } + *p = 20; + *q = 20; +} + +safe void test5(void) { + int a = 10; + int *borrow _Nullable p = nullptr; + int *borrow _Nullable q = nullptr; + while (p != nullptr && q != nullptr) { + *p = 10; + *q = 10; + p = get_random_ptr(&mut a); + q = get_random_ptr(&mut a); + } + *p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *q = 20; // expected-error {{nullable pointer cannot be dereferenced}} +} + +struct S { + int a; +}; +safe struct S *borrow _Nullable get_random_ptr_S(int* borrow p); +safe void test6(void) { + int a = 10; + struct S *borrow _Nullable p = nullptr; + while (p == nullptr) { + p = get_random_ptr_S(&mut a); + } + p->a = 20; +} + +safe void test7(void) { + int a = 10; + struct S *borrow _Nullable p = get_random_ptr_S(&mut a); + while (p != nullptr) { + p->a = 10; + p = get_random_ptr_S(&mut a); + } + p->a = 20; // expected-error {{cannot access member througn nullable pointer}} +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/NullabilityCheck/while_stmt_struct.cbs b/clang/test/BSC/Negative/NullabilityCheck/while_stmt_struct.cbs new file mode 100644 index 0000000000000000000000000000000000000000..1ba70d31d74db6153b8d3110427802f2ccac4c96 --- /dev/null +++ b/clang/test/BSC/Negative/NullabilityCheck/while_stmt_struct.cbs @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -verify %s + +struct S { + int *borrow _Nullable p; +}; +safe int *borrow _Nullable get_random_ptr(int *borrow p); +safe void test1(void) { + int a = 10; + struct S s = { .p = nullptr }; + while (s.p == nullptr) { + s.p = get_random_ptr(&mut a); + } + *s.p = 20; +} + +safe void test2(void) { + int a = 10; + struct S s = { .p = nullptr }; + while (s.p != nullptr) { + *s.p = 10; + s.p = get_random_ptr(&mut a); + } + *s.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} +} + +safe void test3(void) { + int a = 10; + struct S s = { .p = nullptr }; + if (s.p != nullptr) { + do { + *s.p = 10; + s.p = get_random_ptr(&mut a); + } while (s.p != nullptr); + *s.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + } +} + +safe void test4(void) { + int a = 10; + int b = 10; + struct S s1 = { .p = nullptr }; + struct S s2 = { .p = nullptr }; + while (s1.p == nullptr || s2.p == nullptr) { + s1.p = get_random_ptr(&mut a); + s2.p = get_random_ptr(&mut b); + } + *s1.p = 20; + *s2.p = 20; +} + +safe void test5(void) { + int a = 10; + struct S s1 = { .p = nullptr }; + struct S s2 = { .p = nullptr }; + while (s1.p != nullptr && s2.p != nullptr) { + *s1.p = 10; + *s2.p = 10; + s1.p = get_random_ptr(&mut a); + s2.p = get_random_ptr(&mut a); + } + *s1.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} + *s2.p = 20; // expected-error {{nullable pointer cannot be dereferenced}} +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/NullabilityCheck/generic_func_and_struct.cbs b/clang/test/BSC/Positive/NullabilityCheck/generic_func_and_struct.cbs new file mode 100644 index 0000000000000000000000000000000000000000..00a8412a74dc074c946eab4e1391248b741e4931 --- /dev/null +++ b/clang/test/BSC/Positive/NullabilityCheck/generic_func_and_struct.cbs @@ -0,0 +1,58 @@ +// RUN: %clang %s -o %t.output +// RUN: %t.output +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: %t-rw.output +// expected-no-diagnostics + +#include +safe T *owned safe_malloc(T a) { + unsafe { + T *owned p = (T *owned)malloc(sizeof(T)); + *p = a; + return p; + } +} +safe void use(T *borrow p) {} + +owned struct Data { +public: + T *owned _Nullable p1; + T *owned p2; + ~Data(This this) { + free((void*)this.p2); + if (this.p1 != nullptr) + free((void*)this.p1); + } +}; + +safe Data Data::init(T a) { + T *owned _Nullable data1 = nullptr; + T *owned data2 = safe_malloc(a); + Data data = { .p1 = data1, .p2 = data2 }; + return data; +} + +owned struct BorrowData { +public: + Data *borrow _Nullable p1; + Data *borrow p2; +}; + +safe int main(void) { + Data data1 = Data::init(5); + if (data1.p1 != nullptr) { + use(&mut *(data1.p1)); + } + use(&mut *(data1.p2)); + + Data data2 = Data::init(5); + BorrowData borrow_data = { .p1 = nullptr, .p2 = &mut data2 }; + if (borrow_data.p1 != nullptr) { + if (borrow_data.p1->p1 != nullptr) { + use(&mut *(borrow_data.p1->p1)); + } + } + use(&mut *(borrow_data.p2->p2)); + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/NullabilityCheck/init_nullptr.cbs b/clang/test/BSC/Positive/NullabilityCheck/init_nullptr.cbs new file mode 100644 index 0000000000000000000000000000000000000000..f68c7669dc6c58256c0052eb751a61aaec038df2 --- /dev/null +++ b/clang/test/BSC/Positive/NullabilityCheck/init_nullptr.cbs @@ -0,0 +1,68 @@ +// RUN: %clang %s -o %t.output +// RUN: %t.output +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: %clang %t-rw.c -o %t-rw.output +// RUN: %t-rw.output +// expected-no-diagnostics + +#include +safe int get_current_status(void) { + return 1; +} + +safe int *owned get_nonnull(int a) { + unsafe { + int *owned p = (int *owned)malloc(sizeof(int)); + *p = a; + return p; + } +} + +safe int *owned _Nullable get_nullable(int a) { + if (get_current_status > 0) { + unsafe { + int *owned p = (int *owned)malloc(sizeof(int)); + *p = a; + return p; + } + } else + return nullptr; +} + +safe void use(int *borrow p) { } + +owned struct Data { +public: + int *owned _Nullable p1; + int *owned p2; + ~Data(This this) { + free((void*)this.p2); + if (this.p1 != nullptr) + free((void*)this.p1); + } +}; + +safe Data Data::init(int a) { + int *owned _Nullable data1 = get_nullable(a); + int *owned data2 = get_nonnull(a); + Data data = { .p1 = data1, .p2 = data2 }; + return data; +} + +safe int main(void) { + Data data1 = Data::init(5); + Data data2 = Data::init(10); + Data *borrow _Nullable p = nullptr; + if (get_current_status() == 1) { + p = &mut data1; + } else if (get_current_status() == 2){ + p = &mut data2; + } + if (p != nullptr) { + use(&mut *(p->p2)); + if (p->p1 != nullptr) { + use(&mut *(p->p1)); + } + } + return 0; +} \ No newline at end of file diff --git a/libcbs/src/string/string.cbs b/libcbs/src/string/string.cbs index 863dc1f5ff4bdfe12e68912a50ebea4b5b87f58e..8fc001d2bd0cc09e5701f7301aa09fa6412e5298 100644 --- a/libcbs/src/string/string.cbs +++ b/libcbs/src/string/string.cbs @@ -5,13 +5,15 @@ const size_t bsc_string_no_pos = SIZE_MAX; safe char* borrow String::as_mut_str(String* borrow this) { unsafe { - return &mut *this->vec.buf.ptr; + if (this->vec.buf.ptr != nullptr) + return &mut *this->vec.buf.ptr; } } safe const char* borrow String::as_str(const String* borrow this) { unsafe { - return &const *this->vec.buf.ptr; + if (this->vec.buf.ptr != nullptr) + return &const *this->vec.buf.ptr; } }