diff --git a/clang/docs/BiShengCLanguageUserManual.md b/clang/docs/BiShengCLanguageUserManual.md index 2ceb09558656e642838a6e825163e0eb8d3c66b0..7ade6b5507b5cab3fbd38a165d6137bd4046de2e 100644 --- a/clang/docs/BiShengCLanguageUserManual.md +++ b/clang/docs/BiShengCLanguageUserManual.md @@ -2802,9 +2802,9 @@ void use_mut(int *borrow p); void test() { int local = 5; int *borrow p1 = &mut local; //p1是可变借用指针,借用了local - const int *borrow p2 = &const local; //p2是不可变借用指针,也借用了local - use_immut(p2); use_mut(p1); + const int *borrow p2 = &const local; //p2是不可变借用指针,借用了local + use_immut(p2); } ``` 另外,表达式 e 如果是指针的解引用表达式,`&mut *p`和`&const *p`分别可以看作对地址 p 中存放的值,也就是`*p`,取可变借用和不可变借用,这一操作不为`*p`产生临时变量。其中,p可以是裸指针、owned指针和其它借用指针。例如: @@ -2812,14 +2812,14 @@ void test() { void test() { int *x1 = malloc(sizeof(int)); *x1 = 42; - int *borrow p1 = &mut *x1; //p1借用了*x1,但由于我们没有办法跟踪裸指针所指向的内存,所以我们假设x1和*x1是一一对应的,等价于p1借用了x1 + int *borrow p1 = &mut *x1; //p1借用了*x1 int* owned x2 = safe_malloc(); - int *borrow p2 = &mut *x2; //p2借用了*x2,由于owned指针与它所指向的内存是一一对应的,等价于p2借用了x2 + int *borrow p2 = &mut *x2; //p2借用了*x2 int local = 5; int *borrow x3 = &mut local; - int *borrow p3 = &mut *x3; //p3借用了*x3,由于x3借用了local,所以我们认为p3也借用了local + int *borrow p3 = &mut *x3; //p3借用了*x3 } ``` #### 1.2 借用的作用 @@ -2861,7 +2861,7 @@ void other_operation(MyFile* borrow p) { //其他文件操作函数,对文件 int main(void) { MyFile* owned p = create_file(); - insert_str(&mut *p, str); //不取得文件指针p的所有权,而是借用p,后续p可以继续被使用 + insert_str(&mut *p, str); //不取得文件指针p的所有权,而是借用*p,后续p可以继续被使用 other_operation(&mut *p); safe_free(p); return 0; @@ -2902,7 +2902,7 @@ void test() { int local1 = 5; p = &mut local1; //对 p 进行再赋值之后,p 不再借用 local,而是借用 local1 } - use(p); // error,借用变量 p 的生命周期比被借用对象 local1 的生命周期长 + use(p); // error,local1 的生命周期不够长 } ``` @@ -2914,16 +2914,16 @@ void test(int a, int *owned b, int *c, struct S d) { // 被借用对象是普通局部变量 int local = 5; int *borrow p1 = &mut local; //p1的被借用对象是local - int *borrow p2 = &mut *p1; //p2的被借用对象是local,因为p1的借用对象是local - int *borrow p3 = p1; //p3的被借用对象是local,因为p1的借用对象是local + int *borrow p2 = &mut *p1; //p2的被借用对象是*p1 + int *borrow p3 = p1; //p3的被借用对象是*p1 // 被借用对象是owned变量 int *owned x1 = safe_malloc(2); - int *borrow p4 = &mut *x1; //p4的被借用对象是x1 + int *borrow p4 = &mut *x1; //p4的被借用对象是*x1 // 被借用对象是裸指针变量 int *x2 = malloc(sizeof(int)); - int *borrow p5 = &mut *x2; //p5的被借用对象是x2 + int *borrow p5 = &mut *x2; //p5的被借用对象是*x2 // 被借用对象是结构体的某个字段 struct S s = { .a = 5 }; @@ -2938,14 +2938,14 @@ void test(int a, int *owned b, int *c, struct S d) { // 被借用对象是函数入参 int *borrow p9 = &mut a; //p9的被借用对象是a - int *borrow p10 = &mut *b; //p10的被借用对象是b - int *borrow p11 = &mut *c; //p11的被借用对象是c + int *borrow p10 = &mut *b; //p10的被借用对象是*b + int *borrow p11 = &mut *c; //p11的被借用对象是*c int *borrow p12 = &mut d.a; //p12的被借用对象是d.a } ``` ##### 2.3 借用变量的 Non-Lexical Lifetime -一个变量的生命周期从它的声明开始,到当前整个语句块结束,这个设计被称为Lexical Lifetime,因为变量的生命周期是严格和词法中的作用域范围绑定的。这个策略实现起来非常简单,但它可能过于保守了,某些情况下借用变量的作用范围被过度拉长了,以至于某些实质上是安全的代码也被阻止了,这在一定程度上限制了程序员的发挥。因此,毕昇 C 为借用变量引入 Non-Lexical Lifetime(简写为NLL),用更精细的手段计算借用变量真正起作用的范围,**借用变量的 NLL 范围为:从借用处开始,一直持续到最后一次使用的地方**。具体的,它是**从借用变量定义或被再赋值开始,到被再赋值之前最后一次被使用结束**。 +一个变量的生命周期从它的声明开始,到当前整个语句块结束,这个设计被称为Lexical Lifetime,因为变量的生命周期是严格和词法中的作用域范围绑定的。这个策略实现起来非常简单,但它可能过于保守了,某些情况下借用变量的作用范围被过度拉长了,以至于某些实质上是安全的代码也被阻止了,这在一定程度上限制了程序员能编写出的代码。因此,毕昇 C 为借用变量引入 Non-Lexical Lifetime(简写为NLL),用更精细的手段计算借用变量真正起作用的范围,**借用变量的 NLL 范围为:从借用处开始,一直持续到最后一次使用的地方**。具体的,它是**从借用变量定义或被再赋值开始,到被再赋值之前最后一次被使用结束**。 其中,以下场景属于对借用变量p的使用: 1. 函数调用use(p)或use(&mut*p) @@ -3033,7 +3033,7 @@ void test4() { use(p); //#10 } -//本例中,p的生命周期为[2,4],被借用对象x的生命周期为[1,3],不满足生命周期约束,error +//本例中,p的生命周期为[2,4],被借用对象*x的生命周期为[1,3],不满足生命周期约束,error void test5() { int *owned x = safe_malloc(5); //#1 int *borrow p = &mut *x; //#2 @@ -3079,78 +3079,44 @@ void test() { read_a(p2); //该函数会读取 a 所指向的内存 } ``` -由于借用本质上也是指针,所以为了避免上述问题,毕昇 C 规定,**同一时刻,对于同一个对象,只能拥有要么一个可变借用, 要么任意多个不可变借用**。 +由于借用本质上也是指针,所以为了避免上述问题,毕昇 C 规定,**同一时刻,对于同一个对象,要么只能拥有一个可变借用, 要么任意多个不可变借用**。 ```C void test1() { int local = 5; int *borrow p1 = &mut local; - int *borrow p2 = &mut local; - modify(p1); //error,同一时刻最多只能有一个指向local的可变借用变量 + int *borrow p2 = &mut local; //error,同一时刻最多只能有一个指向local的可变借用变量 + modify(p1); modify(p2); } void test2() { int local = 1; - int * borrow p1 = &mut local; - const int * borrow p2 = &const local; - use(p1); //error,指向local的可变和不可变借用不能同时存在 + int *borrow p1 = &mut local; + int *borrow p2 = &mut local; //error,同一时刻最多只能有一个指向local的可变借用变量 use(p2); + use(p1); } -``` -由于不可变借用不会导致被借用对象被修改,因此同一时刻可以拥有任意多个不可变借用,例如: -```C -void test() { - int local = 5; - const int *borrow p1 = &const local; - const int *borrow p2 = &const local; - read(p1); //ok,同一时刻可以拥有任意多个不可变借用 - read(p2); -} -``` -##### 3.3 定义可变借用会使在它之前定义的其它借用被冻结 -定义一个可变借用变量,会导致在它之前定义的其它借用变量(前提是这些借用变量都借用了同一个对象)处于冻结状态,我们认为借用在被冻结期间不存在,也就不能被使用。例如: -```C -void test1() { +void test3() { int local = 1; int * borrow p1 = &mut local; - int * borrow p2 = &mut local; //p2生命周期开始,p1被p2冻结 - use(p1); //error,p2生命周期此时没有结束,因此p1仍然处于被冻结状态,不能使用p1 + const int * borrow p2 = &const local; //error,指向local的可变和不可变借用不能同时存在 + use(p1); use(p2); } -void test2() { +void test4() { int local = 1; const int * borrow p1 = &const local; - int * borrow p2 = &mut local; //p2生命周期开始,p1被p2冻结 - use(p1); //error,p2生命周期此时没有结束,因此p1仍然处于被冻结状态,不能使用p1 + int * borrow p2 = &mut local; // error,指向local的可变和不可变借用不能同时存在 + use(p1); use(p2); } ``` -这条规则实际上是 3.2 中“可变借用同时只能存在一个”规则的补充,它保证了同一时刻,最多只有一个可变借用处于活跃状态。 -##### 3.4 使用可变借用会使在它之前定义的其它借用失效 -可变借用变量能够使在它之前被定义的其它借用变量(前提是这些借用变量都借用了同一个对象)失效,我们认为失效的借用不存在,也就无法再被使用。例如: -```C -void test1() { - int local = 1; - int * borrow p1 = &mut local; - int * borrow p2 = &mut local; - use(p2); //使用p2,会使p1失效 - use(p1); //error,p1已经失效,无法再被使用 -} - -void test2() { - int local = 1; - const int * borrow p1 = &mut local; - int * borrow p2 = &mut local; - use(p2); //使用p2,会使p1失效 - use_immut(p1); //error,p1已经失效,无法再被使用 -} -``` - -使用可变借用,可能会导致被借用对象的内存状态发生改变,如果后续通过其它借用变量访问这块被修改的内存,可能会导致未定义行为。 +如果同时存在对一个变量的可变借用和不可变借用,可能会出现通过可变借用修改被借用对象的内存状态,然后再使用不可借用访问被修改的内存,从而导致未定义行为的情况。 例如: + ```C struct A { int *p; @@ -3172,8 +3138,20 @@ int main() { return 0; } ``` + 上述代码中,`a.free_p()`实际上使用了一个指向 a 的可变借用,该可变借用会使在它之前被定义的借用 q 失效,由于`printf("%d", *q)`使用了失效的 q,毕昇 C 编译器会报错,也就阻止了不安全行为的发生。 +由于不可变借用不会导致被借用对象被修改,因此同一时刻可以拥有任意多个不可变借用,例如: +```C +void test() { + int local = 5; + const int *borrow p1 = &const local; + const int *borrow p2 = &const local; + read(p1); //ok,同一时刻可以拥有任意多个不可变借用 + read(p2); +} +``` + #### 4.借用对被借用对象的影响 ##### 4.1 不可变借用对被借用对象的影响 对表达式 e 做不可变借用, 即`&const e`,在这个不可变借用的生命周期结束之前,e 只能读不能修改,也不能对 e 创建可变借用。 @@ -3185,7 +3163,7 @@ int main() { | &const e->field | e->field 进入 “只读” 状态,也不允许整体修改 *e。但允许修改 e 指向的其它成员,或者对其它成员做可变借用 | | &const e.field | e.field 进入 “只读” 状态,也不允许整体修改 e。但允许修改 e 的其它成员,或者对其它成员做可变借用 | | &const e[index] | e 进入 “只读” 状态,不允许修改 e 及其直接或间接成员,或者对其它成员做可变借用 | -| &const *e | e 进入 “只读” 状态,不允许修改 e 及其直接或间接成员,或者对其它成员做可变借用 | +| &const *e | *e 进入 “只读” 状态,不允许修改 *e 及其直接或间接成员,或者对其它成员做可变借用,如果 e 是 owned 指针类型,则 e 也进入只读状态 | ##### 4.2 可变借用对被借用对象的影响 对表达式 e 做可变借用, 即`&mut e`,表达式 e 进入 “冻结” 状态。在这个可变借用的生命周期结束之前,e 不能读,不能修改(包含被move),也不能被借用。 @@ -3197,7 +3175,7 @@ int main() { | &mut e->field | e->field 被冻结,不允许读写 e->field,不允许整体修改 *e,但允许修改 e 指向的其它成员,或者对其它成员做可变借用 | | &mut e.field | e.field 被冻结,不允许读写 e.field,不允许整体修改 e,但允许修改 e 的其它成员,或者对其它成员做可变借用 | | &mut e[index] | e 被冻结,不允许读写 e 以及它的成员 | -| &mut *e | e 被冻结,不允许读写 e 以及它的成员 | +| &mut *e | *e 被冻结,不允许读写 *e 以及它的成员,如果 e 是 owned 指针类型,则也不允许读写 e | #### 5. 函数定义中包含借用类型 1. 不允许函数参数中没有借用类型的参数,但是函数返回是借用类型。 @@ -3398,25 +3376,27 @@ void int *borrow::f() {} //error 8. union 的成员不允许是借用类型。 ```C union MyUnion { - int *borrow p;//error,借用指针不允许作为泛型实参 + int *borrow p;//error,借用指针不允许作为union成员 }; ``` -9. 借用指针变量不支持索引运算。 +9. 借用指针类型不能是泛型实参。 + +10. 借用指针变量不支持索引运算。 -10. 借用指针变量不支持算术运算。 +11. 借用指针变量不支持算术运算。 -11. 允许同类型的借用变量之间,使用 `==`、`!=`、`>`、`<`、`<=`、`>=` 等比较运算符操作。 +12. 允许同类型的借用变量之间,使用 `==`、`!=`、`>`、`<`、`<=`、`>=` 等比较运算符操作。 -12. 允许对借用类型使用 `sizeof`、`alignof`操作符,并且有: +13. 允许对借用类型使用 `sizeof`、`alignof`操作符,并且有: sizeof(T* borrow) == sizeof(T*) _Alignof(T* borrow) == _Alignof(T*) -13. 允许对借用类型使用一元的`&`、`!`及二元的`&&`、`||`运算符。 +14. 允许对借用类型使用一元的`&`、`!`及二元的`&&`、`||`运算符。 -14. 不允许对借用类型使用一元的`-`、`~`、`&const`、`&mut`、`[]`、`++`、`--`运算符,也不允许对借用类型使用二元的`*`、`/`、`%`、`&`、`|`、`<<`、`>>`、`+`、`-`运算符。 +15. 不允许对借用类型使用一元的`-`、`~`、`&const`、`&mut`、`[]`、`++`、`--`运算符,也不允许对借用类型使用二元的`*`、`/`、`%`、`&`、`|`、`<<`、`>>`、`+`、`-`运算符。 -15. 如果一个借用指针变量指向的是函数,那么可以通过这个借用指针变量来调用函数。 +16. 如果一个借用指针变量指向的是函数,那么可以通过这个借用指针变量来调用函数。 ```C int f() {} void test() { @@ -3425,7 +3405,7 @@ void test() { } ``` -16. 不允许对函数做可变借用,只能做只读借用。 +17. 不允许对函数做可变借用,只能做只读借用。 ### 安全区 @@ -5693,19 +5673,17 @@ int main() { #line 12 "basic_math.hbs" int struct_MyStruct_divide(int a, int b); - - - #endif ``` + `calc_demo.c` ```c #include "basic_math.h" - + static int min_int(int a, int b); - + static int max_int(int a, int b); - + #line 3 "calc_demo.cbs" int main(void) { struct MyStruct s = {4, 2}; @@ -5717,12 +5695,12 @@ int main() { int c2 = min_int(s.a, s.b); return 0; } - + #line 14 "./basic_math.hbs" static int min_int(int a, int b) { return a > b ? b : a; } - + #line 18 "./basic_math.hbs" static int max_int(int a, int b) { return a > b ? a : b; @@ -5762,6 +5740,89 @@ int main() { - 调试中显示的代码位置指向原始cbs文件,支持多源文件的调试跳转。 - 当源源变换前后代码行数存在差异、无法逐行映射时,例如owned struct析构函数、trait等会生成新代码的特性,显示的调试位置可能不准确,需要开发者注意。 +## 编译错误屏蔽 + +### 概述 + +​ 开发者可以针对性的让编译器在编译时不显示某些已知且暂时无需关注的错误,从而避免编译过程被这些错误打断,提高开发效率。当前只支持屏蔽 Nullability、Owned、Borrow 数据流安全分析过程中的报错,不支持屏蔽其他语法语义报错。在某些场景中,这些安全规则可能会过于严格,开发者可以针对这些过于严格的报错进行屏蔽。但需注意,屏蔽错误并不意味着错误不存在,只是在编译阶段不展示相关提示。过度使用错误屏蔽功能可能会导致一些严重问题被忽视。 + +### 使用方式 + +- 使用编译选项屏蔽错误:毕昇 C 编译器新增了 `-Eno-xxx` 错误屏蔽编译选项。这里的`xxx`代表具体的**错误类型标识**。 + + 示例: 对于如下代码,使用毕昇编译器编译时会上报注释中所述错误,我们通过在编译命令中添加 `-Eno-repeated-borrow`可关闭此类错误提示。 + + ```c++ + // file: test1.cbs + // clang -Eno-repeated-borrow test1.cbs + void use(int * borrow a){} + int main() { + int local = 1; + int * borrow p1 = &mut local; + int * borrow p2 = &mut local; // error: cannot borrow `local` as mutable more than once at a time + use(p2); + use(p1); + return 0; + } + ``` + +- 屏蔽代码片段中的错误:通过在代码片段中添加`#pragma` 预处理指令管理编译过程中的报错行为。 + + `#pragma GCC diagnostic push`保存当前的诊断状态。 + + `#pragma GCC diagnostic ignored "-Exxx"`告诉编译器忽略`xxx`错误类型的报错。这里的`xxx`代表具体的**错误类型标识**。 + + `#pragma GCC diagnostic pop`恢复到之前保存的诊断状态。 + + 示例:对于如下代码,编译器会忽略`*p1 = 2;`触发的`assign-borrowed`错误类型的报错。 + + ```c++ + int main() { + int local = 42; + int temp = 2; + int *borrow p1 = &mut local; + int *borrow p2 = &mut temp; + p2 = p1; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eassign-borrowed" + *p1 = 2; // error: cannot assign to `*p1` because it is borrowed + #pragma GCC diagnostic pop + temp = *p2; + return 0; + } + ``` +### 可被屏蔽的错误 + + 具体错误类型标识对应的错误日志详情见下表: + +| **错误类型标识** | 可屏蔽错误日志 (日志中%0、%1为您所写的代码中变量的属性。) | +| -------------------- | ------------------------------------------------------------ | +| deref-nullable | "nullable pointer cannot be dereferenced" | +| pass-nullable | "cannot pass nullable pointer argument" | +| return-nullable | "cannot return nullable pointer type" | +| cast-nullable | "cannot cast nullable pointer to nonnull type" | +| assign-nullable | "cannot access member through nullable pointer" | +| assign-nonnull | "nonnull pointer cannot be assigned by nullable pointer" | +| bsc-nullability | 可屏蔽所有 Nullability 数据流分析过程的报错,
包括 deref-nullable、pass-nullable、return-nullable、cast-nullable、
assign-nullable、assign-nonnull 错误类型标识。 | +| use-moved-owned | "use of moved value: \`%0\`“
"use of partially moved value: \`%0\`, %1 moved"
"use of all moved value: \`%0\`" | +| use-uninit-owned | "use of uninitialized value: \`%0\`"
"use of possibly uninitialized value: \`%0\`" | +| use-owned | 包括use-moved-owned、use-uninit-owned 错误类型标识可屏蔽的错误日志 | +| assign-moved-owned | "assign to partially moved value: \`%0\`, %1 moved"
"assign to possibly partially moved value: \`%0\`, %1 possibly moved"
"assign to all moved value: \`%0\`"
"assign to part of moved value: \`%0\`" | +| assign-uninit-owned | "assign to part of uninitialized value: \`%0\`" | +| assign-owned | "assign to owned value: \`%0\`"
"assign to part of owned value: \`%0\`"
"assign to subfield owned value: \`%0\`, %1 owned"
还包括assign-moved-owned、assign-uninit-owned 错误类型标识可屏蔽的错误日志 | +| cast-moved-owned | "invalid cast to `void * owned` of moved value: \`%0\`" | +| cast-owned | "invalid cast to `void * owned` of owned value: \`%0\`"
"invalid cast to `void * owned` of uninit value: \`%0\`"
"invalid cast to `void * owned` of not all moved value: \`%0\`, %1 owned"
"invalid cast to `void * owned` of moved value:\`%0\`" | +| check-memory-leak | "field memory leak of value: `%0`, %1 leak"
"memory leak of value: `%0` | +| bsc-ownership | 可屏蔽所有 owned 数据流分析过程的报错,
包括 use-owned、assign-owned、cast-owned、check-memory-leak 错误类型标识可屏蔽的错误日志。 | +| assign-borrowed | "cannot assign to \`%0\` because it is borrowed" | +| move-borrowed | "cannot move out of \`%0\` because it is borrowed" | +| use-mutably-borrowed | "cannot use \`%0\` because it was mutably borrowed" | +| repeated-borrow | "cannot borrow \`%0\` as mutable more than once at a time"
"cannot borrow \`%0\` as immutable because it is also borrowed as mutable"
"cannot borrow \`%0\` as mutable because it is also borrowed as immutable" | +| return-local-borrow | "cannot return reference to local variable \`%0\`" | +| short-life-borrow | "\`%0\` does not live long enough" | +| bsc-borrow | 可屏蔽所有 borrow 数据流分析过程的报错,
包括assign-borrowed、move-borrowed、use-mutably-borrowed、
repeated-borrow、return-local-borrow、short-life-borrow错误类型标识可屏蔽的错误日志。 | +| bsc-safety-check | 可屏蔽所有 Nullability、owned、borrow 数据流分析过程的报错,
包括bsc-nullability、bsc-ownership、bsc-borrow错误类型标识可屏蔽的错误日志。 | + diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index cdaca83b0f61cfce23f20183ff8f4f356ea5ed1f..ccf49eac7dd94c5c25c3d3ae9ac11fc0bb8f46c5 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2175,6 +2175,8 @@ public: bool hasOwnedFields() const; bool hasBorrowFields() const; + + bool withBorrowFields() const; #endif /// Test for a particular builtin type. @@ -4951,6 +4953,10 @@ public: bool hasBorrowFields() const; + /// Recursively check all fields in the record for borrow-ness. If any field + /// is declared borrow, return true. Otherwise, return false. + bool withBorrowFields() const; + void initBorrowStatus() const; #endif @@ -4989,13 +4995,13 @@ public: // If the value of CondExpr is true, then UnderlyingType is Type1, otherwise is Type2. class ConditionalType : public Type { friend class ASTContext; // ASTContext creates these. - + llvm::Optional CondResult; Expr* CondExpr; QualType Type1; QualType Type2; QualType UnderlyingType; - + ConditionalType(llvm::Optional CondRes, Expr* CondE, QualType T1, QualType T2, QualType can); public: @@ -5004,7 +5010,7 @@ public: QualType getConditionalType1() const { return Type1; } QualType getConditionalType2() const { return Type2; } QualType getUnderlyingType() const { return UnderlyingType; } - + /// Remove a single level of sugar. QualType desugar() const; diff --git a/clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h b/clang/include/clang/Analysis/Analyses/BSC/BSCBorrowCheck.h similarity index 100% rename from clang/include/clang/Analysis/Analyses/BSCBorrowCheck.h rename to clang/include/clang/Analysis/Analyses/BSC/BSCBorrowCheck.h diff --git a/clang/include/clang/Analysis/Analyses/BSC/BSCBorrowChecker.h b/clang/include/clang/Analysis/Analyses/BSC/BSCBorrowChecker.h new file mode 100644 index 0000000000000000000000000000000000000000..7fbca6ef031740958b1b90882cbae3722bfcf952 --- /dev/null +++ b/clang/include/clang/Analysis/Analyses/BSC/BSCBorrowChecker.h @@ -0,0 +1,984 @@ +//===- BSCBorrowChecker.h - Borrow Check for Source CFGs -*- BSC --*----------// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements BSC borrow checker for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_BSCBORROWCHECKER_H +#define LLVM_CLANG_ANALYSIS_ANALYSES_BSCBORROWCHECKER_H + +#if ENABLE_BSC + +#include "clang/AST/Decl.h" +#include "clang/Analysis/CFG.h" +#include "clang/Basic/DiagnosticSema.h" +#include "clang/Sema/Sema.h" +#include +#include + +#define DEBUG_PRINT 0 + +namespace clang { +namespace borrow { +class RegionCheck; + +/// Name of region. Each RegionName corresponds to an AST node. +/// +/// Each bound region is named `'region` plus a positive integer, +/// such as 'region_0, 'region_1, etc. Bound region is related to +/// variables or a borrow/reborrow expression in the function. +/// The free region is named 'region_r. Free region is related to +/// the return point in the function or points in the caller. +class RegionName { +private: + RegionName(std::string Name) : Name(Name) {} + +public: + std::string Name; + constexpr static const char *const NamePrefix = "'region_"; + static unsigned Cnt; + + bool operator<(const RegionName &other) const { return Name < other.Name; } + + static RegionName Create() { + return RegionName(NamePrefix + std::to_string(Cnt++)); + } + + static RegionName CreateFree() { + return RegionName(std::string(NamePrefix) + "r"); + } + + RegionName() { Name = "invalid"; } + + bool isInvalid() const { return Name == "invalid"; } + + std::string print() const { return "RegionName { " + Name + " }"; } +}; + +/// An index representation of RegionName, in order to facilitate calculations. +/// +/// Each RegionName corresponds to a RegionVariable. +/// The index of RegionVariable increases from 0. +struct RegionVariable { + unsigned index; + + RegionVariable(unsigned index = 0) : index(index) {} + + std::string print() const { + return "RegionVariable { index: " + std::to_string(index) + " }"; + } +}; + +/// Representation of variables and fields of structs. +/// +/// Examples: +/// +/// - the Path of `a` is: +/// Path(Var, "a"). +/// - the Path of `p.a` is: +/// Path(Extension, +/// Path(Var, "p"), +/// "a"). +/// - the Path of `p->a` is: +/// Path(Extension, +/// Path(Extension, +/// Path(Var, "p"), +/// "*"), +/// "a"). +/// - the Path of `*p.a` is: +/// Path(Extension, +/// Path(Extension, +/// Path(Var, "p"), +/// "a"), +/// "*"). +/// - the Path of `*(p->a)` is: +/// Path(Extension, +/// Path(Extension, +/// Path(Extension, +/// Path(Var, "p"), +/// "*"), +/// "a"), +/// "*"). +/// - the Path of `p->a->b` is: +/// Path(Extension, +/// Path(Extension, +/// Path(Extension, +/// Path(Extension, +/// Path(Var, "p"), +/// "*"), +/// "a"), +/// "*"), +/// "b"). +struct Path { + enum class PathType : unsigned char { Var, Extension }; + + PathType type; + + // Non-null when type is Extension. + std::unique_ptr base = nullptr; + + std::string fieldName; + + // Type of the current path. + QualType ty; + + // Need for reborrow constraints generation. + Decl *D = nullptr; + + SourceLocation Location; + + /// Construct a Var type Path. + explicit Path(const std::string &name, QualType ty, SourceLocation Location) + : type(PathType::Var), fieldName(name), ty(ty), Location(Location) {} + + // Construct an Extension type Path. + Path(std::unique_ptr base, const std::string &name, QualType ty, + SourceLocation Location) + : type(PathType::Extension), base(std::move(base)), fieldName(name), + ty(ty), Location(Location) {} + + Path(const Path &other) + : type(other.type), fieldName(other.fieldName), ty(other.ty), D(other.D), + Location(other.Location) { + if (other.base) { + base = std::make_unique(*other.base); + } + } + + ~Path() = default; + + void setDecl(Decl *D) { this->D = D; } + + bool isDeref() const { + if (type == PathType::Var) + return false; + return fieldName == "*"; + } + + /// The prefixes of a path are all the lvalues you get by stripping away + /// fields and derefs. + /// + /// Examples: + /// + /// - the prefixes of `*a.b` where `a` is a struct are + /// `*a.b`, `a.b` and `a`. + /// - the prefixes of `a.b.c` where both `a` and `b` are structs are + /// `a.b.c`, `a.b` and `a`. + llvm::SmallVector prefixes() const { + llvm::SmallVector result; + const Path *cur = this; + while (true) { + result.push_back(cur); + if (cur->type == PathType::Var) + return result; + cur = cur->base.get(); + } + } + + /// The supporting prefixes of a path are all the prefixes of a path that + /// must remain valid for the path itself to remain valid. For the most part, + /// this means all prefixes, except that recursion stops when dereferencing a + /// shared reference. + /// + /// Examples: + /// + /// - the supporting prefixes of `s.f` where `s` is a struct are + /// `s.f` and `s`. + /// - the supporting prefixes of `(*r).f` where `r` is a shared reference are + /// `(*r).f` and `*r`, but not `r`. + /// - Intuition: one could always copy `*r` into a temporary `t` and reach + /// the data through `*t`, so it is not important to preserve `r` itself. + /// - the supporting prefixes of `(*m).f` where `m` is a mutable reference are + /// `(*m).f`, `*m`, and `m`. + llvm::SmallVector supportingPrefixes() const { + llvm::SmallVector result; + const Path *cur = this; + while (true) { + result.push_back(cur); + if (cur->type == PathType::Var) + return result; + if (cur->fieldName == "*" && cur->base->ty.isConstBorrow()) { + return result; + } + cur = cur->base.get(); + } + } + + std::string to_string() const { + if (type == PathType::Var) + return fieldName; + if (fieldName == "*") + return "*" + base->to_string(); + if (base->isDeref()) + return '(' + base->to_string() + ")." + fieldName; + return base->to_string() + '.' + fieldName; + } + + std::string print() const { return to_string(); } +}; + +enum class BorrowKind { Mut, Shared }; + +/// An abstraction of CFG nodes. +/// +/// Every CFG node is converted to one or more actions. +struct Action { + enum ActionKind { Noop, Init, Borrow, Assign, Use, StorageDead }; + + ActionKind Kind; + + Action(ActionKind Kind) : Kind(Kind) {} + + virtual ~Action() = default; + + ActionKind getKind() const { return Kind; } + + virtual llvm::Optional OverWrites() const { return llvm::None; } + + virtual std::string print() const = 0; + +private: + virtual void anchor(); +}; + +/// ActionNoop represents statement that does not use any variables. +struct ActionNoop : public Action { + ActionNoop() : Action(Noop) {} + + ~ActionNoop() override = default; + + std::string print() const override { return "ActionNoop"; } + + static bool classof(const Action *A) { return A->getKind() == Noop; } + +private: + void anchor() override; +}; + +/// ActionInit represents a variable declaration or assignment statement. +/// +/// Note that the declared or assigned variable is of a non-borrow type. +struct ActionInit : public Action { + std::unique_ptr Dest; + std::vector> Sources; + std::vector> DerefSources; + + ActionInit(std::unique_ptr Dest, + std::vector> Sources, + std::vector> DerefSources = {}) + : Action(Init), Dest(std::move(Dest)), Sources(std::move(Sources)), + DerefSources(std::move(DerefSources)) {} + + ~ActionInit() override = default; + + virtual llvm::Optional OverWrites() const override { + return llvm::Optional(Dest.get()); + } + + std::string print() const override { + std::string Ret; + Ret += "ActionInit: "; + Ret += Dest->print(); + Ret += " = use("; + for (auto &a : Sources) { + Ret += a->print(); + Ret += ", "; + } + Ret += ")"; + return Ret; + } + + static bool classof(const Action *A) { return A->getKind() == Init; } + +private: + void anchor() override; +}; + +/// ActionBorrow represents a statement that explicity borrows a variable using +/// `&mut`, `&const`, `&mut *`, or `&const *`. +struct ActionBorrow : public Action { + std::unique_ptr Dest; + RegionName RNL; + RegionName RNR; + BorrowKind BK; + std::unique_ptr Source; + + ActionBorrow(std::unique_ptr Dest, RegionName RNL, RegionName RNR, + BorrowKind BK, std::unique_ptr Source) + : Action(Borrow), Dest(std::move(Dest)), RNL(RNL), RNR(RNR), BK(BK), + Source(std::move(Source)) {} + + ~ActionBorrow() override = default; + + virtual llvm::Optional OverWrites() const override { + return llvm::Optional(Dest.get()); + } + + std::string print() const override { + std::string Ret; + Ret += "ActionBorrow: "; + Ret += Dest->print(); + Ret += " = "; + Ret += RNR.Name; + if (BK == BorrowKind::Mut) + Ret += " &mut "; + else + Ret += " & "; + Ret += Source->print(); + return Ret; + } + + static bool classof(const Action *A) { return A->getKind() == Borrow; } + +private: + void anchor() override; +}; + +/// ActionAssign represents an assignment between variables of borrow types. +struct ActionAssign : public Action { + std::unique_ptr Dest; + RegionName RNL; + RegionName RNR; + std::unique_ptr Source; + RegionName DerefRN; + std::vector> DerefSources; + + ActionAssign(std::unique_ptr Dest, RegionName RNL, RegionName RNR, + std::unique_ptr Source, RegionName DerefRN, + std::vector> DerefSources) + : Action(Assign), Dest(std::move(Dest)), RNL(RNL), RNR(RNR), + Source(std::move(Source)), DerefRN(DerefRN), + DerefSources(std::move(DerefSources)) {} + + ~ActionAssign() override = default; + + virtual llvm::Optional OverWrites() const override { + return llvm::Optional(Dest.get()); + } + + std::string print() const override { + std::string Ret; + Ret += "ActionAssign: "; + Ret += Dest->print(); + Ret += " " + RNL.print(); + Ret += " = "; + Ret += Source->print(); + Ret += " " + RNR.print(); + return Ret; + } + + static bool classof(const Action *A) { return A->getKind() == Assign; } + +private: + void anchor() override; +}; + +/// ActionUse represents the usage of a vairble, including reading, writing, or +/// transferring its ownership. +struct ActionUse : public Action { + std::vector> Uses; + std::vector> DerefSources; + + ActionUse(std::vector> Uses, + std::vector> DerefSources) + : Action(Use), Uses(std::move(Uses)), + DerefSources(std::move(DerefSources)) {} + + ~ActionUse() override = default; + + std::string print() const override { + std::string Ret; + Ret += "ActionUse: "; + Ret += " use("; + for (auto &a : Uses) { + Ret += a->print(); + Ret += ", "; + } + Ret += ")"; + return Ret; + } + + static bool classof(const Action *A) { return A->getKind() == Use; } + +private: + void anchor() override; +}; + +/// ActionStorageDead represents leaving the lexical scope of a variable, +/// meaning it is destroyed on the stack. +struct ActionStorageDead : public Action { + std::unique_ptr Var; + + ActionStorageDead(std::unique_ptr Var) + : Action(StorageDead), Var(std::move(Var)) {} + + ~ActionStorageDead() override = default; + + std::string print() const override { + std::string Ret; + Ret += "ActionStorageDead: "; + Ret += Var->print(); + return Ret; + } + + static bool classof(const Action *A) { return A->getKind() == StorageDead; } + +private: + void anchor() override; +}; + +/// Represents the position of nodes in the cfg. +/// +/// `blockID` represents the index of the basic block, and `index` represents +/// the index of node in the current basic block. +struct Point { + unsigned blockID; + unsigned index; + + /// Indicates the `End('region_r)` of free region `'region_r`. + static const unsigned EndBlockID = -1u; + static const unsigned EndIndex = -1u; + + Point(unsigned blockID, unsigned index) : blockID(blockID), index(index) {} + + bool operator<(const Point &other) const { + if (this->blockID != other.blockID) + return this->blockID < other.blockID; + return this->index < other.index; + } + + std::string print() const { + if (blockID == EndBlockID && index == EndIndex) + return "End('region_r)"; + return "BB" + std::to_string(blockID) + '/' + std::to_string(index); + } +}; + +/// Represents the region scope of a region variable, consisting a series +/// points in the CFG. +struct Region { + std::set points; + + bool AddPoint(Point P) { + auto Ret = points.insert(P); + return Ret.second; + } + + bool MayContain(Point P) const { return points.find(P) != points.end(); } + + std::string print() const { + std::string Ret = "{ "; + unsigned index = 0; + for (const Point &point : points) { + Ret += point.print(); + if (index != points.size() - 1) + Ret += ", "; + ++index; + } + Ret += " }"; + return Ret; + } +}; + +/// A complete definition of a region variable, consisting of the corresponding +/// region name, region scope and a capped flag. +/// +/// Note that each region variable corresponds to a VarDecl or an explicit or +/// implicit borrow expression. +struct VarDefinition { + RegionName name; + + /// The current value of this region name. This is adjusted during region + /// check by calls to `AddLivePoint`, and then finally adjusted further by + /// the call to `Solve`. + Region value; + + /// Capped region names should no longer have to grow as a result of + /// inference. If they do wind up growing, we will report an error. + bool capped; + + VarDefinition(RegionName name, Region value, bool capped) + : name(name), value(value), capped(capped) {} +}; + +/// The constraint indicates `sub` outlives `sup` at `point`. +struct Constraint { + RegionVariable sub; + RegionVariable sup; + Point point; + + Constraint(RegionVariable sub, RegionVariable sup, Point point) + : sub(sub), sup(sup), point(point) {} + + std::string print() const { + return "Constraint { " + sub.print() + " : " + sup.print() + " @ " + + point.print() + " }" + '\n'; + } +}; + +class Environment { +public: + const FunctionDecl &fd; + const CFG &cfg; + const ASTContext &Ctx; + +public: + Environment(const FunctionDecl &fd, const CFG &cfg, const ASTContext &Ctx) + : fd(fd), cfg(cfg), Ctx(Ctx) {} + + llvm::SmallVector SuccessorPoints(Point point) const; +}; + +/// All the information required for inference solving, including the +/// definition of region variables and all the constraints in the function. +class InferenceContext { +private: + llvm::SmallVector definitions; + llvm::SmallVector constraints; + + /// Used for implicit borrows. + Region emptyRegion; + +public: + InferenceContext() {} + + RegionVariable AddVar(RegionName Name) { + size_t index = definitions.size(); +#if DEBUG_PRINT + llvm::outs() << Name.print() << " => " << RegionVariable(index).print() + << '\n'; +#endif + definitions.push_back(VarDefinition(Name, Region(), false)); + return RegionVariable(index); + } + + void CapVar(RegionVariable RV) { definitions[RV.index].capped = true; } + + void AddLivePoint(RegionVariable RV, Point P); + + void AddOutLives(RegionVariable Sup, RegionVariable Sub, Point P) { +#if DEBUG_PRINT + llvm::outs() << "AddOutLives: " << Sub.print() << " : " << Sup.print() + << " @ " << P.print() << '\n'; +#endif + constraints.push_back(Constraint(Sub, Sup, P)); + } + + const Region &getRegion(RegionVariable RV) const { + return definitions[RV.index].value; + } + + const Region &getEmptyRegion() const { return emptyRegion; } + + void Solve(const Environment &env); +}; + +class DFS { +private: + llvm::SmallVector stack; + std::set visited; + const Environment &env; + +public: + DFS(const Environment &env) : env(env) {} + + bool Copy(const Region &From, Region &To, Point StartPoint); +}; + +/// Compute the set of live variables at each point. +class Liveness { +private: + using LivenessFact = llvm::DenseSet; + + const Environment &env; + RegionCheck &rc; + + /// For a given key, which is a basic block, the value is the set of all live + /// variables at the entry of the block. + llvm::DenseMap liveness; + + void Kill(LivenessFact &fact, VarDecl *D) { fact.erase(D); } + + void Gen(LivenessFact &fact, VarDecl *D) { fact.insert(D); } + + bool SetFrom(LivenessFact &Dest, const LivenessFact &Src) { + if (Src.empty()) + return false; + + unsigned old = Dest.size(); + Dest.insert(Src.begin(), Src.end()); + return old != Dest.size(); + } + + template + void SimulateBlock(LivenessFact &fact, const CFGBlock *Block, CB callback); + +public: + Liveness(const Environment &env, RegionCheck &rc) : env(env), rc(rc) { + Compute(); + } + + void Compute(); + + template void Walk(CB callback); + + std::set LiveRegions(const LivenessFact &liveFact); + + void print() const { + llvm::outs() << "Liveness Result: \n"; + for (auto elem : liveness) { + llvm::outs() << "CFG Block ID: " << elem.first->getBlockID() << '\n'; + llvm::outs() << "Live Variables at block entry: \n"; + for (VarDecl *var : elem.second) { + llvm::outs() << var->getName() << '\t'; + } + llvm::outs() << '\n'; + } + } +}; + +struct Loan { + Point point; + const std::unique_ptr &path; + BorrowKind kind; + const Region ®ion; + + Loan(Point point, const std::unique_ptr &path, BorrowKind kind, + const Region ®ion) + : point(point), path(path), kind(kind), region(region) {} + + std::string print() const { + std::string Ret; + Ret += " Loan {\n"; + Ret += " point: " + point.print() + "\n"; + Ret += " path: " + path->print() + "\n"; + Ret += " kind: "; + if (kind == BorrowKind::Mut) + Ret += "Mut\n"; + else + Ret += "Shared\n"; + Ret += " region: " + region.print() + "\n"; + Ret += " }"; + return Ret; + } +}; + +class LoansInScope { +private: + using LoansFact = llvm::DenseSet; + + const Environment &env; + const RegionCheck &rc; + + /// All loans in the function. + llvm::SmallVector loans; + + llvm::DenseMap loansInScopeAfterBlock; + + /// For a given key, which is a point of the CFG, the value is a vector of + /// the index of all loans at the entry of the point. + std::map> loansByPoint; + + void Kill(LoansFact &fact, unsigned index) { fact.erase(index); } + + void Gen(LoansFact &fact, std::vector indexes) { + fact.insert(indexes.begin(), indexes.end()); + } + + bool SetFrom(LoansFact &Dest, const LoansFact &Src) { + if (Src.empty()) + return false; + + unsigned old = Dest.size(); + Dest.insert(Src.begin(), Src.end()); + return old != Dest.size(); + } + + llvm::SmallVector LoansNotInScopeAt(Point point) const { + llvm::SmallVector ret; + for (const Loan *it = loans.begin(), *ei = loans.end(); it != ei; ++it) { + if (!it->region.MayContain(point)) + ret.push_back(std::distance(loans.begin(), it)); + } + return ret; + } + + llvm::SmallVector LoansKilledByWriteTo(const Path *path) const; + + template + void SimulateBlock(LoansFact &fact, const CFGBlock *Block, CB callback); + +public: + LoansInScope(const Environment &env, const RegionCheck &rc); + + void Compute(); + + template void Walk(CB callback); +}; + +enum class Depth { Shallow, Deep }; + +enum class Mode { Read, Write }; + +enum class BorrowDiagKind { + ForImmutWhenMut, + ForMove, + ForMultiMut, + ForMutWhenImmut, + ForRead, + ForWrite, + ForStorageDead, + BorrowMaxDiagKind +}; + +const unsigned BorrowDiagIdList[] = { + diag::err_borrow_immut_borrow_when_mut_borrowed, + diag::err_borrow_move_when_borrowed, + diag::err_borrow_mut_borrow_more_than_once, + diag::err_borrow_mut_borrow_when_immut_borrowed, + diag::err_borrow_use_when_mut_borrowed, + diag::err_borrow_not_live_long, + diag::err_borrow_assign_when_borrowed}; + +struct BorrowDiagInfo { + BorrowDiagKind Kind; + SourceLocation Location; + std::string path; + SourceLocation LoanLoc; + std::string loanPath; + + BorrowDiagInfo(BorrowDiagKind Kind, SourceLocation Location, std::string path, + SourceLocation LoanLoc, std::string loanPath = "") + : Kind(Kind), Location(Location), path(path), LoanLoc(LoanLoc), + loanPath(loanPath) {} +}; + +class BorrowDiagReporter { +private: + Sema &S; + llvm::SmallVector Infos; + + void flushDiagnostics() { + for (BorrowDiagInfo Info : Infos) { + switch (Info.Kind) { + case BorrowDiagKind::ForImmutWhenMut: + S.Diag(Info.Location, diag::err_borrow_immut_borrow_when_mut_borrowed) + << Info.path; + S.Diag(Info.LoanLoc, diag::note_mutable_borrow_occurs_here); + break; + case BorrowDiagKind::ForMove: + S.Diag(Info.Location, diag::err_borrow_move_when_borrowed) << Info.path; + S.Diag(Info.LoanLoc, diag::note_borrowed_here) << Info.loanPath; + break; + case BorrowDiagKind::ForMultiMut: + S.Diag(Info.Location, diag::err_borrow_mut_borrow_more_than_once) + << Info.path; + S.Diag(Info.LoanLoc, diag::note_first_mut_borrow_occurs_here); + break; + case BorrowDiagKind::ForMutWhenImmut: + S.Diag(Info.Location, diag::err_borrow_mut_borrow_when_immut_borrowed) + << Info.path; + S.Diag(Info.LoanLoc, diag::note_immutable_borrow_occurs_here); + break; + case BorrowDiagKind::ForRead: + S.Diag(Info.Location, diag::err_borrow_use_when_mut_borrowed) + << Info.path; + S.Diag(Info.LoanLoc, diag::note_borrowed_here) << Info.loanPath; + break; + case BorrowDiagKind::ForStorageDead: + S.Diag(Info.Location, diag::err_borrow_not_live_long) << Info.path; + S.Diag(Info.LoanLoc, diag::note_dropped_while_borrowed) << Info.path; + break; + case BorrowDiagKind::ForWrite: + S.Diag(Info.Location, diag::err_borrow_assign_when_borrowed) + << Info.path; + S.Diag(Info.LoanLoc, diag::note_borrowed_here) << Info.path; + break; + default: + break; + } + S.getDiagnostics().increaseBorrowCheckErrors(); + } + } + +public: + BorrowDiagReporter(Sema &S) : S(S) {} + + ~BorrowDiagReporter() { flushDiagnostics(); } + + void ForImmutWhenMut(SourceLocation Location, const Path *path, + SourceLocation LoanLoc) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForImmutWhenMut, Location, + path->to_string(), LoanLoc)); + } + + void ForMove(SourceLocation Location, const Path *path, + SourceLocation LoanLoc, const Path *loanPath) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForMove, Location, + path->to_string(), LoanLoc, + loanPath->to_string())); + } + + void ForMultiMut(SourceLocation Location, const Path *path, + SourceLocation LoanLoc) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForMultiMut, Location, + path->to_string(), LoanLoc)); + } + + void ForMutWhenImmut(SourceLocation Location, const Path *path, + SourceLocation LoanLoc) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForMutWhenImmut, Location, + path->to_string(), LoanLoc)); + } + + void ForRead(SourceLocation Location, const Path *path, + SourceLocation LoanLoc, const Path *loanPath) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForRead, Location, + path->to_string(), LoanLoc, + loanPath->to_string())); + } + + void ForStorageDead(SourceLocation Location, const Path *path, + SourceLocation LoanLoc) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForStorageDead, LoanLoc, + path->to_string(), Location)); + } + + void ForWrite(SourceLocation Location, const Path *path, + SourceLocation LoanLoc) { + Infos.push_back(BorrowDiagInfo(BorrowDiagKind::ForWrite, Location, + path->to_string(), LoanLoc)); + } + + unsigned getBorrowDiagID(BorrowDiagKind Kind) { + unsigned index = static_cast(Kind); + assert(index < static_cast(BorrowDiagKind::BorrowMaxDiagKind) && + "Unknown error type"); + return BorrowDiagIdList[index]; + } + + void emitDiag(BorrowDiagKind Kind, SourceLocation Location, const Path *path, + SourceLocation LoanLoc, const Path *loanPath = nullptr) { + if (S.getDiagnostics().getDiagnosticLevel( + getBorrowDiagID(Kind), Location) == DiagnosticsEngine::Ignored) { + return; + } + switch (Kind) { + case BorrowDiagKind::ForImmutWhenMut: + ForImmutWhenMut(Location, path, LoanLoc); + break; + case BorrowDiagKind::ForMove: + ForMove(Location, path, LoanLoc, loanPath); + break; + case BorrowDiagKind::ForMultiMut: + ForMultiMut(Location, path, LoanLoc); + break; + case BorrowDiagKind::ForMutWhenImmut: + ForMutWhenImmut(Location, path, LoanLoc); + break; + case BorrowDiagKind::ForRead: + ForRead(Location, path, LoanLoc, loanPath); + break; + case BorrowDiagKind::ForStorageDead: + ForStorageDead(Location, path, LoanLoc); + break; + case BorrowDiagKind::ForWrite: + ForWrite(Location, path, LoanLoc); + break; + default: + break; + } + } +}; + +class BorrowCheck { +private: + BorrowDiagReporter &reporter; + const Environment &env; + Point point; + const llvm::SmallVector &loans; + bool IsBorrow = false; + + void CheckBorrows(Depth depth, Mode accessMode, + const std::unique_ptr &path); + void CheckMove(const std::unique_ptr &path); + void CheckMutBorrow(const std::unique_ptr &path); + void CheckRead(const std::unique_ptr &path); + void CheckShallowWrite(const std::unique_ptr &path); + void CheckStorageDead(const std::unique_ptr &path); + + llvm::SmallVector + FindLoansThatFreeze(const std::unique_ptr &path); + llvm::SmallVector + FindLoansThatIntersect(const std::unique_ptr &path); + llvm::SmallVector + FrozenByBorrowOf(const std::unique_ptr &path); + +public: + BorrowCheck(BorrowDiagReporter &reporter, const Environment &env, Point point, + const llvm::SmallVector &loans) + : reporter(reporter), env(env), point(point), loans(loans) {} + + void CheckAction(const std::unique_ptr &action); +}; + +class RegionCheck { +private: + BorrowDiagReporter &reporter; + Environment env; + InferenceContext infer; + std::unordered_map declToRegionNameMap; + std::unordered_map stmtToRegionNameMap; + std::map regionMap; + std::map>> actionMap; + + void PopulateInference(Liveness &liveness); + RegionVariable getRegionVariable(RegionName RV); + void EnsureBorrowSource(Point SuccPoint, RegionName BorrowRegionName, + const std::unique_ptr &SourcePath); + void RelateRegions(Point SuccPoint, RegionName Sub, RegionName Sup); + void PreprocessForParamAndReturn(); + +public: + RegionCheck(const FunctionDecl &fd, const CFG &cfg, ASTContext &Ctx, + BorrowDiagReporter &Reporter) + : reporter(Reporter), env(fd, cfg, Ctx) {} + + void Check(); + + /// Get the corresponding RegionName for a Decl in the regionMap. + /// If not existing, create a RegionName and return it. + RegionName getRegionName(Decl *D); + + /// Get the corresponding RegionName for a Stmt in the regionMap. + /// If not existing, create a RegionName and return it. + RegionName getRegionName(Stmt *S); + + const Region &getRegion(RegionName RN) const; + + const Region &getEmptyRegion() const { return infer.getEmptyRegion(); } + + BorrowDiagReporter &getReporter() { return reporter; } + + const std::map>> & + getActionMap() const { + return actionMap; + } +}; + +void BorrowCk(const Environment &env, RegionCheck &rc, LoansInScope &LIS); +void runBorrowChecker(const FunctionDecl &fd, const CFG &cfg, ASTContext &Ctx, + BorrowDiagReporter &Reporter); + +} // end namespace borrow +} // end namespace clang + +#endif // ENABLE_BSC + +#endif // LLVM_CLANG_ANALYSIS_ANALYSES_BSCBORROWCHECKER_H \ No newline at end of file diff --git a/clang/include/clang/Analysis/Analyses/BSC/BSCFatPtrCheck.h b/clang/include/clang/Analysis/Analyses/BSC/BSCFatPtrCheck.h new file mode 100644 index 0000000000000000000000000000000000000000..e9df8841a98993dc2338f4d11714f8150fcd28bc --- /dev/null +++ b/clang/include/clang/Analysis/Analyses/BSC/BSCFatPtrCheck.h @@ -0,0 +1,30 @@ +//===- 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_BSCFATPTRCHECK_H +#define LLVM_CLANG_ANALYSIS_ANALYSES_BSCFATPTRCHECK_H + +#if ENABLE_BSC + +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CallGraph.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Sema/Sema.h" + +namespace clang { +void runFatPtrReduntantCheck(const FunctionDecl &fd, const CFG &cfg, + AnalysisDeclContext &ac, ASTContext &ctx); +} + +#endif // ENABLE_BSC + +#endif // LLVM_CLANG_ANALYSIS_ANALYSES_BSCFATPTRCHECK_H \ No newline at end of file diff --git a/clang/include/clang/Analysis/Analyses/BSCNullabilityCheck.h b/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h similarity index 76% rename from clang/include/clang/Analysis/Analyses/BSCNullabilityCheck.h rename to clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h index d7cec1207cd5901b64a563572661ffd9a476cfb3..7e862348f0b47536b7395b7c28e7fbe1ac77c7c9 100644 --- a/clang/include/clang/Analysis/Analyses/BSCNullabilityCheck.h +++ b/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h @@ -28,8 +28,17 @@ enum NullabilityCheckDiagKind { NullableCastNonnull, NullablePointerDereference, NullablePointerAccessMember, + NullabilityMaxDiagKind }; +const unsigned NullabilityDiagIdList[] = { + diag::err_nonnull_assigned_by_nullable, + diag::err_pass_nullable_argument, + diag::err_return_nullable, + diag::err_nullable_cast_nonnull, + diag::err_nullable_pointer_dereference, + diag::err_nullable_pointer_access_member}; + struct NullabilityCheckDiagInfo { SourceLocation Loc; NullabilityCheckDiagKind Kind; @@ -60,9 +69,22 @@ public: if (DI == *it) return; } + if (S.getDiagnostics().getDiagnosticLevel(getNullabilityDiagID(DI.Kind), + DI.Loc) == + DiagnosticsEngine::Ignored) { + return; + } DIV.push_back(DI); } + unsigned getNullabilityDiagID(NullabilityCheckDiagKind Kind) { + unsigned index = static_cast(Kind); + assert(index < static_cast( + NullabilityCheckDiagKind::NullabilityMaxDiagKind) && + "Unknown error type"); + return NullabilityDiagIdList[index]; + } + void flushDiagnostics() { // Sort the diag info by SourceLocation. While not strictly // guaranteed to produce them in line/column order, this will provide @@ -75,29 +97,7 @@ public: }); 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 NullableCastNonnull: - S.Diag(DI.Loc, diag::err_nullable_cast_nonnull); - 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.Diag(DI.Loc, getNullabilityDiagID(DI.Kind)); S.getDiagnostics().increaseNullabilityCheckErrors(); } } diff --git a/clang/include/clang/Analysis/Analyses/BSCOwnership.h b/clang/include/clang/Analysis/Analyses/BSC/BSCOwnership.h similarity index 85% rename from clang/include/clang/Analysis/Analyses/BSCOwnership.h rename to clang/include/clang/Analysis/Analyses/BSC/BSCOwnership.h index a2c673e6f5edbb6a2ae5b2eb68c992be92578117..f84f3ba72087a3a30add2122b9f9c7e5a16535b5 100644 --- a/clang/include/clang/Analysis/Analyses/BSCOwnership.h +++ b/clang/include/clang/Analysis/Analyses/BSC/BSCOwnership.h @@ -186,8 +186,30 @@ enum OwnershipDiagKind { InvalidCastFieldOwned, FieldMemoryLeak, MemoryLeak, + OwnershipMaxDiagKind }; +const unsigned OwnershipDiagIdList[] = { + diag::err_ownership_use_moved, + diag::err_ownership_use_partially_moved, + diag::err_ownership_use_all_moved, + diag::err_ownership_use_possibly_uninit, + diag::err_ownership_use_uninit, + diag::err_ownership_assign_owned, + diag::err_ownership_assign_partially_moved, + diag::err_ownership_assign_possibly_partially_moved, + diag::err_ownership_assign_all_moved, + diag::err_ownership_assign_field_uninit, + diag::err_ownership_assign_field_owned, + diag::err_ownership_assign_field_moved, + diag::err_ownership_assign_field_subfield_owned, + diag::err_ownership_cast_moved, + diag::err_ownership_cast_owned, + diag::err_ownership_cast_uninit, + diag::err_ownership_cast_subfield_owned, + diag::err_ownership_memory_leak_field, + diag::err_ownership_memory_leak}; + class OwnershipDiagInfo { public: SourceLocation Loc; @@ -235,9 +257,22 @@ public: if (DI == *it) return; } + if (S.getDiagnostics().getDiagnosticLevel(getOwnershipDiagID(DI.Kind), + DI.Loc) == + DiagnosticsEngine::Ignored) { + return; + } DIV.push_back(DI); } + unsigned getOwnershipDiagID(OwnershipDiagKind Kind) { + unsigned index = static_cast(Kind); + assert(index < + static_cast(OwnershipDiagKind::OwnershipMaxDiagKind) && + "Unknown error type"); + return OwnershipDiagIdList[index]; + } + void flushDiagnostics() { // Sort the diag info by SourceLocation. While not strictly // guaranteed to produce them in line/column order, this will provide @@ -250,68 +285,28 @@ public: for (const OwnershipDiagInfo &DI : DIV) { switch (DI.Kind) { - case InvalidUseOfMoved: - S.Diag(DI.Loc, diag::err_ownership_use_moved) << DI.Name; - break; - case InvalidUseOfPartiallyMoved: - S.Diag(DI.Loc, diag::err_ownership_use_partially_moved) - << DI.Name << DI.Fields; - break; - case InvalidUseOfAllMoved: - S.Diag(DI.Loc, diag::err_ownership_use_all_moved) << DI.Name; - break; - case InvalidUseOfUninit: - S.Diag(DI.Loc, diag::err_ownership_use_uninit) << DI.Name; - break; - case InvalidUseOfPossiblyUninit: - S.Diag(DI.Loc, diag::err_ownership_use_possibly_uninit) << DI.Name; - break; case InvalidAssignOfOwned: - S.Diag(DI.Loc, diag::err_ownership_assign_owned) << DI.Name; - break; - case InvalidAssignOfPartiallyMoved: - S.Diag(DI.Loc, diag::err_ownership_assign_partially_moved) - << DI.Name << DI.Fields; - break; - case InvalidAssignOfPossiblyPartiallyMoved: - S.Diag(DI.Loc, diag::err_ownership_assign_possibly_partially_moved) - << DI.Name << DI.Fields; - break; case InvalidAssignOfAllMoved: - S.Diag(DI.Loc, diag::err_ownership_assign_all_moved) << DI.Name; - break; case InvalidAssignFieldOfUninit: - S.Diag(DI.Loc, diag::err_ownership_assign_field_uninit) << DI.Name; - break; case InvalidAssignFieldOfOwned: - S.Diag(DI.Loc, diag::err_ownership_assign_field_owned) << DI.Name; - break; case InvalidAssignFieldOfMoved: - S.Diag(DI.Loc, diag::err_ownership_assign_field_moved) << DI.Name; - break; - case InvalidAssignSubFieldOwned: - S.Diag(DI.Loc, diag::err_ownership_assign_field_subfield_owned) - << DI.Name << DI.Fields; - break; case InvalidCastMoved: - S.Diag(DI.Loc, diag::err_ownership_cast_moved) << DI.Name; - break; case InvalidCastOwned: - S.Diag(DI.Loc, diag::err_ownership_cast_owned) << DI.Name; - break; case InvalidCastUninit: - S.Diag(DI.Loc, diag::err_ownership_cast_uninit) << DI.Name; + case InvalidUseOfAllMoved: + case InvalidUseOfMoved: + case InvalidUseOfPossiblyUninit: + case InvalidUseOfUninit: + case MemoryLeak: + S.Diag(DI.Loc, getOwnershipDiagID(DI.Kind)) << DI.Name; break; + case InvalidAssignOfPartiallyMoved: + case InvalidAssignOfPossiblyPartiallyMoved: + case InvalidAssignSubFieldOwned: case InvalidCastFieldOwned: - S.Diag(DI.Loc, diag::err_ownership_cast_subfield_owned) - << DI.Name << DI.Fields; - break; + case InvalidUseOfPartiallyMoved: case FieldMemoryLeak: - S.Diag(DI.Loc, diag::err_ownership_memory_leak_field) - << DI.Name << DI.Fields; - break; - case MemoryLeak: - S.Diag(DI.Loc, diag::err_ownership_memory_leak) << DI.Name; + S.Diag(DI.Loc, getOwnershipDiagID(DI.Kind)) << DI.Name << DI.Fields; break; default: llvm_unreachable("Unknown error type"); diff --git a/clang/include/clang/Analysis/CFG.h b/clang/include/clang/Analysis/CFG.h index 2a27a377c54cfdcb8f8bcf72027bde7639780524..7bc04a64688c4a3de1c9f1e4cd1d16d86fbbb5b5 100644 --- a/clang/include/clang/Analysis/CFG.h +++ b/clang/include/clang/Analysis/CFG.h @@ -1245,9 +1245,10 @@ public: bool AddLoopExit = false; bool AddTemporaryDtors = false; bool AddScopes = false; - #if ENABLE_BSC +#if ENABLE_BSC bool BSCMode = false; - #endif + bool BSCBorrowCk = false; +#endif bool AddStaticInitBranches = false; bool AddCXXNewAllocator = false; bool AddCXXDefaultInitExprInCtors = false; diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCFrontendKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCFrontendKinds.td new file mode 100644 index 0000000000000000000000000000000000000000..57dd04ad5de84881be04da00a77abed71ae0de0c --- /dev/null +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCFrontendKinds.td @@ -0,0 +1,3 @@ +def warn_unknown_bsc_diag_option : Warning< + "unknown %select{warning|remark|error}0 option '%1'%select{|; did you mean '%3'?}2">, + InGroup; \ No newline at end of file diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td b/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td index 59f8b42f811dfba9862ae9b19869f9c6ba4ab24a..1ad0044454853c3c9cdcbc2a9b54d417be5e0039 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td @@ -1,2 +1,50 @@ def BSCTraitMissing : DiagGroup<"bsc-trait-missing">; -def BSCOwnedStruct : DiagGroup<"bsc-owned-struct">; \ No newline at end of file +def BSCOwnedStruct : DiagGroup<"bsc-owned-struct">; + +def DerefNullable : DiagGroup<"deref-nullable">; +def PassNullable : DiagGroup<"pass-nullable">; +def ReturnNullable : DiagGroup<"return-nullable">; +def CastNullable : DiagGroup<"cast-nullable">; +def AssignNullable : DiagGroup<"assign-nullable">; +def AssignNonnull : DiagGroup<"assign-nonnull">; +def BSCNullability : DiagGroup<"bsc-nullability", + [DerefNullable, + PassNullable, + ReturnNullable, + CastNullable, + AssignNullable, + AssignNonnull]>; + +def UseMovedOwned : DiagGroup<"use-moved-owned">; +def UseUninitOwned : DiagGroup<"use-uninit-owned">; +def UseOwned : DiagGroup<"use-owned",[UseMovedOwned, UseUninitOwned]>; +def AssignMovedOwned : DiagGroup<"assign-moved-owned">; +def AssignUninitOwned : DiagGroup<"assign-uninit-owned">; +def AssignOwned : DiagGroup<"assign-owned",[AssignUninitOwned, AssignMovedOwned]>; +def CastMovedOwned : DiagGroup<"cast-moved-owned">; +def CastOwned : DiagGroup<"cast-owned", [CastMovedOwned]>; +def CheckMemoryLeak : DiagGroup<"check-memory-leak">; +def BSCOwnership : DiagGroup<"bsc-ownership", + [UseOwned, + AssignOwned, + CastOwned, + CheckMemoryLeak]>; + +def AssignBorrowed : DiagGroup<"assign-borrowed">; +def MoveBorrowed : DiagGroup<"move-borrowed">; +def UseMutablyBorrowed : DiagGroup<"use-mutably-borrowed">; +def RepeatedBorrow : DiagGroup<"repeated-borrow">; +def ReturnLocalBorrow : DiagGroup<"return-local-borrow">; +def ShortLifeBorrow : DiagGroup<"short-life-borrow">; +def BSCBorrow : DiagGroup<"bsc-borrow", + [AssignBorrowed, + MoveBorrowed, + UseMutablyBorrowed, + RepeatedBorrow, + ReturnLocalBorrow, + ShortLifeBorrow]>; + +def BSCSafetyCheck : DiagGroup<"bsc-safety-check", + [BSCNullability, + BSCOwnership, + BSCBorrow]>; \ No newline at end of file diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td index 61485f99f7c2c8c83e63e1039a7eb51979d2560d..a73a262c2ce9351305b1f02a399d331b7995187d 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -8,11 +8,11 @@ def err_cvrqualified_this_type_unsupported : Error< "owned type requires a pointer, '%0' is invalid">; def err_this_type_unsupported : Error< "type of parameter 'this' is wrong, expected 'This*' or 'This' or '%0*' or '%0', but found '%1'">; -def err_incompatible_pointer_cast : Error< +def err_incompatible_pointer_cast : Error< "incompatible conversion from pointer type '%0' to non-pointer type '%1' in member function call">; -def err_incompatible_const_cast : Error< +def err_incompatible_const_cast : Error< "incompatible conversion from const type '%0' to non-const type '%1' in member function call">; -def err_incompatible_owned_cast : Error< +def err_incompatible_owned_cast : Error< "incompatible conversion from non owned type '%0' to owned type '%1' in member function call">; // BSC attributes warnings and errors. @@ -139,50 +139,68 @@ def err_owned_struct_destructor_body : Error<"destructor must have a function bo // BSC Ownership Analysis errors. // use def err_ownership_use_moved : Error< - "use of moved value: `%0`">; + "use of moved value: `%0`">, + InGroup; def err_ownership_use_partially_moved : Error< - "use of partially moved value: `%0`, %1 moved">; + "use of partially moved value: `%0`, %1 moved">, + InGroup; def err_ownership_use_all_moved: Error< - "use of all moved value: `%0`">; + "use of all moved value: `%0`">, + InGroup; def err_ownership_use_uninit : Error< - "use of uninitialized value: `%0`">; + "use of uninitialized value: `%0`">, + InGroup; def err_ownership_use_possibly_uninit : Error< - "use of possibly uninitialized value: `%0`">; + "use of possibly uninitialized value: `%0`">, + InGroup; // assign def err_ownership_assign_owned : Error< - "assign to owned value: `%0`">; + "assign to owned value: `%0`">, + InGroup; def err_ownership_assign_partially_moved : Error< - "assign to partially moved value: `%0`, %1 moved">; + "assign to partially moved value: `%0`, %1 moved">, + InGroup; def err_ownership_assign_possibly_partially_moved : Error< - "assign to possibly partially moved value: `%0`, %1 possibly moved">; + "assign to possibly partially moved value: `%0`, %1 possibly moved">, + InGroup; def err_ownership_assign_all_moved : Error< - "assign to all moved value: `%0`">; + "assign to all moved value: `%0`">, + InGroup; // field assign def err_ownership_assign_field_uninit : Error< - "assign to part of uninitialized value: `%0`">; + "assign to part of uninitialized value: `%0`">, + InGroup; def err_ownership_assign_field_owned : Error< - "assign to part of owned value: `%0`">; + "assign to part of owned value: `%0`">, + InGroup; def err_ownership_assign_field_moved : Error< - "assign to part of moved value: `%0`">; + "assign to part of moved value: `%0`">, + InGroup; def err_ownership_assign_field_subfield_owned : Error< - "assign to subfield owned value: `%0`, %1 owned">; + "assign to subfield owned value: `%0`, %1 owned">, + InGroup; // cast to `void * owned` def err_ownership_cast_moved : Error< - "invalid cast to `void * owned` of moved value: `%0`">; + "invalid cast to `void * owned` of moved value: `%0`">, + InGroup; def err_ownership_cast_owned : Error< - "invalid cast to `void * owned` of owned value: `%0`">; + "invalid cast to `void * owned` of owned value: `%0`">, + InGroup; def err_ownership_cast_uninit: Error< - "invalid cast to `void * owned` of uninit value: `%0`">; + "invalid cast to `void * owned` of uninit value: `%0`">, + InGroup; def err_ownership_cast_subfield_owned : Error< - "invalid cast to `void * owned` of not all moved value: `%0`, %1 owned">; - + "invalid cast to `void * owned` of not all moved value: `%0`, %1 owned">, + InGroup; // memory leak def err_ownership_memory_leak_field : Error< - "field memory leak of value: `%0`, %1 leak">; + "field memory leak of value: `%0`, %1 leak">, + InGroup; def err_ownership_memory_leak : Error< - "memory leak of value: `%0`">; + "memory leak of value: `%0`">, + InGroup; // BSC Borrow Checker errors. def err_borrow_live_longer_than_target_var : Error< @@ -199,6 +217,37 @@ def note_previous_borrow : Note<"previous borrow is here">; def err_use_expired_borrow_var : Error< "Can not use '%0' because expired">; +// New BSC Borrow Checker errors. +def err_borrow_assign_when_borrowed : Error< + "cannot assign to `%0` because it is borrowed">, + InGroup; +def err_borrow_move_when_borrowed : Error< + "cannot move out of `%0` because it is borrowed">, + InGroup; +def err_borrow_use_when_mut_borrowed : Error< + "cannot use `%0` because it was mutably borrowed">, + InGroup; +def note_borrowed_here : Note<"`%0` is borrowed here">; +def err_borrow_mut_borrow_more_than_once : Error< + "cannot borrow `%0` as mutable more than once at a time">, + InGroup; +def note_first_mut_borrow_occurs_here : Note<"first mut borrow occurs here">; +def err_borrow_return_local_borrow : Error< + "cannot return reference to local variable `%0`">, + InGroup; +def err_borrow_not_live_long : Error< + "`%0` does not live long enough">, + InGroup; +def note_dropped_while_borrowed: Note<"`%0` dropped here while still borrowed">; +def err_borrow_immut_borrow_when_mut_borrowed : Error< + "cannot borrow `%0` as immutable because it is also borrowed as mutable">, + InGroup; +def note_mutable_borrow_occurs_here : Note<"mutable borrow occurs here">; +def err_borrow_mut_borrow_when_immut_borrowed : Error< + "cannot borrow `%0` as mutable because it is also borrowed as immutable">, + InGroup; +def note_immutable_borrow_occurs_here : Note<"immutable borrow occurs here">; + // BSC operator overload errors. def err_unsupport_overload_fun : Error<"function unsupport overload">; def err_operator_overload_needs_point : Error< @@ -206,14 +255,20 @@ def err_operator_overload_needs_point : Error< // BSC Pointer Nullability Check errors. def err_nullable_pointer_dereference : Error< - "nullable pointer cannot be dereferenced">; + "nullable pointer cannot be dereferenced">, + InGroup; def err_pass_nullable_argument : Error< - "cannot pass nullable pointer argument">; + "cannot pass nullable pointer argument">, + InGroup; def err_return_nullable : Error< - "cannot return nullable pointer type">; + "cannot return nullable pointer type">, + InGroup; def err_nullable_cast_nonnull : Error< - "cannot cast nullable pointer to nonnull type">; + "cannot cast nullable pointer to nonnull type">, + InGroup; def err_nullable_pointer_access_member : Error< - "cannot access member througn nullable pointer">; + "cannot access member through nullable pointer">, + InGroup; def err_nonnull_assigned_by_nullable : Error< - "nonnull pointer cannot be assigned by nullable pointer">; \ No newline at end of file + "nonnull pointer cannot be assigned by nullable pointer">, + InGroup; diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 50f7e2b9221d02b0a781fe58cfddd4e95b327658..2e4f250ee33f1c76e8fe0774803b94caf37e6bd4 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -322,4 +322,9 @@ def warn_profile_data_misexpect : Warning< InGroup; } // end of instrumentation issue category +// BSC warnings and errors +#ifdef BSC +include "clang/Basic/BSC/DiagnosticBSCFrontendKinds.td" +#endif + } diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 91b180f8004deafd6323a55b50ce1842d50431a1..48a2ccde73ec959ba3c113e545ea736a13fc7a5f 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -95,6 +95,10 @@ namespace clang { ///< problem. Can be made fatal by -Werror. Remark ///< A diagnostic that indicates normal progress through ///< compilation. +#if ENABLE_BSC + , + Error +#endif }; } diff --git a/clang/include/clang/Basic/DiagnosticOptions.h b/clang/include/clang/Basic/DiagnosticOptions.h index c4134835b5dec9c760ac7916705372ed9abda768..4c049ce7cf8e9aa7d5b2edde22a271b9c7f8057a 100644 --- a/clang/include/clang/Basic/DiagnosticOptions.h +++ b/clang/include/clang/Basic/DiagnosticOptions.h @@ -121,6 +121,11 @@ public: /// The prefixes for comment directives sought by -verify ("expected" by /// default). std::vector VerifyPrefixes; +#if ENABLE_BSC + /// The list of -E... options used to alter the diagnostic mappings, with the + /// prefixes removed. + std::vector Errors; +#endif public: // Define accessors/mutators for diagnostic options of enumeration type. diff --git a/clang/include/clang/Driver/BSC/BSCOptions.td b/clang/include/clang/Driver/BSC/BSCOptions.td index 1b7d0328d08c4f108fc4d1f91b02264f32eb13aa..f07573965822a0f64aee1c40921be25cad5b82f9 100644 --- a/clang/include/clang/Driver/BSC/BSCOptions.td +++ b/clang/include/clang/Driver/BSC/BSCOptions.td @@ -10,4 +10,9 @@ def nullability_check : Joined<["-"], "nullability-check=">, Flags<[CC1Option]>, HelpText<"Select Nullability Check Enable Zone">, Values<"none,safeonly,all">, NormalizedValuesScope<"LangOptions">, NormalizedValues<["NC_NONE", "NC_SAFE", "NC_ALL"]>, - MarshallingInfoEnum, "NC_SAFE">; \ No newline at end of file + MarshallingInfoEnum, "NC_SAFE">; +def E_Group : OptionGroup<"">, Group, DocFlatten; +def E_value_Group : OptionGroup<"">, Group, + DocFlatten; +def E_Joined : Joined<["-"], "E">, Group, Flags<[CC1Option, CoreOption]>, + MetaVarName<"">, HelpText<"Enable the specified error">; \ No newline at end of file diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index f492feaabcf852abe1676e94079a129b0f98c403..9b81a134c8004faff3a3e71c316dc1593778cea8 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12409,6 +12409,7 @@ public: bool CheckTemporaryVarMemoryLeak(Expr* E); void BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck = true, bool EnableNullabilityCheck = true); + void BSCBorrowChecker(FunctionDecl *FD); bool IsInSafeZone(); bool IsSafeBuiltinTypeConversion(BuiltinType::Kind SourceType, BuiltinType::Kind DestType); diff --git a/clang/lib/AST/BSC/TypeBSC.cpp b/clang/lib/AST/BSC/TypeBSC.cpp index bba9b782c748f8eec03dac9fdf0fce091154196a..a4c393f37b090b1b6631c4cc4132b13f999ccf7c 100644 --- a/clang/lib/AST/BSC/TypeBSC.cpp +++ b/clang/lib/AST/BSC/TypeBSC.cpp @@ -66,6 +66,13 @@ bool Type::hasBorrowFields() const { return false; } +bool Type::withBorrowFields() const { + if (const auto *RT = dyn_cast(CanonicalType)) { + return RT->withBorrowFields(); + } + return false; +} + bool FunctionProtoType::hasOwnedRetOrParams() const { if (getReturnType().isOwnedQualified()) { return true; @@ -254,6 +261,28 @@ bool RecordType::hasBorrowFields() const { return false; } +bool RecordType::withBorrowFields() const { + std::vector RecordTypeList; + RecordTypeList.push_back(this); + unsigned NextToCheckIndex = 0; + + while (RecordTypeList.size() > NextToCheckIndex) { + for (FieldDecl *FD : + RecordTypeList[NextToCheckIndex]->getDecl()->fields()) { + QualType FieldTy = FD->getType(); + if (FieldTy.isBorrowQualified()) + return true; + FieldTy = FieldTy.getCanonicalType(); + if (const auto *FieldRecTy = FieldTy->getAs()) { + if (!llvm::is_contained(RecordTypeList, FieldRecTy)) + RecordTypeList.push_back(FieldRecTy); + } + } + ++NextToCheckIndex; + } + return false; +} + bool Type::isBSCFutureType() const { if (const auto *RT = getAs()) { RecordDecl *RD = RT->getAsRecordDecl(); diff --git a/clang/lib/Analysis/BSCBorrowCheck.cpp b/clang/lib/Analysis/BSC/BSCBorrowCheck.cpp similarity index 99% rename from clang/lib/Analysis/BSCBorrowCheck.cpp rename to clang/lib/Analysis/BSC/BSCBorrowCheck.cpp index bc9243999a9c8dceb779aaa1aa5a2568c14eb098..8a229525182553ed7beded50cfc25ffb53111454 100644 --- a/clang/lib/Analysis/BSCBorrowCheck.cpp +++ b/clang/lib/Analysis/BSC/BSCBorrowCheck.cpp @@ -12,7 +12,7 @@ #if ENABLE_BSC -#include "clang/Analysis/Analyses/BSCBorrowCheck.h" +#include "clang/Analysis/Analyses/BSC/BSCBorrowCheck.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" diff --git a/clang/lib/Analysis/BSC/BSCBorrowChecker.cpp b/clang/lib/Analysis/BSC/BSCBorrowChecker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4d3ff316ed87034f0e3049441e3554aaa98cba57 --- /dev/null +++ b/clang/lib/Analysis/BSC/BSCBorrowChecker.cpp @@ -0,0 +1,1707 @@ +//===- BSCBorrowChecker.cpp - Borrow Check for Source CFGs -*- BSC --*--------// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements BSC borrow checker for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#if ENABLE_BSC + +#include "clang/Analysis/Analyses/BSC/BSCBorrowChecker.h" +#include "clang/AST/StmtVisitor.h" + +using namespace clang; +using namespace clang::borrow; + +static bool IsTrackedType(QualType type) { + return type.isBorrowQualified() || type->withBorrowFields(); +} + +namespace { +/// Given a statement, returns the corresponding (defs, uses). +/// +/// The `defs` contains variables whose current value is completely +/// overwritten, and the `uses` contains variables whose current value is used. +/// Note that a variable may exist in both sets. +class DefUse : public clang::StmtVisitor { + enum { None, Def, Use } Action; + llvm::SmallVector defs; + llvm::SmallVector uses; + +public: + DefUse(Stmt *S) { + Action = None; + Visit(S); + } + + const llvm::SmallVector &getDefs() const { return defs; } + const llvm::SmallVector &getUses() const { return uses; } + + void VisitBinaryOperator(BinaryOperator *BO); + void VisitBinAssign(BinaryOperator *BO); + void VisitCallExpr(CallExpr *CE); + void VisitDeclRefExpr(DeclRefExpr *DRE); + void VisitDeclStmt(DeclStmt *DS); + void VisitMemberExpr(MemberExpr *ME); + void VisitReturnStmt(ReturnStmt *RS); + void VisitStmt(Stmt *S); + void VisitUnaryDeref(UnaryOperator *UO); + void VisitUnaryOperator(UnaryOperator *UO); +}; +} // namespace + +void DefUse::VisitBinaryOperator(BinaryOperator *BO) { + auto Opcode = BO->getOpcode(); + if ((Opcode >= BO_Mul && Opcode <= BO_Shr) || + (Opcode >= BO_And && Opcode <= BO_LOr) || + (Opcode >= BO_LT && Opcode <= BO_NE)) { + Action = Use; + Visit(BO->getLHS()); + Visit(BO->getRHS()); + } else if (Opcode >= BO_MulAssign && Opcode <= BO_OrAssign) { + Action = Def; + Visit(BO->getLHS()); + Action = Use; + Visit(BO->getLHS()); + Visit(BO->getRHS()); + } +} + +void DefUse::VisitBinAssign(BinaryOperator *BO) { + Action = Def; + Visit(BO->getLHS()); + Action = Use; + Visit(BO->getRHS()); +} + +void DefUse::VisitCallExpr(CallExpr *CE) { + Action = Use; + for (Expr *E : CE->arguments()) { + Visit(E); + } +} + +void DefUse::VisitDeclRefExpr(DeclRefExpr *DRE) { + if (VarDecl *VD = dyn_cast(DRE->getDecl())) { + if (Action == Def) { + defs.push_back(VD); + } else if (Action == Use) { + uses.push_back(VD); + } + } +} + +void DefUse::VisitDeclStmt(DeclStmt *DS) { + for (Decl *D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + defs.push_back(VD); + if (VD->hasInit()) { + Action = Use; + Visit(VD->getInit()); + } + } + } +} + +void DefUse::VisitMemberExpr(MemberExpr *ME) { + /// When you have `p = ...`, which variable is reaasigned? + /// If `p` is `x`, then `x` is. Otherwise, nothing. + if (Action == Use) + Visit(ME->getBase()); +} + +void DefUse::VisitReturnStmt(ReturnStmt *RS) { + Action = Use; + if (Expr *RV = RS->getRetValue()) { + Visit(RV); + } +} + +void DefUse::VisitStmt(Stmt *S) { + for (auto *C : S->children()) { + if (C) + Visit(C); + } +} + +void DefUse::VisitUnaryDeref(UnaryOperator *UO) { + Action = Use; + Visit(UO->getSubExpr()); +} + +void DefUse::VisitUnaryOperator(UnaryOperator *UO) { + if (UO->isIncrementDecrementOp()) { + Action = Def; + Visit(UO->getSubExpr()); + Action = Use; + Visit(UO->getSubExpr()); + } else { + Visit(UO->getSubExpr()); + } +} + +namespace { +/// Given a statement, extract and generate actions. +/// +/// Due to the complexity of AST nodes, implementing the borrow checker becomes +/// challenging, so it is necessary to parse out the corresponding actions to +/// simplify the algorithm implementations. +/// +/// We classify each statement into six type of actions, with each statement +/// corresponding to one or more actions. +class ActionExtract : public clang::StmtVisitor { + RegionCheck &rc; + std::vector> actions; + std::vector> Sources; + std::unique_ptr Src = nullptr; + std::unique_ptr Dest = nullptr; + RegionName RNL; + RegionName RNR; + borrow::BorrowKind BK; + Action::ActionKind Kind = Action::Noop; + enum { LHS, RHS } op; + unsigned pathDepth = 0; + Decl *D = nullptr; + bool isArrow = false; + bool BuildOnGet = true; + + void Reset() { + Sources.clear(); + Src = nullptr; + Dest = nullptr; + RNL = RegionName(); + RNR = RegionName(); + Kind = Action::Noop; + op = LHS; + pathDepth = 0; + D = nullptr; + isArrow = false; + } + + void BuildAction() { + switch (Kind) { + case Action::Assign: { + assert(!RNL.isInvalid() && !RNR.isInvalid() && + "do not expect a invalid region name!"); + RegionName DerefRN = RegionName::Create(); + + std::vector> DerefSources = + ProcessDeref(Dest->ty, Sources[0]); + actions.emplace_back(std::make_unique( + std::move(Dest), RNL, RNR, std::move(Sources[0]), DerefRN, + std::move(DerefSources))); + break; + } + case Action::Borrow: { + assert(!RNL.isInvalid() && !RNR.isInvalid() && + "do not expect a invalid region name!"); + actions.emplace_back(std::make_unique( + std::move(Dest), RNL, RNR, BK, std::move(Sources[0]))); + break; + } + case Action::Init: { + if (IsTrackedType(Dest->ty)) { + GenerateImplicitAssign(); + actions.insert( + actions.begin(), + std::make_unique(std::move(Dest), std::move(Sources))); + } else { + std::vector> DerefSources; + for (const std::unique_ptr &Source : Sources) { + QualType QT = Source->ty; + std::vector> Derefs = ProcessDeref(QT, Source); + DerefSources.insert(DerefSources.end(), + std::make_move_iterator(Derefs.begin()), + std::make_move_iterator(Derefs.end())); + } + actions.emplace_back(std::make_unique( + std::move(Dest), std::move(Sources), std::move(DerefSources))); + } + break; + }; + case Action::StorageDead: { + actions.emplace_back( + std::make_unique(std::move(Dest))); + break; + } + case Action::Use: { + std::vector> DerefSources; + for (const std::unique_ptr &Source : Sources) { + QualType QT = Source->ty; + std::vector> Derefs = ProcessDeref(QT, Source); + DerefSources.insert(DerefSources.end(), + std::make_move_iterator(Derefs.begin()), + std::make_move_iterator(Derefs.end())); + } + actions.emplace_back(std::make_unique( + std::move(Sources), std::move(DerefSources))); + break; + } + default: { + actions.emplace_back(std::make_unique()); + break; + } + } + + Reset(); + } + + std::vector> + ProcessDeref(QualType QT, const std::unique_ptr &path) { + std::vector> Res; + if (QT.isBorrowQualified()) { + std::unique_ptr DerefSource = std::make_unique(*path); + DerefSource = std::make_unique(std::move(DerefSource), "*", + DerefSource->ty->getPointeeType(), + DerefSource->Location); + Res.emplace_back(std::move(DerefSource)); + } else if (QT->withBorrowFields()) { + const RecordType *RT = dyn_cast(QT.getCanonicalType()); + RecursiveForFields(RT, Res, path); + } + return Res; + } + + // Traverse the field of record type and create implicit dereferences. + void RecursiveForFields(const RecordType *RT, + std::vector> &Res, + const std::unique_ptr &Base) { + for (FieldDecl *FD : RT->getDecl()->fields()) { + std::unique_ptr Deref = std::make_unique(*Base); + Deref = std::make_unique(std::move(Deref), FD->getName().str(), + FD->getType(), Deref->Location); + if (FD->getType().isBorrowQualified()) { + Deref = std::make_unique(std::move(Deref), "*", + Deref->ty->getPointeeType(), + Deref->Location); + Res.emplace_back(std::move(Deref)); + } + if (FD->getType()->withBorrowFields()) { + const RecordType *rt = + dyn_cast(FD->getType().getCanonicalType()); + RecursiveForFields(rt, Res, Deref); + } + } + } + + void GenerateImplicitAssign() { + RegionName DerefRN = RegionName::Create(); + for (const std::unique_ptr &Source : Sources) { + if (IsTrackedType(Source->ty) && Source->D != nullptr) { + std::unique_ptr DestCopy = std::make_unique(*Dest); + std::unique_ptr SourceCopy = std::make_unique(*Source); + assert(Source->D != nullptr && "expected non nullptr"); + RegionName SourceRN = rc.getRegionName(Source->D); + assert(!SourceRN.isInvalid() && "expected valid region name"); + std::vector> DerefSources = + ProcessDeref(DestCopy->ty, Source); + actions.emplace_back(std::make_unique( + std::move(DestCopy), RNL, SourceRN, std::move(SourceCopy), DerefRN, + std::move(DerefSources))); + } + } + } + +public: + ActionExtract(Stmt *S, const VarDecl *VD, SourceLocation ScopeEndLoc, + RegionCheck &rc) + : rc(rc) { + /// For CFGScopeEnd, just create an `ActionStorageDead`. + if (VD) { + Kind = Action::StorageDead; + Dest = std::make_unique(VD->getName().str(), VD->getType(), + ScopeEndLoc); + } else { + Visit(S); + } + } + + ~ActionExtract() = default; + + std::vector> GetAction() { + if (BuildOnGet) + BuildAction(); + + return std::move(actions); + } + + void VisitArraySubscriptExpr(ArraySubscriptExpr *ASE); + void VisitBinaryOperator(BinaryOperator *BO); + void VisitBinAssign(BinaryOperator *BO); + void VisitCallExpr(CallExpr *CE); + void VisitCStyleCastExpr(CStyleCastExpr *CSCE); + void VisitDeclRefExpr(DeclRefExpr *DRE); + void VisitDeclStmt(DeclStmt *DS); + void VisitInitListExpr(InitListExpr *ILE); + void VisitMemberExpr(MemberExpr *ME); + void VisitReturnStmt(ReturnStmt *RS); + void VisitStmt(Stmt *S); + void VisitUnaryAddrConst(UnaryOperator *UO); + void VisitUnaryAddrConstDeref(UnaryOperator *UO); + void VisitUnaryAddrMut(UnaryOperator *UO); + void VisitUnaryAddrMutDeref(UnaryOperator *UO); + void VisitUnaryDeref(UnaryOperator *UO); + void VisitUnaryPostDec(UnaryOperator *UO); + void VisitUnaryPostInc(UnaryOperator *UO); + void VisitUnaryPreDec(UnaryOperator *UO); + void VisitUnaryPreInc(UnaryOperator *UO); +}; +} // namespace + +void ActionExtract::VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) { + Visit(ASE->getLHS()); +} + +void ActionExtract::VisitBinaryOperator(BinaryOperator *BO) { + auto Opcode = BO->getOpcode(); + if ((Opcode >= BO_Mul && Opcode <= BO_Shr) || + (Opcode >= BO_And && Opcode <= BO_LOr)) { + op = RHS; + Visit(BO->getLHS()); + Visit(BO->getRHS()); + } else if (Opcode >= BO_LT && Opcode <= BO_NE) { + Kind = Action::Use; + op = RHS; + Visit(BO->getLHS()); + Visit(BO->getRHS()); + } else if (Opcode >= BO_MulAssign && Opcode <= BO_OrAssign) { + Kind = Action::Init; + op = LHS; + Visit(BO->getLHS()); + op = RHS; + Visit(BO->getLHS()); + Visit(BO->getRHS()); + } +} + +void ActionExtract::VisitBinAssign(BinaryOperator *BO) { + if (BO->getType()->isStructureType() && IsTrackedType(BO->getType()) && + isa(BO->getRHS()->IgnoreImpCasts())) { + BuildOnGet = false; + op = LHS; + Visit(BO->getLHS()); + op = RHS; + Visit(BO->getRHS()); + } else { + Kind = Action::Assign; + op = LHS; + Visit(BO->getLHS()); + op = RHS; + Visit(BO->getRHS()); + if (RNL.isInvalid() || RNR.isInvalid() || Sources.empty()) + Kind = Action::Init; + } +} + +void ActionExtract::VisitCallExpr(CallExpr *CE) { + if (Kind == Action::Noop) + Kind = Action::Use; + else if (Dest != nullptr) + Kind = Action::Init; + op = RHS; + for (Expr *E : CE->arguments()) { + Visit(E); + } +} + +void ActionExtract::VisitCStyleCastExpr(CStyleCastExpr *CSCE) { + Visit(CSCE->getSubExpr()); + if (op == RHS) { + if (CSCE->getType().isBorrowQualified()) { + if (!Sources.empty() && Sources[0] != nullptr) { + RNR = rc.getRegionName(CSCE); + if (CSCE->getType().isConstBorrow()) { + BK = borrow::BorrowKind::Shared; + } else { + BK = borrow::BorrowKind::Mut; + } + Kind = Action::Borrow; + bool AddDeref = Sources[0]->ty != Dest->ty->getPointeeType(); + if (UnaryOperator *Sub = dyn_cast( + CSCE->getSubExpr()->IgnoreParenImpCasts())) { + if (Sub->getOpcode() == UO_AddrOf) { + // If the sub expr of a CStyleCastExpr is UO_AddrOf such as + // `(int* borrow)&x`, don't add dereference. + AddDeref = false; + } + } + if (AddDeref) { + std::unique_ptr Deref = std::make_unique( + std::move(Sources[0]), "*", CSCE->getType(), CSCE->getEndLoc()); + Sources[0] = std::move(Deref); + } + } + } + } +} + +void ActionExtract::VisitDeclRefExpr(DeclRefExpr *DRE) { + if (op == LHS) { + Dest = std::make_unique(DRE->getDecl()->getName().str(), + DRE->getType(), DRE->getLocation()); + if (isArrow) { + Dest = std::make_unique(std::move(Dest), "*", + DRE->getType()->getPointeeType(), + DRE->getLocation()); + } + if (IsTrackedType(DRE->getType())) { + RNL = rc.getRegionName(DRE->getDecl()); + } + } else if (op == RHS) { + Src = std::make_unique(DRE->getDecl()->getName().str(), + DRE->getType(), DRE->getLocation()); + // Decl of DeclRefExpr is for reborrow constraints. + if (DRE->getType().isBorrowQualified()) { + Src->setDecl(DRE->getDecl()); + } else if (IsTrackedType(DRE->getType())) { + Src->setDecl(DRE->getDecl()); + D = DRE->getDecl(); + } + if (Kind == Action::Assign && IsTrackedType(DRE->getType())) { + RNR = rc.getRegionName(DRE->getDecl()); + } + if (isArrow) { + Src = std::make_unique(std::move(Src), "*", + DRE->getType()->getPointeeType(), + DRE->getLocation()); + } + if (pathDepth == 0) + Sources.emplace_back(std::move(Src)); + } +} + +void ActionExtract::VisitDeclStmt(DeclStmt *DS) { + // Note: The construction of CFG ensures that there is only one VarDecl in + // the DeclStmt. + for (Decl *D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + if (VD->getType()->isStructureType() && IsTrackedType(VD->getType()) && + isa(VD->getInit())) { + BuildOnGet = false; + Dest = std::make_unique(VD->getName().str(), VD->getType(), + VD->getLocation()); + RNL = rc.getRegionName(VD); + Visit(VD->getInit()); + } else { + Kind = Action::Assign; + Dest = std::make_unique(VD->getName().str(), VD->getType(), + VD->getLocation()); + if (IsTrackedType(VD->getType())) + RNL = rc.getRegionName(VD); + if (VD->hasInit()) { + Sources.clear(); + op = RHS; + Visit(VD->getInit()); + } + // Handle cases like `int *borrow p = (int *borrow)NULL` and + // `int *borrow p = (int *borrow)q`. + if (RNL.isInvalid() || RNR.isInvalid() || Sources.empty()) + Kind = Action::Init; + } + } + } +} + +void ActionExtract::VisitInitListExpr(InitListExpr *ILE) { + if (IsTrackedType(ILE->getType())) { + Kind = Action::Noop; + RegionName RN = RNL; + RegionName CurRN; + std::unique_ptr Base = std::make_unique(*Dest); + unsigned Index = 0; + const RecordType *RT = + dyn_cast(ILE->getType().getCanonicalType()); + for (FieldDecl *Field : RT->getDecl()->fields()) { + Kind = Action::Assign; + std::unique_ptr FieldName = std::make_unique(*Base); + FieldName = + std::make_unique(std::move(FieldName), Field->getName().str(), + Field->getType(), ILE->getBeginLoc()); + Dest = std::move(FieldName); + if (IsTrackedType(Field->getType())) + CurRN = RN; + RNL = CurRN; + op = RHS; + Visit(ILE->getInit(Index)); + if (RNL.isInvalid() || RNR.isInvalid() || Sources.empty()) + Kind = Action::Init; + if (Dest) + BuildAction(); + ++Index; + } + } else { + for (Expr *Init : ILE->inits()) { + Visit(Init); + } + } +} + +void ActionExtract::VisitMemberExpr(MemberExpr *ME) { + if (op == LHS) { + bool oldIsArrow = isArrow; + isArrow = ME->isArrow(); + Visit(ME->getBase()); + std::unique_ptr Member = std::make_unique( + std::move(Dest), ME->getMemberNameInfo().getAsString(), ME->getType(), + ME->getMemberLoc()); + if (oldIsArrow) { + Member = std::make_unique(std::move(Member), "*", + ME->getType()->getPointeeType(), + ME->getMemberLoc()); + } + isArrow = oldIsArrow; + Dest = std::move(Member); + } else if (op == RHS) { + ++pathDepth; + bool oldIsArrow = isArrow; + isArrow = ME->isArrow(); + Visit(ME->getBase()); + std::unique_ptr Member = std::make_unique( + std::move(Src), ME->getMemberNameInfo().getAsString(), ME->getType(), + ME->getMemberLoc()); + if (ME->getType().isBorrowQualified()) { + Member->setDecl(D); + } + if (oldIsArrow) { + Member = std::make_unique(std::move(Member), "*", + ME->getType()->getPointeeType(), + ME->getMemberLoc()); + } + isArrow = oldIsArrow; + Src = std::move(Member); + --pathDepth; + if (pathDepth == 0) + Sources.emplace_back(std::move(Src)); + } +} + +void ActionExtract::VisitReturnStmt(ReturnStmt *RS) { + if (!RS->getRetValue()) { + return; + } + if (!IsTrackedType(RS->getRetValue()->getType())) { + Kind = Action::Use; + op = RHS; + Visit(RS->getRetValue()); + return; + } + Kind = Action::Assign; + Dest = std::make_unique("__ret", RS->getRetValue()->getType(), + RS->getReturnLoc()); + RNL = RegionName::CreateFree(); + op = RHS; + Visit(RS->getRetValue()); + if (RNR.isInvalid()) + Kind = Action::Init; +} + +void ActionExtract::VisitStmt(Stmt *S) { + for (auto *C : S->children()) { + if (C) + Visit(C); + } +} + +void ActionExtract::VisitUnaryAddrConst(UnaryOperator *UO) { + RNR = rc.getRegionName(UO); + BK = borrow::BorrowKind::Shared; + Kind = Action::Borrow; + Visit(UO->getSubExpr()); +} + +void ActionExtract::VisitUnaryAddrConstDeref(UnaryOperator *UO) { + RNR = rc.getRegionName(UO); + BK = borrow::BorrowKind::Shared; + Kind = Action::Borrow; + Visit(UO->getSubExpr()); + if (Sources[0]->ty->isPointerType()) { + std::unique_ptr Deref = std::make_unique( + std::move(Sources[0]), "*", UO->getType(), UO->getEndLoc()); + Sources[0] = std::move(Deref); + } +} + +void ActionExtract::VisitUnaryAddrMut(UnaryOperator *UO) { + RNR = rc.getRegionName(UO); + BK = borrow::BorrowKind::Mut; + Kind = Action::Borrow; + Visit(UO->getSubExpr()); +} + +void ActionExtract::VisitUnaryAddrMutDeref(UnaryOperator *UO) { + RNR = rc.getRegionName(UO); + BK = borrow::BorrowKind::Mut; + Kind = Action::Borrow; + Visit(UO->getSubExpr()); + // Note that in some cases such as `&mut *&p`, there is no need to add + // dereference, as it allows for more accurate analysis. + if (Sources[0]->ty->isPointerType()) { + std::unique_ptr Deref = std::make_unique( + std::move(Sources[0]), "*", UO->getType(), UO->getEndLoc()); + Sources[0] = std::move(Deref); + } +} + +void ActionExtract::VisitUnaryDeref(UnaryOperator *UO) { + if (Kind == Action::Noop) { + Kind = Action::Use; + op = RHS; + } + if (op == LHS) { + Visit(UO->getSubExpr()); + std::unique_ptr Deref = std::make_unique( + std::move(Dest), "*", UO->getType(), UO->getBeginLoc()); + Dest = std::move(Deref); + } else if (op == RHS) { + ++pathDepth; + Visit(UO->getSubExpr()); + bool DontAddDeref = false; + if (UnaryOperator *Sub = + dyn_cast(UO->getSubExpr()->IgnoreParenImpCasts())) { + if (Sub->getOpcode() == UO_AddrOf) { + DontAddDeref = true; + } + } + if (!DontAddDeref) { + std::unique_ptr Deref = std::make_unique( + std::move(Src), "*", UO->getType(), UO->getBeginLoc()); + Src = std::move(Deref); + } + --pathDepth; + if (pathDepth == 0) + Sources.emplace_back(std::move(Src)); + } +} + +void ActionExtract::VisitUnaryPostDec(UnaryOperator *UO) { + if (Kind == Action::Noop) { + Kind = Action::Init; + op = LHS; + Visit(UO->getSubExpr()); + op = RHS; + Visit(UO->getSubExpr()); + } else { + op = RHS; + Visit(UO->getSubExpr()); + } +} + +void ActionExtract::VisitUnaryPostInc(UnaryOperator *UO) { + if (Kind == Action::Noop) { + Kind = Action::Init; + op = LHS; + Visit(UO->getSubExpr()); + op = RHS; + Visit(UO->getSubExpr()); + } else { + op = RHS; + Visit(UO->getSubExpr()); + } +} + +void ActionExtract::VisitUnaryPreDec(UnaryOperator *UO) { + if (Kind == Action::Noop) { + Kind = Action::Init; + op = LHS; + Visit(UO->getSubExpr()); + op = RHS; + Visit(UO->getSubExpr()); + } else { + op = RHS; + Visit(UO->getSubExpr()); + } +} + +void ActionExtract::VisitUnaryPreInc(UnaryOperator *UO) { + if (Kind == Action::Noop) { + Kind = Action::Init; + op = LHS; + Visit(UO->getSubExpr()); + op = RHS; + Visit(UO->getSubExpr()); + } else { + op = RHS; + Visit(UO->getSubExpr()); + } +} + +//===----------------------------------------------------------------------===// +// Operations on RegionName +//===----------------------------------------------------------------------===// + +unsigned RegionName::Cnt = 0; + +//===----------------------------------------------------------------------===// +// Achors for Actions +//===----------------------------------------------------------------------===// + +void Action::anchor() {} +void ActionNoop::anchor() {} +void ActionInit::anchor() {} +void ActionBorrow::anchor() {} +void ActionAssign::anchor() {} +void ActionUse::anchor() {} +void ActionStorageDead::anchor() {} + +//===----------------------------------------------------------------------===// +// Query functions on Environment +//===----------------------------------------------------------------------===// + +/// Given a point, returns the set of all its successor points in the CFG. +llvm::SmallVector Environment::SuccessorPoints(Point point) const { + llvm::SmallVector Succs; + + const CFGBlock *block = *(cfg.nodes_begin() + point.blockID); + if (point.index != block->size()) { + Succs.push_back(Point(point.blockID, point.index + 1)); + } else { + llvm::DenseSet succs(block->succ_begin(), + block->succ_end()); + bool changed = true; + while (changed) { + changed = false; + size_t oldSize = succs.size(); + // Note: to avoid iterator invalidation + llvm::DenseSet newBlocks; + for (const CFGBlock *succ : succs) { + if (succ && succ->empty()) { + newBlocks.insert(succ->succ_begin(), succ->succ_end()); + } + } + succs.insert(newBlocks.begin(), newBlocks.end()); + if (succs.size() != oldSize) + changed = true; + } + for (const CFGBlock *succ : succs) { + if (succ && !succ->empty()) + Succs.push_back(Point(succ->BlockID, 1)); + } + } + + return Succs; +} + +//===----------------------------------------------------------------------===// +// Operation functions on InferenceContext and DFS +//===----------------------------------------------------------------------===// + +void InferenceContext::AddLivePoint(RegionVariable RV, Point P) { +#if DEBUG_PRINT + llvm::outs() << "AddLivePoint: " << RV.print() << " @ " << P.print() << '\n'; +#endif + VarDefinition &definition = definitions[RV.index]; + if (definition.value.AddPoint(P)) { + if (definition.capped) { + llvm_unreachable("Free region should not grow anymore!"); + } + } +} + +/// Inference algorithm, which is implemented based on fixed-point iteration. +/// +/// During fixed-point iteration, the algorithm solves inference constraints +/// and updates the regions of region variables. +void InferenceContext::Solve(const Environment &env) { + bool changed = true; + DFS dfs(env); + while (changed) { + changed = false; + + for (Constraint &constraint : constraints) { + const Region &Sup = definitions[constraint.sup.index].value; + VarDefinition &SubDef = definitions[constraint.sub.index]; +#if DEBUG_PRINT + llvm::outs() << "constraint: " << constraint.print(); + llvm::outs() << " sup (before): " << Sup.print() << '\n'; + llvm::outs() << " sub (before): " << SubDef.value.print() << '\n'; +#endif + + // DFS from the start point of constraint. + if (dfs.Copy(Sup, SubDef.value, constraint.point)) { + changed = true; + + if (SubDef.capped) { + llvm_unreachable("Free region should not grow anymore!"); + } + } + +#if DEBUG_PRINT + llvm::outs() << " sub (after) : " << SubDef.value.print() << '\n'; + llvm::outs() << " changed : " << (changed ? "true" : "false") + << '\n'; +#endif + } +#if DEBUG_PRINT + llvm::outs() << '\n'; +#endif + } +} + +/// Update `To` using `From`, starting DFS from `StartPoint` until the visited +/// is not in `From` or has already been visited. +bool DFS::Copy(const Region &From, Region &To, Point StartPoint) { + bool changed = false; + + stack.clear(); + visited.clear(); + + stack.push_back(StartPoint); + while (!stack.empty()) { + Point p = stack.back(); + stack.pop_back(); + +#if DEBUG_PRINT + llvm::outs() << " dfs: p=" << p.print() << '\n'; +#endif + + if (!From.MayContain(p)) { +#if DEBUG_PRINT + llvm::outs() << " not in From-Region\n"; +#endif + continue; + } + + if (!visited.insert(p).second) { +#if DEBUG_PRINT + llvm::outs() << " already visited\n"; +#endif + continue; + } + + changed |= To.AddPoint(p); + + llvm::SmallVector SuccessorPoints = env.SuccessorPoints(p); + if (SuccessorPoints.empty()) { + Point endPoint(Point::EndBlockID, Point::EndIndex); + if (From.MayContain(endPoint)) { + changed |= To.AddPoint(endPoint); + } + } else { + stack.insert(stack.end(), SuccessorPoints.begin(), SuccessorPoints.end()); + } + } + + return changed; +} + +//===----------------------------------------------------------------------===// +// Liveness computations +//===----------------------------------------------------------------------===// + +/// Iterates until a fixed point, computing live variables on the entry of each +/// basic block. +/// +/// Note that an empty callback is sufficient when computing liveness. +void Liveness::Compute() { + LivenessFact fact; + bool changed = true; + while (changed) { + changed = false; + + for (const CFGBlock *B : env.cfg.const_nodes()) { + SimulateBlock(fact, B, + [](auto _p, auto _a, auto _s, auto _v, auto _l) {}); + changed |= SetFrom(liveness[B], fact); + } + } +} + +template +void Liveness::SimulateBlock(LivenessFact &fact, const CFGBlock *Block, + CB callback) { + fact.clear(); + + // Everything live in a successor is live at the exit of the block. + for (auto succ : Block->succs()) { + if (succ) + SetFrom(fact, liveness[succ]); + } + + // Walk backwards through the actions. + for (CFGBlock::const_reverse_iterator it = Block->rbegin(), + ei = Block->rend(); + it != ei; ++it) { + const CFGElement &elem = *it; + const Stmt *S = nullptr; + const VarDecl *ScopeEndVD = nullptr; + SourceLocation ScopeEndLoc; + + if (elem.getAs()) { + S = elem.castAs().getStmt(); + // Get the def-use information of a given statement. + DefUse DU(const_cast(S)); + const llvm::SmallVector &defs = DU.getDefs(); + const llvm::SmallVector &uses = DU.getUses(); + + // Anything we write to is no longer live. + for (VarDecl *def : defs) { + Kill(fact, def); + } + + // Any variables we read from, we make live. + for (VarDecl *use : uses) { + Gen(fact, use); + } + } + + // There is no need to handle CFGScopeEnd when calculating liveness, while + // it's necessary to handle CFGScopeEnd when populating inference, so we + // need to get the `ScopeEndVD` and `ScopeEndLoc` here. + if (elem.getAs()) { + ScopeEndVD = elem.castAs().getVarDecl(); + ScopeEndLoc = elem.castAs().getTriggerStmt()->getEndLoc(); + } + + Point point(Block->getBlockID(), std::distance(it, ei)); + + callback(point, S, fact, ScopeEndVD, ScopeEndLoc); + } +} + +/// Invokes callback once for each statement with: +/// a. the point of the statement; +/// b. the statement itself; +/// c. the set of live variables on entry to the statement. +/// +/// Note that all constraints will be generated after the walk. +template void Liveness::Walk(CB callback) { + LivenessFact fact; + + for (const CFGBlock *B : env.cfg.const_nodes()) { + SimulateBlock(fact, B, callback); + } +} + +/// Given a LivenessFact, return the corresponding set of RegionName based on +/// the type of the variables. +/// +/// Only variables of borrowed qualified type or structure type with borrowed +/// qualifed field has corresponding RegionName. +std::set Liveness::LiveRegions(const LivenessFact &liveFact) { + std::set set; + for (VarDecl *VD : liveFact) { + if (IsTrackedType(VD->getType())) { + set.insert(rc.getRegionName(VD)); + } + } + return set; +} + +//===----------------------------------------------------------------------===// +// LoansInScope computations +//===----------------------------------------------------------------------===// + +LoansInScope::LoansInScope(const Environment &env, const RegionCheck &rc) + : env(env), rc(rc) { + // Collect the full set of loans, including explicit loans created with + // `&const` and `&mut` expressions, as well as implicit loans created when + // using variables of borrow qualified types. + for (const CFGBlock *Block : env.cfg.const_reverse_nodes()) { + for (CFGBlock::const_iterator it = Block->begin(), ei = Block->end(); + it != ei; ++it) { + Point point(Block->getBlockID(), std::distance(Block->begin(), it) + 1); + if (rc.getActionMap().find(point) != rc.getActionMap().end()) { + const auto &actions = rc.getActionMap().at(point); + for (const std::unique_ptr &action : actions) { + if (action->getKind() == Action::ActionKind::Borrow) { + const ActionBorrow *AB = dyn_cast(action.get()); + const Region ®ion = rc.getRegion(AB->RNR); + loans.push_back(Loan(point, AB->Source, AB->BK, region)); + } else if (action->getKind() == Action::ActionKind::Assign) { + const ActionAssign *AA = dyn_cast(action.get()); + const Region ®ion = rc.getRegion(AA->DerefRN); + for (const std::unique_ptr &DerefSource : AA->DerefSources) { + borrow::BorrowKind BK = DerefSource->base->ty.isConstBorrow() + ? BorrowKind::Shared + : BorrowKind::Mut; + loans.push_back(Loan(point, DerefSource, BK, region)); + } + } else if (action->getKind() == Action::ActionKind::Use) { + const ActionUse *AU = dyn_cast(action.get()); + const Region ®ion = rc.getEmptyRegion(); + for (const std::unique_ptr &DerefSource : AU->DerefSources) { + borrow::BorrowKind BK = DerefSource->base->ty.isConstBorrow() + ? BorrowKind::Shared + : BorrowKind::Mut; + loans.push_back(Loan(point, DerefSource, BK, region)); + } + } else if (action->getKind() == Action::ActionKind::Init) { + const ActionInit *AI = dyn_cast(action.get()); + const Region ®ion = rc.getEmptyRegion(); + for (const std::unique_ptr &DerefSource : AI->DerefSources) { + borrow::BorrowKind BK = DerefSource->base->ty.isConstBorrow() + ? BorrowKind::Shared + : BorrowKind::Mut; + loans.push_back(Loan(point, DerefSource, BK, region)); + } + } + } + } + } + } + +#if DEBUG_PRINT + llvm::outs() << "loans: [\n"; + for (const Loan &loan : loans) { + llvm::outs() << loan.print() << ",\n"; + } + llvm::outs() << "]\n"; +#endif + + // Make a convenient hash map for getting the index of a loan based on where + // it appears. + for (const auto &IndexedLoan : llvm::enumerate(loans)) { + loansByPoint[IndexedLoan.value().point].push_back(IndexedLoan.index()); + } + + // Iterates until a fixed point. + Compute(); +} + +llvm::SmallVector +LoansInScope::LoansKilledByWriteTo(const Path *path) const { + llvm::SmallVector loanIndexes; + + // When an assignment like `a.b.c = ...` occurs, we kill all + // the loans for `a.b.c` or some subpath like `a.b.c.d`, since + // the path no longer evaluates to the same thing. + for (const auto &IndexedLoan : llvm::enumerate(loans)) { + for (const Path *p : IndexedLoan.value().path->prefixes()) { + if (p->to_string() == path->to_string()) { + loanIndexes.push_back(IndexedLoan.index()); + } + } + } + + return loanIndexes; +} + +template +void LoansInScope::SimulateBlock(LoansFact &fact, const CFGBlock *Block, + CB callback) { + fact.clear(); + + // Everything live at end of a pred is live at the entry of the block. + for (auto pred : Block->preds()) { + SetFrom(fact, loansInScopeAfterBlock[pred]); + } + + // Walk forwards through the actions on by one. + for (CFGBlock::const_iterator it = Block->begin(), ei = Block->end(); + it != ei; ++it) { + const CFGElement &elem = *it; + const Stmt *S = nullptr; + + if (elem.getAs()) + S = elem.castAs().getStmt(); + + Point point(Block->getBlockID(), std::distance(Block->begin(), it) + 1); + + // kill any loans where `point` is not in their region + for (unsigned loanIndex : LoansNotInScopeAt(point)) { + Kill(fact, loanIndex); + } + + // callback at start of the action + callback(point, S, fact); + + // bring the loan into scope after the borrow + if (loansByPoint.find(point) != loansByPoint.end()) { + Gen(fact, loansByPoint[point]); + } + + // Figure out which path is overwritten by this action; this may cancel out + // some loans. + if (S) { + const auto &actions = rc.getActionMap().at(point); + for (const std::unique_ptr &action : actions) { + llvm::Optional overwritten = action->OverWrites(); + if (overwritten.hasValue()) { + const Path *overwrittenPath = overwritten.getValue(); + for (unsigned loanIndex : LoansKilledByWriteTo(overwrittenPath)) { + Kill(fact, loanIndex); + } + } + } + } + } +} + +/// Iterates until a fixed point, computing the loans in scope after each block +/// terminates. +void LoansInScope::Compute() { + LoansFact fact; + bool changed = true; + while (changed) { + changed = false; + + for (const CFGBlock *B : env.cfg.const_reverse_nodes()) { + SimulateBlock(fact, B, [](auto _p, auto _a, auto _s) {}); + changed |= SetFrom(loansInScopeAfterBlock[B], fact); + } + } +} + +/// Invoke `callback` with the loans in scope at each point. +template void LoansInScope::Walk(CB callback) { + llvm::SmallVector loans; + LoansFact fact; + + for (const CFGBlock *B : env.cfg.const_reverse_nodes()) { + SimulateBlock(fact, B, [&](Point point, const Stmt *S, LoansFact &fact) { + // Convert from the LoansFact into a vector of loans. + loans.clear(); + for (const auto &IndexedLoan : llvm::enumerate(this->loans)) { + if (fact.find(IndexedLoan.index()) != fact.end()) + loans.push_back(IndexedLoan.value()); + } + + // Invoke the callback, check actions at each point according to `loans`. + callback(point, S, loans); + }); + } +} + +//===----------------------------------------------------------------------===// +// Check functions on BorrowCheck +//===----------------------------------------------------------------------===// + +void BorrowCheck::CheckAction(const std::unique_ptr &action) { + IsBorrow = false; +#if DEBUG_PRINT + llvm::outs() << "CheckAction(" << action->print() << ") at " << point.print() + << '\n'; +#endif + switch (action->getKind()) { + case Action::Assign: { + const ActionAssign *AA = llvm::cast(action.get()); + CheckShallowWrite(AA->Dest); + CheckRead(AA->Source); + for (const std::unique_ptr &Deref : AA->DerefSources) { + if (Deref->base->ty.isConstBorrow()) { + CheckRead(Deref); + } else { + CheckMutBorrow(Deref); + } + } + break; + } + case Action::Borrow: { + const ActionBorrow *AB = llvm::cast(action.get()); + CheckShallowWrite(AB->Dest); + IsBorrow = true; + if (AB->BK == BorrowKind::Shared) { + CheckRead(AB->Source); + } else { + CheckMutBorrow(AB->Source); + } + break; + } + case Action::Init: { + const ActionInit *AI = llvm::cast(action.get()); + CheckShallowWrite(AI->Dest); + for (const std::unique_ptr &Source : AI->Sources) { + if (Source->ty.isOwnedQualified() || Source->ty->isMoveSemanticType()) { + CheckMove(Source); + } else { + CheckRead(Source); + } + } + for (const std::unique_ptr &Deref : AI->DerefSources) { + if (Deref->base->ty.isConstBorrow()) { + CheckRead(Deref); + } else { + CheckMutBorrow(Deref); + } + } + break; + } + case Action::StorageDead: { + const ActionStorageDead *ASD = llvm::cast(action.get()); + CheckStorageDead(ASD->Var); + break; + } + case Action::Use: { + const ActionUse *AU = llvm::cast(action.get()); + for (const std::unique_ptr &Use : AU->Uses) { + if (Use->ty.isOwnedQualified() || Use->ty->isMoveSemanticType()) { + CheckMove(Use); + } else { + CheckRead(Use); + } + } + for (const std::unique_ptr &Deref : AU->DerefSources) { + if (Deref->base->ty.isConstBorrow()) { + CheckRead(Deref); + } else { + CheckMutBorrow(Deref); + } + } + break; + } + default: + break; + } +} + +void BorrowCheck::CheckBorrows(Depth depth, Mode accessMode, + const std::unique_ptr &path) { + llvm::SmallVector loans; + switch (depth) { + case Depth::Shallow: + loans = FindLoansThatFreeze(path); + break; + case Depth::Deep: + loans = FindLoansThatIntersect(path); + break; + default: + break; + } + + for (const Loan *loan : loans) { + switch (accessMode) { + case Mode::Read: { + switch (loan->kind) { + case BorrowKind::Shared: + /* Ok */ + break; + case BorrowKind::Mut: { + if (IsBorrow) { + reporter.emitDiag(BorrowDiagKind::ForImmutWhenMut, path->Location, + path.get(), loan->path->Location); + } else { + reporter.emitDiag(BorrowDiagKind::ForRead, path->Location, path.get(), + loan->path->Location, loan->path.get()); + } + return; + } + default: + break; + } + break; + } + case Mode::Write: { + if (depth == Depth::Shallow) { + reporter.emitDiag(BorrowDiagKind::ForWrite, path->Location, path.get(), + loan->path->Location); + } else { + if (loan->kind == BorrowKind::Mut) { + reporter.emitDiag(BorrowDiagKind::ForMultiMut, path->Location, + path.get(), loan->path->Location); + } else { + reporter.emitDiag(BorrowDiagKind::ForMutWhenImmut, path->Location, + path.get(), loan->path->Location); + } + } + return; + } + default: + break; + } + } +} + +/// Cannot move from a path `p` if: +/// - `p` is borrowed; +/// - some subpath `p.foo` is borrowed; +/// - some prefix of `p` is borrowed. +/// +/// Note that it is stricter than both write and storage dead. In particular, +/// you can write to a variable `x` that contains an `&mut` value when `*x` is +/// borrowed, but you cannot move `x`. This is because moving it would make the +/// `&mut` variable in the new location, but writing (and storage dead) both +/// kill it forever. +void BorrowCheck::CheckMove(const std::unique_ptr &path) { + for (const Loan *loan : FindLoansThatIntersect(path)) { + reporter.emitDiag(BorrowDiagKind::ForMove, path->Location, path.get(), + loan->path->Location, loan->path.get()); + } +} + +/// `&mut x` may mutate `x`, but it can also *read* from `x`, and mutate things +/// reachable from `x`. +void BorrowCheck::CheckMutBorrow(const std::unique_ptr &path) { + CheckBorrows(Depth::Deep, Mode::Write, path); +} + +/// `use(x)` may access `x` and (by going through the produced value) anything +/// reachable from `x`. +void BorrowCheck::CheckRead(const std::unique_ptr &path) { + CheckBorrows(Depth::Deep, Mode::Read, path); +} + +/// `x = ...` overwrites `x` (without reading it) and prevents any further +/// reads from that path. +void BorrowCheck::CheckShallowWrite(const std::unique_ptr &path) { + CheckBorrows(Depth::Shallow, Mode::Write, path); +} + +/// Cannot free a local variable `var` if: +/// -data interior to `var` is borrowed. +/// +/// In particular, having something like `*var` borrowed is ok. +void BorrowCheck::CheckStorageDead(const std::unique_ptr &path) { + for (const Loan *loan : FindLoansThatFreeze(path)) { + reporter.emitDiag(BorrowDiagKind::ForStorageDead, path->Location, + loan->path.get(), loan->path->Location); + } +} + +/// Helper for `CheckWrite` and `CheckStorageDead`. +/// +/// Finds if there is a loan that "freezes" the given path -- that is, a +/// loan that would make modifying the `path` (or freeing it) illegal. +/// This is slightly more permissive than the rules around move and reads, +/// precisely because overwriting or freeing `path` makes the previous value +/// unavailable from that point on. +llvm::SmallVector +BorrowCheck::FindLoansThatFreeze(const std::unique_ptr &path) { + llvm::SmallVector loans; + llvm::SmallVector pathPrefixes = path->prefixes(); + for (const Loan &loan : this->loans) { + bool NeedToInsert = false; + llvm::SmallVector frozenPaths = FrozenByBorrowOf(loan.path); + // If you have borrowed `a.b`, this prevents writes to `a` or `a.b`: + for (const Path *frozen : frozenPaths) { + if (frozen->to_string() == path.get()->to_string()) { + NeedToInsert = true; + break; + } + } + // If you have borrowed `a.b`, this prevents writes to `a.b.c`: + for (const Path *prefix : pathPrefixes) { + if (prefix->to_string() == loan.path->to_string()) { + NeedToInsert = true; + break; + } + } + if (NeedToInsert) { + loans.push_back(&loan); + } + } + return loans; +} + +/// A loan L *intersects* a path P if either: +/// +/// - the loan is for the path P; or, +/// - the path P can be extended to reach the data in the loan; or, +/// - the loan path can be extended to reach the data in P. +/// +/// So, for example, if the path P is `a.b.c`, then: +/// +/// - a loan of `a.b.c` intersects P; +/// - a loan of `a.b.c.d` intersects P, because (e.g.) after reading P +/// you have also read `a.b.c.d`; +/// - a loan of `a.b` intersects P, because you can use the +/// reference to access the data at P. +llvm::SmallVector +BorrowCheck::FindLoansThatIntersect(const std::unique_ptr &path) { + llvm::SmallVector loans; + llvm::SmallVector pathPrefixes = path->prefixes(); + for (const Loan &loan : this->loans) { + bool NeedToInsert = false; + // Accessing `a.b.c` intersects a loan of `a.b.c`, `a.b` and `a`. + for (const Path *prefix : pathPrefixes) { + if (prefix->to_string() == loan.path->to_string()) { + NeedToInsert = true; + break; + } + } + /// Accessing `a.b.c` also intersects a loan of `a.b.c.d`. + for (const Path *prefix : loan.path->supportingPrefixes()) { + if (prefix->to_string() == path->to_string()) { + NeedToInsert = true; + break; + } + } + if (NeedToInsert) { + loans.push_back(&loan); + } + } + return loans; +} + +/// If `path` is mutably borrowed, returns a vector of paths which -- if +/// moved or if the storage went away -- would invalidate this +/// reference. +llvm::SmallVector +BorrowCheck::FrozenByBorrowOf(const std::unique_ptr &path) { + llvm::SmallVector paths; + const Path *curPath = path.get(); + while (true) { + paths.push_back(curPath); + switch (curPath->type) { + case Path::PathType::Var: { + return paths; + } + case Path::PathType::Extension: { + const Path *basePath = curPath->base.get(); + // If you borrowed `*r`, writing to `r` does not actually affect the + // memory at `*r`, so we can stop iterating backwards now. + if (basePath->ty->isPointerType() && !basePath->ty.isOwnedQualified()) { + return paths; + } + // If you have borrowed `a.b`, then writing to `a` would overwrite `a.b`, + // which is disallowed. Besides, for owned pointer `p`, if you have + // borrowed `*p`, then writing to `p` would overwrite `*p`, which is also + // disallowed. + curPath = basePath; + break; + } + default: + break; + } + } + return paths; +} + +//===----------------------------------------------------------------------===// +// Operation and query functions on RegionCheck +//===----------------------------------------------------------------------===// + +void RegionCheck::PopulateInference(Liveness &liveness) { + // Process free region for function parameters. + PreprocessForParamAndReturn(); + + // Walk statements to generate liveness constraints, subtyping constraints + // and reborrow constraints. + liveness.Walk([&, this](Point point, const Stmt *S, + llvm::DenseSet liveOnEntry, + const VarDecl *VD, + SourceLocation ScopeEndLoc) -> void { + // To start, find every variable `x` that is live. All regions in the type + // of `x` must include `point`. + std::set liveRegionsOnEntry = liveness.LiveRegions(liveOnEntry); + for (RegionName Name : liveRegionsOnEntry) { + RegionVariable RV = getRegionVariable(Name); + infer.AddLivePoint(RV, point); + } + + // Skip CFGScopeBegin nodes. + if (!S && !VD) + return; + + // Note: Since the last statement of the CFG basic block may be a + // borrow/reborrow statement, the set of successor points needs to be + // calculated here. + llvm::SmallVector SuccPoints = env.SuccessorPoints(point); + + // Next, walk the actions and establish any additional constraints that may + // arise from subtyping. + ActionExtract AE(const_cast(S), VD, ScopeEndLoc, *this); + actionMap[point] = std::move(AE.GetAction()); + const auto &actions = actionMap[point]; + for (const std::unique_ptr &action : actions) { +#if DEBUG_PRINT + llvm::outs() << point.print() << ": " << action->print() << '\n'; +#endif + switch (action->getKind()) { + case Action::Borrow: { + const ActionBorrow *AB = llvm::dyn_cast(action.get()); + for (Point SuccPoint : SuccPoints) { + RelateRegions(SuccPoint, AB->RNR, AB->RNL); + EnsureBorrowSource(SuccPoint, AB->RNR, AB->Source); + } + break; + } + case Action::Assign: { + const ActionAssign *AA = llvm::dyn_cast(action.get()); + // Note: manually add DerefRN to regionMap. + getRegionVariable(AA->DerefRN); + for (Point SuccPoint : SuccPoints) { + RelateRegions(SuccPoint, AA->DerefRN, AA->RNL); + RelateRegions(SuccPoint, AA->RNR, AA->DerefRN); + } + break; + } + default: + break; + } + } + }); +} + +void RegionCheck::Check() { + // Compute liveness. + Liveness liveness(env, *this); +#if DEBUG_PRINT + liveness.print(); +#endif + // Add inference constraints. + PopulateInference(liveness); + + // Solve inference constraints, reporting any errors. + infer.Solve(env); + + // Compute loans in scope at each point. + LoansInScope LIS(env, *this); + +#if DEBUG_PRINT + llvm::outs() << "========== BorrowCk ==========\n"; +#endif + // Run the borrow check, reporting any errors. + BorrowCk(env, *this, LIS); +} + +RegionName RegionCheck::getRegionName(Decl *D) { + if (declToRegionNameMap.find(D) != declToRegionNameMap.end()) + return declToRegionNameMap[D]; + RegionName RN = RegionName::Create(); +#if DEBUG_PRINT + llvm::outs() << "Decl: { " << cast(D)->getName() << " } => " + << RN.print() << '\n'; +#endif + declToRegionNameMap[D] = RN; + return RN; +} + +RegionName RegionCheck::getRegionName(Stmt *S) { + if (stmtToRegionNameMap.find(S) != stmtToRegionNameMap.end()) + return stmtToRegionNameMap[S]; + RegionName RN = RegionName::Create(); +#if DEBUG_PRINT + llvm::outs() << "Stmt: { " << S->getStmtClassName() << " } => " << RN.print() + << '\n'; +#endif + stmtToRegionNameMap[S] = RN; + return RN; +} + +/// Given a RegionName, returns the corresponding RegionVariable from regionMap. +/// If not existing, insert it into regionMap and return it. +RegionVariable RegionCheck::getRegionVariable(RegionName Name) { + if (regionMap.find(Name) != regionMap.end()) + return regionMap[Name]; + RegionVariable RV = infer.AddVar(Name); + regionMap[Name] = RV; + return RV; +} + +/// Given a RegionName, returns the corresponding Region from InferenceContext. +const Region &RegionCheck::getRegion(RegionName RN) const { + assert(regionMap.find(RN) != regionMap.end() && + ("no region variable ever created with name: " + RN.print()).c_str()); + RegionVariable RV = regionMap.at(RN); + return infer.getRegion(RV); +} + +/// EnsureBorrowSource - Add any relations between regions that are needed to +/// ensures that reborrows live long enough. +/// Specifically, if we borrow something like `*r` for `'a`, +/// where `r: &'b i32`, then `'b: 'a` is required. +void RegionCheck::EnsureBorrowSource(Point SuccPoint, + RegionName BorrowRegionName, + const std::unique_ptr &SourcePath) { +#if DEBUG_PRINT + llvm::outs() << "EnsureBorrowSource(" << SuccPoint.print() << ", " + << BorrowRegionName.print() << ", " << SourcePath->print() + << ")\n"; +#endif + for (const Path *prefix : SourcePath->supportingPrefixes()) { + switch (prefix->type) { + case Path::PathType::Var: { + return; + } + case Path::PathType::Extension: { + if (prefix->base->ty.isBorrowQualified()) { + assert(prefix->base->D != nullptr && "expected non nullptr!"); + RegionName RefRegionName = getRegionName(prefix->base->D); + RegionVariable BorrowRV = getRegionVariable(BorrowRegionName); + RegionVariable RefRV = getRegionVariable(RefRegionName); + infer.AddOutLives(BorrowRV, RefRV, SuccPoint); + } + break; + } + default: + break; + } + } +} + +void RegionCheck::RelateRegions(Point SuccPoint, RegionName Sub, + RegionName Sup) { + RegionVariable SubRV = getRegionVariable(Sub); + RegionVariable SupRV = getRegionVariable(Sup); +#if DEBUG_PRINT + llvm::outs() << "RelateRegions: " << SubRV.print() << " : " << SupRV.print() + << " @ " << SuccPoint.print() << '\n'; +#endif + infer.AddOutLives(SupRV, SubRV, SuccPoint); +} + +/// If the return type of a function is borrow qualifed or has borrow fields, +/// we relate a free region to function parameters. +void RegionCheck::PreprocessForParamAndReturn() { + if (!IsTrackedType(env.fd.getReturnType())) + return; + + RegionName ParamRN = RegionName::Create(); + for (ParmVarDecl *PVD : env.fd.parameters()) { + if (IsTrackedType(PVD->getType())) { +#if DEBUG_PRINT + llvm::outs() << "Decl: { " << cast(PVD)->getName() << " } => " + << ParamRN.print() << '\n'; +#endif + declToRegionNameMap[PVD] = ParamRN; + } + } + + RegionName FreeRN = RegionName::CreateFree(); + // This is sort of a hack, but for the free region `'region_r`, we will wind + // up with a region variable. We want that region variable to be inferred to + // precisely the set: `{G, End('region_r)}`, where `G` is all the points in + // the control-flow graph, and `End('region_r)` is the end-point of + // `'region_r`. We are enforcing (in inference) that `'region_r` doesn't get + // inferred to some larger region. + RegionVariable FreeRV = getRegionVariable(FreeRN); + for (const CFGBlock *block : env.cfg.const_nodes()) { + unsigned blockID = block->getBlockID(); + for (CFGBlock::const_iterator it = block->begin(), ei = block->end(); + it != ei; ++it) { + Point point(blockID, std::distance(block->begin(), it) + 1); + infer.AddLivePoint(FreeRV, point); + } + } + infer.AddLivePoint(FreeRV, Point(Point::EndBlockID, Point::EndIndex)); + infer.CapVar(FreeRV); +} + +/// Traverse each statement in the CFG, check the corresponding actions and +/// report erros according to the loans in scope information. +void clang::borrow::BorrowCk(const Environment &env, RegionCheck &rc, + LoansInScope &LIS) { + LIS.Walk([&](Point point, const Stmt *S, + const llvm::SmallVector &loans) -> void { + BorrowCheck borrowck(rc.getReporter(), env, point, loans); +#if DEBUG_PRINT + llvm::outs() << "Point: " << point.print() << '\n'; + llvm::outs() << "Loans: [\n"; + for (const Loan &loan : loans) { + llvm::outs() << loan.print() << ",\n"; + } + llvm::outs() << "]\n"; +#endif + if (rc.getActionMap().find(point) != rc.getActionMap().end()) { + const auto &actions = rc.getActionMap().at(point); + for (const std::unique_ptr &action : actions) { + borrowck.CheckAction(action); + } + } + }); +} + +// Entry point of borrow checker. +void clang::borrow::runBorrowChecker(const FunctionDecl &fd, const CFG &cfg, + ASTContext &Ctx, + BorrowDiagReporter &Reporter) { + RegionCheck RC(fd, cfg, Ctx, Reporter); + RC.Check(); +} + +#endif \ No newline at end of file diff --git a/clang/lib/Analysis/BSCNullabilityCheck.cpp b/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp similarity index 99% rename from clang/lib/Analysis/BSCNullabilityCheck.cpp rename to clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp index fac9bc2c1bfe9c322811e5716bbad4831ce7c16a..ebe781adcee201c0cfe37186ef2e1c6840d09c0c 100644 --- a/clang/lib/Analysis/BSCNullabilityCheck.cpp +++ b/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp @@ -12,7 +12,7 @@ #if ENABLE_BSC -#include "clang/Analysis/Analyses/BSCNullabilityCheck.h" +#include "clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" diff --git a/clang/lib/Analysis/BSCOwnership.cpp b/clang/lib/Analysis/BSC/BSCOwnership.cpp similarity index 99% rename from clang/lib/Analysis/BSCOwnership.cpp rename to clang/lib/Analysis/BSC/BSCOwnership.cpp index f240816e9502c3bc40ff11fec10abf7fb609bf00..749359e9256a24cf88503a3816ad0efa75a8bd78 100644 --- a/clang/lib/Analysis/BSCOwnership.cpp +++ b/clang/lib/Analysis/BSC/BSCOwnership.cpp @@ -12,7 +12,7 @@ #if ENABLE_BSC -#include "clang/Analysis/Analyses/BSCOwnership.h" +#include "clang/Analysis/Analyses/BSC/BSCOwnership.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 10d20507ef50b93f678a9002820661205e4af536..844fa6eaacd0ac61f13abcd8e3e4d70067f351e2 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -1518,6 +1518,16 @@ std::unique_ptr CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { if (const CXXDestructorDecl *DD = dyn_cast_or_null(D)) addImplicitDtorsForDestructor(DD); +#if ENABLE_BSC + if (BuildOpts.BSCBorrowCk) { + if (const FunctionDecl *FD = dyn_cast(D)) { + for (ParmVarDecl *PVD : FD->parameters()) { + addLocalScopeForVarDecl(PVD); + } + } + } +#endif + // Visit the statements and create the CFG. CFGBlock *B = addStmt(Statement); @@ -2582,7 +2592,12 @@ CFGBlock *CFGBuilder::VisitBinaryOperator(BinaryOperator *B, if (B->getOpcode() == BO_Comma) { // , autoCreateBlock(); +#if ENABLE_BSC + if (!BuildOpts.BSCBorrowCk) + appendStmt(Block, B); +#else appendStmt(Block, B); +#endif addStmt(B->getRHS()); return addStmt(B->getLHS()); } @@ -2991,7 +3006,11 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) { // statement-expression. CFGBlock *LastBlock = Block; +#if ENABLE_BSC + if (Init && !BuildOpts.BSCBorrowCk) { +#else if (Init) { +#endif if (HasTemporaries) { // For expression with temporaries go directly to subexpression to omit // generating destructors for the second time. @@ -3198,8 +3217,15 @@ CFGBlock *CFGBuilder::VisitReturnStmt(Stmt *S) { // Visit children if (ReturnStmt *RS = dyn_cast(S)) { +#if ENABLE_BSC + if (!BuildOpts.BSCBorrowCk) { + if (Expr *O = RS->getRetValue()) + return Visit(O, AddStmtChoice::AlwaysAdd, /*ExternallyDestructed=*/true); + } +#else if (Expr *O = RS->getRetValue()) return Visit(O, AddStmtChoice::AlwaysAdd, /*ExternallyDestructed=*/true); +#endif return Block; } @@ -4297,7 +4323,11 @@ CFGBlock *CFGBuilder::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E, /// VisitStmtExpr - Utility method to handle (nested) statement /// expressions (a GCC extension). CFGBlock *CFGBuilder::VisitStmtExpr(StmtExpr *SE, AddStmtChoice asc) { +#if ENABLE_BSC + if (!BuildOpts.BSCBorrowCk && asc.alwaysAdd(*this, SE)) { +#else if (asc.alwaysAdd(*this, SE)) { +#endif autoCreateBlock(); appendStmt(Block, SE); } @@ -4883,6 +4913,12 @@ CFGBlock *CFGBuilder::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *C, CFGBlock *CFGBuilder::VisitImplicitCastExpr(ImplicitCastExpr *E, AddStmtChoice asc) { +#if ENABLE_BSC + if (BuildOpts.BSCBorrowCk) { + if (!asc.alwaysAdd(*this, E)) + return Block; + } +#endif if (asc.alwaysAdd(*this, E)) { autoCreateBlock(); appendStmt(Block, E); diff --git a/clang/lib/Analysis/CMakeLists.txt b/clang/lib/Analysis/CMakeLists.txt index 2695d570073142165d8c35c61e1240ea83a46884..f0c0a46c2750d1de2127d39cc1048e3a990ba00c 100644 --- a/clang/lib/Analysis/CMakeLists.txt +++ b/clang/lib/Analysis/CMakeLists.txt @@ -6,9 +6,10 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangAnalysis AnalysisDeclContext.cpp BodyFarm.cpp - BSCNullabilityCheck.cpp - BSCOwnership.cpp - BSCBorrowCheck.cpp + BSC/BSCBorrowCheck.cpp + BSC/BSCBorrowChecker.cpp + BSC/BSCNullabilityCheck.cpp + BSC/BSCOwnership.cpp CalledOnceCheck.cpp CFG.cpp CFGReachabilityAnalysis.cpp diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp index 1d4c0cdb21d67edfbfb5c5d597d4ae1986760572..39f3e6d26acedd1522195104b84471f90c786bd2 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -364,9 +364,13 @@ void DiagnosticsEngine::setSeverity(diag::kind Diag, diag::Severity Map, SourceLocation L) { assert(Diag < diag::DIAG_UPPER_LIMIT && "Can only map builtin diagnostics"); +#if ENABLE_BSC + // BSC allows modification of error log levels +#else assert((Diags->isBuiltinWarningOrExtension(Diag) || (Map == diag::Severity::Fatal || Map == diag::Severity::Error)) && "Cannot map errors into warnings!"); +#endif assert((L.isInvalid() || SourceMgr) && "No SourceMgr for valid location"); // Don't allow a mapping to a warning override an error/fatal mapping. diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index 8e2593b103d1569ac370f81a436bf913cd3d097a..36a31107fbedab9b076e1414a1ce3498eb2144a5 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -134,6 +134,16 @@ struct StaticDiagInfoRec { } diag::Flavor getFlavor() const { +#if ENABLE_BSC + switch (Class) { + case CLASS_ERROR: + return diag::Flavor::Error; + case CLASS_REMARK: + return diag::Flavor::Remark; + default: + return diag::Flavor::WarningOrError; + } +#endif return Class == CLASS_REMARK ? diag::Flavor::Remark : diag::Flavor::WarningOrError; } diff --git a/clang/lib/Basic/Warnings.cpp b/clang/lib/Basic/Warnings.cpp index cc8c138233ca1d9fcc4ef04840b87fb2efb94655..ffd38eda0856f78596aa64d9cfb56203626e72be 100644 --- a/clang/lib/Basic/Warnings.cpp +++ b/clang/lib/Basic/Warnings.cpp @@ -35,8 +35,20 @@ static void EmitUnknownDiagWarning(DiagnosticsEngine &Diags, diag::Flavor Flavor, StringRef Prefix, StringRef Opt) { StringRef Suggestion = DiagnosticIDs::getNearestOption(Flavor, Opt); - Diags.Report(diag::warn_unknown_diag_option) - << (Flavor == diag::Flavor::WarningOrError ? 0 : 1) + Diags.Report( +#if ENABLE_BSC + diag::warn_unknown_bsc_diag_option +#else + diag::warn_unknown_diag_option +#endif + ) + << ( +#if ENABLE_BSC + Flavor == diag::Flavor::Error + ? 2 + : +#endif + Flavor == diag::Flavor::WarningOrError ? 0 : 1) << (Prefix.str() += std::string(Opt)) << !Suggestion.empty() << (Prefix.str() += std::string(Suggestion)); } @@ -198,6 +210,34 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, } } +#if ENABLE_BSC + for (unsigned i = 0, e = Opts.Errors.size(); i != e; ++i) { + const auto Flavor = diag::Flavor::Error; + StringRef Opt = Opts.Errors[i]; + + // Check to see if this error starts with "no-", if so, this is a + // negative form of the option. + bool isPositive = true; + if (Opt.startswith("no-")) { + isPositive = false; + Opt = Opt.substr(3); + } + + // Figure out how this option affects the error. If -Efoo, map the + // diagnostic to a error, if -Eno-foo, map it to ignore. + diag::Severity Mapping = + isPositive ? diag::Severity::Error : diag::Severity::Ignored; + + if (Report) { + if (DiagIDs->getDiagnosticsInGroup(Flavor, Opt, _Diags)) + EmitUnknownDiagWarning(Diags, Flavor, isPositive ? "-E" : "-Eno-", + Opt); + } else { + Diags.setSeverityForGroup(Flavor, Opt, Mapping); + } + } +#endif + for (unsigned i = 0, e = Opts.Remarks.size(); i != e; ++i) { StringRef Opt = Opts.Remarks[i]; const auto Flavor = diag::Flavor::Remark; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index f32da6d90e6514095f24b1246818e9a4a2d4431c..8bca3ce34aae10672ba2a456991668acaf6ef905 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5746,6 +5746,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, claimNoWarnArgs(Args); Args.AddAllArgs(CmdArgs, options::OPT_R_Group); +#if ENABLE_BSC + Args.AddAllArgs(CmdArgs, options::OPT_E_Group); +#endif for (const Arg *A : Args.filtered(options::OPT_W_Group, options::OPT__SLASH_wd)) { diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 68aaf9cc262809904d61a698c17d56427e512aca..55b0018cacbe5d2a0d6de2b29e6087fcc4b47271 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2324,6 +2324,12 @@ void CompilerInvocation::GenerateDiagnosticArgs( Args.push_back(SA(StringRef("-W") + Warning)); } +#if ENABLE_BSC + for (const auto &Error : Opts.Errors) { + Args.push_back(SA(StringRef("-E") + Error)); + } +#endif + for (const auto &Remark : Opts.Remarks) { // These arguments are generated from OptimizationRemark fields of // CodeGenOptions. @@ -2409,6 +2415,9 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, addDiagnosticArgs(Args, OPT_W_Group, OPT_W_value_Group, Opts.Warnings); addDiagnosticArgs(Args, OPT_R_Group, OPT_R_value_Group, Opts.Remarks); +#if ENABLE_BSC + addDiagnosticArgs(Args, OPT_E_Group, OPT_E_value_Group, Opts.Errors); +#endif return Diags->getNumErrors() == NumErrorsBefore; } diff --git a/clang/lib/Frontend/TextDiagnosticPrinter.cpp b/clang/lib/Frontend/TextDiagnosticPrinter.cpp index 0ff5376098ffe8ddb0f6f48e7ed906ed4ca002eb..444b71ec5d991fdd73439299e6ba6b4f0aede89c 100644 --- a/clang/lib/Frontend/TextDiagnosticPrinter.cpp +++ b/clang/lib/Frontend/TextDiagnosticPrinter.cpp @@ -78,12 +78,18 @@ static void printDiagnosticOptions(raw_ostream &OS, StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); if (!Opt.empty()) { - OS << (Started ? "," : " [") - << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt; - StringRef OptValue = Info.getDiags()->getFlagValue(); - if (!OptValue.empty()) - OS << "=" << OptValue; - Started = true; +#if ENABLE_BSC + if (!DiagnosticIDs::isDefaultMappingAsError(Info.getID())) { +#endif + OS << (Started ? "," : " [") + << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt; + StringRef OptValue = Info.getDiags()->getFlagValue(); + if (!OptValue.empty()) + OS << "=" << OptValue; + Started = true; +#if ENABLE_BSC + } +#endif } } diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index fb4f2dc457581fd04df303783339f5f3839803bc..38238ee3529aca14df872b6d69a7a4edac0c6b78 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -1285,13 +1285,21 @@ public: } if (WarningName.size() < 3 || WarningName[0] != '-' || - (WarningName[1] != 'W' && WarningName[1] != 'R')) { + (WarningName[1] != 'W' && WarningName[1] != 'R' +#if ENABLE_BSC + && WarningName[1] != 'E' +#endif + )) { PP.Diag(StringLoc, diag::warn_pragma_diagnostic_invalid_option); return; } - diag::Flavor Flavor = WarningName[1] == 'W' ? diag::Flavor::WarningOrError - : diag::Flavor::Remark; + diag::Flavor Flavor = WarningName[1] == 'W' + ? diag::Flavor::WarningOrError +#if ENABLE_BSC + : WarningName[1] == 'E' ? diag::Flavor::Error +#endif + : diag::Flavor::Remark; StringRef Group = StringRef(WarningName).substr(2); bool unknownDiag = false; if (Group == "everything") { diff --git a/clang/lib/Parse/BSC/ParseStmtBSC.cpp b/clang/lib/Parse/BSC/ParseStmtBSC.cpp index 659ea1cfd22778d468cbbefa8442a080e5c86c9f..7e729c8cd160e3b3ac159896c7b15833f18af65a 100644 --- a/clang/lib/Parse/BSC/ParseStmtBSC.cpp +++ b/clang/lib/Parse/BSC/ParseStmtBSC.cpp @@ -22,15 +22,6 @@ void Parser::CheckStmtTokInSafeZone(tok::TokenKind Kind) { return; } switch (Kind) { - case tok::identifier: { - Token Next = NextToken(); - if (Next.is(tok::colon)) - Diag(Tok, diag::err_unsafe_action) << "label statement"; - break; - } - case tok::kw_goto: - Diag(Tok, diag::err_unsafe_action) << "goto statement"; - break; case tok::kw_asm: Diag(Tok, diag::err_unsafe_action) << "asm statement"; break; diff --git a/clang/lib/Sema/BSC/SemaDeclBSC.cpp b/clang/lib/Sema/BSC/SemaDeclBSC.cpp index 3dc080a812eeb1bd6edada784e69c499d0e81119..5ff1856a0c8086ef3d852ae4564385868b4bd89d 100644 --- a/clang/lib/Sema/BSC/SemaDeclBSC.cpp +++ b/clang/lib/Sema/BSC/SemaDeclBSC.cpp @@ -13,13 +13,13 @@ #if ENABLE_BSC +#include "TreeTransform.h" #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/Analyses/BSC/BSCBorrowChecker.h" +#include "clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h" +#include "clang/Analysis/Analyses/BSC/BSCOwnership.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Sema/Sema.h" -#include "clang/Sema/SemaInternal.h" using namespace clang; using namespace sema; @@ -159,7 +159,7 @@ bool Sema::HasSafeZoneInFunction(const FunctionDecl* FnDecl) { /// | | | | /// FuncDecl--> CFG --> | NullabilityCheck | --> | OwnershipAnalysis | --> /// |__________________| |___________________| -/// __________________ +/// __________________ /// | | /// --> | BorrowCheck | --> FuncDecl --> CodeGen /// |__________________| @@ -185,17 +185,17 @@ void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, bool RequireBorrowCheck = LangOpts.BSC ? FindSafeFeatures(FD) : false; // nullability-check happens in mode: {SafeOnly, All}. // For SafeOnly, do not build cfg when there is no SafeZone in Function. - bool RequireNullabilityCheck = EnableNullabilityCheck; + bool RequireNullabilityCheck = EnableNullabilityCheck; if (getLangOpts().getNullabilityCheck() == LangOptions::NC_SAFE) { if(HasSafeZoneInFunction(FD)) { - RequireNullabilityCheck = EnableNullabilityCheck; + RequireNullabilityCheck = EnableNullabilityCheck; } else { RequireNullabilityCheck = false; } } bool RequireCFGAnalysis = RequireNullabilityCheck || RequireBorrowCheck; if (RequireCFGAnalysis && FD && AC.getCFG()) { // Only build the CFG when needed. - // Step one: Run NullabilityCheck + // Step one: Run NullabilityCheck unsigned NumNullabilityCheckErrorsInCurrFD = 0; if (RequireNullabilityCheck) { NullabilityCheckDiagReporter NullabilityCheckReporter(*this); @@ -213,13 +213,772 @@ void Sema::BSCDataflowAnalysis(const Decl *D, bool EnableOwnershipCheck, OwnershipDiagReporter OwnershipReporter(*this); runOwnershipAnalysis(*FD, *AC.getCFG(), AC, OwnershipReporter, Context); OwnershipReporter.flushDiagnostics(); - // Step three: Run borrow check when there is no other ownership errors in - // current function. + // Step three: Run borrow checker when there is no other ownership errors and + // nullability in current function. if (!OwnershipReporter.getNumErrors()) { - BorrowCheckDiagReporter BorrowCheckReporter(*this); - runBorrowCheck(*FD, *AC.getCFG(), BorrowCheckReporter, Context); + BSCBorrowChecker(const_cast(FD)); } } } } + +struct ReplaceNodesMap { + /// When we replace an AST node `p` with an AST node `q`, we use `q` as value + /// and use `p` as key and insert into the map. + /// When we execute BorrowCheckerEpilogue, when we find the key of an AST + /// node, we replace it with the corresponding value. + llvm::DenseMap replacedExprsMap; + llvm::DenseMap replacedStmtsMap; + + bool Contains(Expr *E) const { + return replacedExprsMap.find(E) != replacedExprsMap.end(); + } + + bool Contains(Stmt *S) const { + return replacedStmtsMap.find(S) != replacedStmtsMap.end(); + } + + void Insert(Expr *Key, Expr *Value) { replacedExprsMap[Key] = Value; } + + void Insert(Stmt *Key, Stmt *Value) { replacedStmtsMap[Key] = Value; } + + Expr *Get(Expr *Key) { return replacedExprsMap[Key]; } + + Stmt *Get(Stmt *Key) { return replacedStmtsMap[Key]; } +}; + +/// Before running borrow checker, introduce some temporary variables to adjust +/// FunctionDecl in the AST, replacing nested function calls and complex +/// expressions. +/// +/// The complexity for AST nodes presents significant challenges for +/// implementing the borrow checker. For example, scenarios like `foo(bar())` +/// are not convenient for analysis. To handle such cases, we use a temporary +/// variable to store the return value of `bar()` before passing it as an +/// argument to `foo()`, ensuring a semantically equivalent transformation. +class BorrowCheckerPrologue : public TreeTransform { + typedef TreeTransform BaseTransform; + + FunctionDecl *FD; + llvm::SmallVector Stmts; + bool NeedToReplace = false; // A flag indicating whether to create a temporary + // variable to replace the current CallExpr. + unsigned TempVarCounter = 0; + + ReplaceNodesMap &replacedNodesMap; + + // Replace function call expression or unary operator expression with a + // temporary variable, and return the corresponding DeclRefExpr. + DeclRefExpr *ReplaceWithTemporaryVariable(Expr *E) { + std::string Name = "_borrowck_tmp_" + std::to_string(TempVarCounter++); + VarDecl *VD = VarDecl::Create( + getSema().Context, FD, SourceLocation(), SourceLocation(), + &getSema().Context.Idents.get(Name), E->getType(), nullptr, SC_None); + VD->setInit(E); + DeclStmt *DS = new (getSema().Context) + DeclStmt(DeclGroupRef(VD), SourceLocation(), SourceLocation()); + Stmts.push_back(DS); + return DeclRefExpr::Create(getSema().Context, NestedNameSpecifierLoc(), + SourceLocation(), VD, false, E->getBeginLoc(), + E->getType(), VK_LValue); + } + + CompoundStmt *GetOrWrapWithCompoundStmt(Stmt *S) { + if (isa(S)) + return dyn_cast(S); + return CompoundStmt::Create(SemaRef.Context, S, FPOptionsOverride(), + S->getBeginLoc(), S->getEndLoc()); + } + +public: + BorrowCheckerPrologue(Sema &SemaRef, FunctionDecl *FD, + ReplaceNodesMap &replacedNodesMap) + : BaseTransform(SemaRef), FD(FD), replacedNodesMap(replacedNodesMap) {} + + // Don't redo semantic analysis to ensure that AST nodes are not rebuilt to + // affect destructor insertion. + bool AlwaysRebuild() { return false; } + + void applyTransform() { + StmtResult Res = BaseTransform::TransformStmt(FD->getBody()); + FD->setBody(Res.get()); + } + + StmtResult TransformCaseStmt(CaseStmt *CS) { + CS->setLHS(getDerived().TransformExpr(CS->getLHS()).get()); + if (Expr *RHS = CS->getRHS()) + CS->setRHS(getDerived().TransformExpr(RHS).get()); + if (Stmt *Sub = CS->getSubStmt()) { + CompoundStmt *SubBody = GetOrWrapWithCompoundStmt(Sub); + StmtResult ResSub = getDerived().TransformStmt(SubBody); + CS->setSubStmt(ResSub.get()); + replacedNodesMap.Insert(ResSub.get(), Sub); + } + return CS; + } + + StmtResult TransformCompoundStmt(CompoundStmt *CS) { + return TransformCompoundStmt(CS, false); + } + + // Transform each stmt in the CompoundStmt. + StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr) { + if (CS == nullptr) + return CS; + + llvm::SmallVector PrevStmts = Stmts; + llvm::SmallVector CurStmts; + Stmts = CurStmts; + // Traverse and transform all statements in the compound statement. + for (Stmt *S : CS->body()) { + StmtResult Res = BaseTransform::TransformStmt(S); + Stmts.push_back(Res.getAs()); + } + CompoundStmt *NewCS = CompoundStmt::Create( + SemaRef.Context, Stmts, FPOptionsOverride(), CS->getLBracLoc(), + CS->getRBracLoc(), CS->getSafeSpecifier(), CS->getSafeSpecifierLoc(), + CS->getCompSafeZoneSpecifier()); + Stmts = PrevStmts; + replacedNodesMap.Insert(NewCS, CS); + + return NewCS; + } + + StmtResult TransformDeclStmt(DeclStmt *DS) { + for (Decl *D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + if (VD->hasInit()) { + getDerived().TransformExpr(VD->getInit()); + } + } + } + + return DS; + } + + StmtResult TransformDefaultStmt(DefaultStmt *DS) { + if (Stmt *Sub = DS->getSubStmt()) { + CompoundStmt *SubBody = GetOrWrapWithCompoundStmt(Sub); + StmtResult ResSub = getDerived().TransformStmt(SubBody); + DS->setSubStmt(ResSub.get()); + replacedNodesMap.Insert(ResSub.get(), Sub); + } + return DS; + } + + StmtResult TransformDoStmt(DoStmt *DS) { + Stmt *Body = DS->getBody(); + CompoundStmt *CSBody = GetOrWrapWithCompoundStmt(Body); + StmtResult ResBody = getDerived().TransformStmt(CSBody); + DS->setBody(ResBody.get()); + replacedNodesMap.Insert(ResBody.get(), Body); + DS->setCond(getDerived().TransformExpr(DS->getCond()).get()); + + return DS; + } + + StmtResult TransformForStmt(ForStmt *FS) { + FS->setInit(getDerived().TransformStmt(FS->getInit()).get()); + FS->setCond(getDerived().TransformExpr(FS->getCond()).get()); + FS->setInc(getDerived().TransformExpr(FS->getInc()).get()); + Stmt *Body = FS->getBody(); + CompoundStmt *CSBody = GetOrWrapWithCompoundStmt(Body); + StmtResult ResBody = getDerived().TransformStmt(CSBody); + FS->setBody(ResBody.get()); + replacedNodesMap.Insert(ResBody.get(), Body); + + return FS; + } + + StmtResult TransformIfStmt(IfStmt *IS) { + Expr *Cond = IS->getCond(); + ExprResult ResCond = getDerived().TransformExpr(Cond); + IS->setCond(ResCond.get()); + + Stmt *Then = IS->getThen(); + CompoundStmt *CSThen = GetOrWrapWithCompoundStmt(Then); + StmtResult ResThen = getDerived().TransformStmt(CSThen); + IS->setThen(ResThen.get()); + replacedNodesMap.Insert(ResThen.get(), Then); + + if (Stmt *Else = IS->getElse()) { + CompoundStmt *CSElse = GetOrWrapWithCompoundStmt(Else); + StmtResult ResElse = getDerived().TransformStmt(CSElse); + IS->setElse(ResElse.get()); + replacedNodesMap.Insert(ResElse.get(), Else); + } + + return IS; + } + + StmtResult TransformReturnStmt(ReturnStmt *RS) { + if (!RS->getRetValue()) + return RS; + + bool Old = NeedToReplace; + NeedToReplace = true; + ExprResult Res = getDerived().TransformExpr(RS->getRetValue()); + Expr *E = Res.get(); + if (CallExpr *CE = dyn_cast_or_null(E)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(CE); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + ImplicitCastExpr *ICE = + ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, DRE, + nullptr, VK_PRValue, FPOptionsOverride()); + replacedNodesMap.Insert(ICE, CE); + RS->setRetValue(ICE); + return RS; + } + if (UnaryOperator *UO = dyn_cast(E)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(UO); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + ImplicitCastExpr *ICE = + ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, DRE, + nullptr, VK_PRValue, FPOptionsOverride()); + replacedNodesMap.Insert(ICE, UO); + RS->setRetValue(ICE); + return RS; + } + RS->setRetValue(E); + NeedToReplace = Old; + return RS; + } + + StmtResult TransformSwitchStmt(SwitchStmt *SS) { + SS->setCond(getDerived().TransformExpr(SS->getCond()).get()); + SS->setBody(getDerived().TransformStmt(SS->getBody()).get()); + return SS; + } + + StmtResult TransformWhileStmt(WhileStmt *WS) { + WS->setCond(getDerived().TransformExpr(WS->getCond()).get()); + Stmt *Body = WS->getBody(); + CompoundStmt *CSBody = GetOrWrapWithCompoundStmt(Body); + StmtResult ResBody = getDerived().TransformStmt(CSBody); + WS->setBody(ResBody.get()); + replacedNodesMap.Insert(ResBody.get(), Body); + + return WS; + } + + ExprResult TransformBinaryOperator(BinaryOperator *BO) { + bool Old = NeedToReplace; + NeedToReplace = true; + ExprResult ResLHS = getDerived().TransformExpr(BO->getLHS()); + Expr *ELHS = ResLHS.get(); + if (CallExpr *CE = dyn_cast(ELHS)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(CE); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + ELHS = + ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, DRE, + nullptr, VK_PRValue, FPOptionsOverride()); + replacedNodesMap.Insert(ELHS, CE); + } + BO->setLHS(ELHS); + NeedToReplace = Old; + ExprResult ResRHS = getDerived().TransformExpr(BO->getRHS()); + Expr *ERHS = ResRHS.get(); + if (BO->getOpcode() < BO_Assign) { + if (CallExpr *CE = dyn_cast(ERHS)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(CE); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + ERHS = ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, + DRE, nullptr, VK_PRValue, + FPOptionsOverride()); + replacedNodesMap.Insert(ERHS, CE); + } + } + BO->setRHS(ERHS); + return BO; + } + + ExprResult TransformCallExpr(CallExpr *CE) { + bool Old = NeedToReplace; + for (unsigned i = 0; i < CE->getNumArgs(); ++i) { + Expr *Arg = CE->getArg(i); + NeedToReplace = true; + ExprResult Res = getDerived().TransformExpr(Arg); + Expr *E = Res.get(); + + if (CallExpr *NestedCall = dyn_cast(E)) { + E = ReplaceWithTemporaryVariable(NestedCall); + CastKind Kind = + E->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, E->getType(), Kind, E, + nullptr, VK_PRValue, FPOptionsOverride()); + replacedNodesMap.Insert(E, NestedCall); + } else if (UnaryOperator *UO = dyn_cast(E)) { + if (UO->getOpcode() >= UO_AddrMut && + UO->getOpcode() <= UO_AddrConstDeref) { + E = ReplaceWithTemporaryVariable(UO); + CastKind Kind = + E->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, E->getType(), Kind, E, + nullptr, VK_PRValue, + FPOptionsOverride()); + replacedNodesMap.Insert(E, UO); + } + } + CE->setArg(i, E); + } + NeedToReplace = Old; + return CE; + } + + ExprResult TransformCompoundLiteralExpr(CompoundLiteralExpr *CLE) { + ExprResult Res = getDerived().TransformExpr(CLE->getInitializer()); + Expr *E = Res.get(); + CLE->setInitializer(E); + return CLE; + } + + ExprResult TransformCStyleCastExpr(CStyleCastExpr *CSCE) { + ExprResult Res = getDerived().TransformExpr(CSCE->getSubExpr()); + Expr *E = Res.get(); + if (CallExpr *CE = dyn_cast(E)) { + if (NeedToReplace) { + E = ReplaceWithTemporaryVariable(CE); + replacedNodesMap.Insert(E, CE); + } + } else if (UnaryOperator *UO = dyn_cast(E)) { + if (NeedToReplace) { + E = ReplaceWithTemporaryVariable(UO); + replacedNodesMap.Insert(E, UO); + } + } + CSCE->setSubExpr(E); + return CSCE; + } + + ExprResult TransformImplicitCastExpr(ImplicitCastExpr *ICE) { + ExprResult Res = getDerived().TransformExpr(ICE->getSubExpr()); + Expr *E = Res.get(); + if (CallExpr *CE = dyn_cast(E)) { + if (NeedToReplace) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(CE); + replacedNodesMap.Insert(DRE, CE); + ICE->setSubExpr(DRE); + return ICE; + } + } + ICE->setSubExpr(E); + return ICE; + } + + ExprResult TransformInitListExpr(InitListExpr *ILE) { + for (unsigned i = 0; i < ILE->getNumInits(); ++i) { + ExprResult Res = getDerived().TransformExpr(ILE->getInit(i)); + Expr *E = Res.get(); + if (CallExpr *CE = dyn_cast(E)) { + E = ReplaceWithTemporaryVariable(CE); + replacedNodesMap.Insert(E, CE); + } else if (UnaryOperator *UO = dyn_cast(E)) { + if (UO->getOpcode() >= UO_AddrMut && + UO->getOpcode() <= UO_AddrConstDeref) { + E = ReplaceWithTemporaryVariable(UO); + CastKind Kind = + E->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + ImplicitCastExpr *ICE = ImplicitCastExpr::Create( + getSema().Context, E->getType(), Kind, E, nullptr, VK_PRValue, + FPOptionsOverride()); + E = ICE; + replacedNodesMap.Insert(E, UO); + } + } + ILE->setInit(i, E); + } + return ILE; + } + + ExprResult TransformMemberExpr(MemberExpr *ME) { + ExprResult Res = getDerived().TransformExpr(ME->getBase()); + Expr *E = Res.get(); + if (CallExpr *CE = dyn_cast(E)) { + E = ReplaceWithTemporaryVariable(CE); + CastKind Kind = + E->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, E->getType(), Kind, E, + nullptr, VK_PRValue, FPOptionsOverride()); + replacedNodesMap.Insert(E, CE); + } + ME->setBase(E); + return ME; + } + + ExprResult TransformParenExpr(ParenExpr *PE) { + ExprResult Res = getDerived().TransformExpr(PE->getSubExpr()); + Expr *E = Res.get(); + if (CallExpr *CE = dyn_cast(E)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(E); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, + DRE, nullptr, VK_PRValue, + FPOptionsOverride()); + replacedNodesMap.Insert(E, CE); + } else if (BinaryOperator *BO = dyn_cast(E)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(E); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, + DRE, nullptr, VK_PRValue, + FPOptionsOverride()); + replacedNodesMap.Insert(E, BO); + } + PE->setSubExpr(E); + + return PE; + } + + ExprResult TransformStmtExpr(StmtExpr *SE) { + StmtResult Res = getDerived().TransformStmt(SE->getSubStmt()); + SE->setSubStmt(Res.getAs()); + return SE; + } + + ExprResult TransformUnaryOperator(UnaryOperator *UO) { + ExprResult Res = getDerived().TransformExpr(UO->getSubExpr()); + Expr *E = Res.get(); + if (UO->getOpcode() == UO_Deref) { + if (CallExpr *CE = dyn_cast(E)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(E); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, + DRE, nullptr, VK_PRValue, + FPOptionsOverride()); + replacedNodesMap.Insert(E, CE); + } else if (BinaryOperator *BO = dyn_cast(E)) { + DeclRefExpr *DRE = ReplaceWithTemporaryVariable(E); + CastKind Kind = + DRE->getType()->getAsCXXRecordDecl() ? CK_NoOp : CK_LValueToRValue; + E = ImplicitCastExpr::Create(getSema().Context, DRE->getType(), Kind, + DRE, nullptr, VK_PRValue, + FPOptionsOverride()); + replacedNodesMap.Insert(E, BO); + } + } + UO->setSubExpr(E); + return UO; + } +}; + +/// After running borrow checker, restore the AST to its original form to avoid +/// any impact on other compiler phases caused by AST transformations. +class BorrowCheckerEpilogue : public TreeTransform { + typedef TreeTransform BaseTransform; + + FunctionDecl *FD; + ReplaceNodesMap &replacedNodesMap; + +public: + BorrowCheckerEpilogue(Sema &SemaRef, FunctionDecl *FD, + ReplaceNodesMap &replacedNodesMap) + : BaseTransform(SemaRef), FD(FD), replacedNodesMap(replacedNodesMap) {} + + // Don't redo semantic analysis to ensure that AST nodes are not rebuilt to + // affect destructor insertion. + bool AlwaysRebuild() { return false; } + + void applyTransform() { + StmtResult Res = BaseTransform::TransformStmt(FD->getBody()); + FD->setBody(Res.get()); + } + + StmtResult TransformCaseStmt(CaseStmt *CS) { + CS->setLHS(getDerived().TransformExpr(CS->getLHS()).get()); + if (Expr *RHS = CS->getRHS()) + CS->setRHS(getDerived().TransformExpr(RHS).get()); + if (Stmt *Sub = CS->getSubStmt()) { + if (replacedNodesMap.Contains(Sub)) { + Sub = replacedNodesMap.Get(Sub); + } + StmtResult ResSub = getDerived().TransformStmt(Sub); + CS->setSubStmt(ResSub.get()); + } + return CS; + } + + StmtResult TransformCompoundStmt(CompoundStmt *CS) { + return TransformCompoundStmt(CS, false); + } + + // Transform each stmt in the CompoundStmt. + StmtResult TransformCompoundStmt(CompoundStmt *CS, bool IsStmtExpr) { + if (CS == nullptr) + return CS; + + if (replacedNodesMap.Contains(CS)) { + CS = cast(replacedNodesMap.Get(CS)); + } + + // Traverse and transform all statements in the compound statement. + for (Stmt *S : CS->body()) { + BaseTransform::TransformStmt(S); + } + + return CS; + } + + StmtResult TransformDeclStmt(DeclStmt *DS) { + for (Decl *D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + if (VD->hasInit()) { + getDerived().TransformExpr(VD->getInit()); + } + } + } + + return DS; + } + + StmtResult TransformDefaultStmt(DefaultStmt *DS) { + if (Stmt *Sub = DS->getSubStmt()) { + if (replacedNodesMap.Contains(Sub)) { + Sub = replacedNodesMap.Get(Sub); + } + StmtResult ResSub = getDerived().TransformStmt(Sub); + DS->setSubStmt(ResSub.get()); + } + return DS; + } + + StmtResult TransformDoStmt(DoStmt *DS) { + Stmt *Body = DS->getBody(); + if (replacedNodesMap.Contains(Body)) { + Body = replacedNodesMap.Get(Body); + } + StmtResult ResBody = getDerived().TransformStmt(Body); + DS->setBody(ResBody.get()); + DS->setCond(getDerived().TransformExpr(DS->getCond()).get()); + + return DS; + } + + StmtResult TransformForStmt(ForStmt *FS) { + FS->setInit(getDerived().TransformStmt(FS->getInit()).get()); + FS->setCond(getDerived().TransformExpr(FS->getCond()).get()); + FS->setInc(getDerived().TransformExpr(FS->getInc()).get()); + Stmt *Body = FS->getBody(); + if (replacedNodesMap.Contains(Body)) { + Body = replacedNodesMap.Get(Body); + } + StmtResult ResBody = getDerived().TransformStmt(Body); + FS->setBody(ResBody.get()); + + return FS; + } + + StmtResult TransformIfStmt(IfStmt *IS) { + Expr *Cond = IS->getCond(); + ExprResult ResCond = getDerived().TransformExpr(Cond); + IS->setCond(ResCond.get()); + + Stmt *Then = IS->getThen(); + if (replacedNodesMap.Contains(Then)) { + Then = replacedNodesMap.Get(Then); + } + StmtResult ResThen = getDerived().TransformStmt(Then); + IS->setThen(ResThen.get()); + + if (Stmt *Else = IS->getElse()) { + if (replacedNodesMap.Contains(Else)) { + Else = replacedNodesMap.Get(Else); + } + StmtResult ResElse = getDerived().TransformStmt(Else); + IS->setElse(ResElse.get()); + } + + return IS; + } + + StmtResult TransformReturnStmt(ReturnStmt *RS) { + if (!RS->getRetValue()) + return RS; + + if (replacedNodesMap.Contains(RS->getRetValue())) { + RS->setRetValue(replacedNodesMap.Get(RS->getRetValue())); + } + + ExprResult Res = getDerived().TransformExpr(RS->getRetValue()); + Expr *E = Res.get(); + + RS->setRetValue(E); + return RS; + } + + StmtResult TransformSwitchStmt(SwitchStmt *SS) { + SS->setCond(getDerived().TransformExpr(SS->getCond()).get()); + SS->setBody(getDerived().TransformStmt(SS->getBody()).get()); + return SS; + } + + StmtResult TransformWhileStmt(WhileStmt *WS) { + WS->setCond(getDerived().TransformExpr(WS->getCond()).get()); + Stmt *Body = WS->getBody(); + if (replacedNodesMap.Contains(Body)) { + Body = replacedNodesMap.Get(Body); + } + StmtResult Res = getDerived().TransformStmt(Body); + WS->setBody(Res.get()); + + return WS; + } + + ExprResult TransformBinaryOperator(BinaryOperator *BO) { + Expr *LHS = BO->getLHS(); + if (replacedNodesMap.Contains(LHS)) { + LHS = replacedNodesMap.Get(LHS); + } + ExprResult ResLHS = getDerived().TransformExpr(LHS); + BO->setLHS(ResLHS.get()); + Expr *RHS = BO->getRHS(); + if (replacedNodesMap.Contains(RHS)) { + RHS = replacedNodesMap.Get(RHS); + } + ExprResult ResRHS = getDerived().TransformExpr(RHS); + BO->setRHS(ResRHS.get()); + return BO; + } + + ExprResult TransformCallExpr(CallExpr *CE) { + for (unsigned i = 0; i < CE->getNumArgs(); ++i) { + Expr *Arg = CE->getArg(i); + if (replacedNodesMap.Contains(Arg)) { + Arg = replacedNodesMap.Get(Arg); + } + ExprResult Res = getDerived().TransformExpr(Arg); + Expr *E = Res.get(); + + CE->setArg(i, E); + } + return CE; + } + + ExprResult TransformCompoundLiteralExpr(CompoundLiteralExpr *CLE) { + ExprResult Res = getDerived().TransformExpr(CLE->getInitializer()); + Expr *E = Res.get(); + CLE->setInitializer(E); + return CLE; + } + + ExprResult TransformCStyleCastExpr(CStyleCastExpr *CSCE) { + Expr *Sub = CSCE->getSubExpr(); + if (replacedNodesMap.Contains(Sub)) { + Sub = replacedNodesMap.Get(Sub); + } + ExprResult Res = getDerived().TransformExpr(Sub); + CSCE->setSubExpr(Res.get()); + + return CSCE; + } + + ExprResult TransformImplicitCastExpr(ImplicitCastExpr *ICE) { + Expr *Sub = ICE->getSubExpr(); + if (replacedNodesMap.Contains(Sub)) { + Sub = replacedNodesMap.Get(Sub); + } + ExprResult Res = getDerived().TransformExpr(Sub); + ICE->setSubExpr(Res.get()); + + return ICE; + } + + ExprResult TransformInitListExpr(InitListExpr *ILE) { + for (unsigned i = 0; i < ILE->getNumInits(); ++i) { + Expr *Init = ILE->getInit(i); + if (replacedNodesMap.Contains(Init)) { + Init = replacedNodesMap.Get(Init); + } + ExprResult Res = getDerived().TransformExpr(Init); + ILE->setInit(i, Res.get()); + } + + return ILE; + } + + ExprResult TransformMemberExpr(MemberExpr *ME) { + Expr *Base = ME->getBase(); + if (replacedNodesMap.Contains(Base)) { + Base = replacedNodesMap.Get(Base); + } + ExprResult Res = getDerived().TransformExpr(Base); + ME->setBase(Res.get()); + + return ME; + } + + ExprResult TransformParenExpr(ParenExpr *PE) { + Expr *Sub = PE->getSubExpr(); + if (replacedNodesMap.Contains(Sub)) { + Sub = replacedNodesMap.Get(Sub); + } + ExprResult Res = getDerived().TransformExpr(Sub); + PE->setSubExpr(Res.get()); + + return PE; + } + + ExprResult TransformStmtExpr(StmtExpr *SE) { + StmtResult Res = getDerived().TransformStmt(SE->getSubStmt()); + SE->setSubStmt(Res.getAs()); + return SE; + } + + ExprResult TransformUnaryOperator(UnaryOperator *UO) { + Expr *Sub = UO->getSubExpr(); + if (UO->getOpcode() == UO_Deref) { + if (replacedNodesMap.Contains(Sub)) { + Sub = replacedNodesMap.Get(Sub); + } + } + ExprResult Res = getDerived().TransformExpr(Sub); + UO->setSubExpr(Res.get()); + + return UO; + } +}; + +void Sema::BSCBorrowChecker(FunctionDecl *FD) { + ReplaceNodesMap replacedNodesMap; + BorrowCheckerPrologue BCP(*this, FD, replacedNodesMap); + BCP.applyTransform(); + + AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, FD); + 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().BSCMode = true; + AC.getCFGBuildOptions().BSCBorrowCk = true; + AC.getCFGBuildOptions() + .setAlwaysAdd(Stmt::BinaryOperatorClass) + .setAlwaysAdd(Stmt::BreakStmtClass) + .setAlwaysAdd(Stmt::CompoundStmtClass) + .setAlwaysAdd(Stmt::DeclStmtClass) + .setAlwaysAdd(Stmt::DoStmtClass) + .setAlwaysAdd(Stmt::ForStmtClass) + .setAlwaysAdd(Stmt::IfStmtClass) + .setAlwaysAdd(Stmt::ReturnStmtClass) + .setAlwaysAdd(Stmt::SwitchStmtClass) + .setAlwaysAdd(Stmt::WhileStmtClass); + + if (AC.getCFG()) { +#if DEBUG_PRINT + AC.getCFG()->dump(LangOpts, true); +#endif + borrow::BorrowDiagReporter Reporter(*this); + borrow::runBorrowChecker(*FD, *AC.getCFG(), Context, Reporter); + } + + BorrowCheckerEpilogue BCE(*this, FD, replacedNodesMap); + BCE.applyTransform(); +} + #endif \ No newline at end of file diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 7f55f3a83696149e7b67d262747d5e5ae4921889..1c6a912bfebc9206f8a329b9aa99c71e0fc539be 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -2211,27 +2211,27 @@ Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP, if (LangOpts.OpenMP) popOpenMPFunctionRegion(Scope.get()); -#if ENABLE_BSC - // FIXME: Rename EnableOwnershipCheck, maybe EnableBorrowCheck is a better name. - bool EnableOwnershipCheck = LangOpts.BSC ? (!(getLangOpts().DisableOwnershipCheck)) : false; - // If NullabilityCheck is in Mode: {SafeOnly, All}, we should build CFG. - bool EnableNullabilityCheck = LangOpts.BSC ? (getLangOpts().getNullabilityCheck() != LangOptions::NC_NONE) : false; - if (EnableOwnershipCheck || EnableNullabilityCheck) { - if (!isBSCCoroutine && - (getDiagnostics().getNumErrors() == - 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()) - BSCDataflowAnalysis(D, EnableOwnershipCheck, - EnableNullabilityCheck); - } - } -#endif +// #if ENABLE_BSC +// // FIXME: Rename EnableOwnershipCheck, maybe EnableBorrowCheck is a better name. +// bool EnableOwnershipCheck = LangOpts.BSC ? (!(getLangOpts().DisableOwnershipCheck)) : false; +// // If NullabilityCheck is in Mode: {SafeOnly, All}, we should build CFG. +// bool EnableNullabilityCheck = LangOpts.BSC ? (getLangOpts().getNullabilityCheck() != LangOptions::NC_NONE) : false; +// if (EnableOwnershipCheck || EnableNullabilityCheck) { +// if (!isBSCCoroutine && +// (getDiagnostics().getNumErrors() == +// 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()) +// BSCDataflowAnalysis(D, EnableOwnershipCheck, +// EnableNullabilityCheck); +// } +// } +// #endif // Issue any analysis-based warnings. if (WP && D) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 27347cea4684378753184da53418728f39aa403b..02aaf1fdf5ed61d54de5766b4104ceea3093db3f 100755 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15990,8 +15990,38 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, PopDeclContext(); PopFunctionScopeInfo(ActivePolicy, dcl); - if (auto *FD = dyn_cast(dcl)) - DesugarDestructorCall(FD); + +#if ENABLE_BSC + if (LangOpts.BSC) { + if (auto FD = dyn_cast_or_null(dcl)) { + // FIXME: Rename EnableOwnershipCheck, maybe EnableBorrowCheck is a better name. + bool EnableOwnershipCheck = !getLangOpts().DisableOwnershipCheck; + // If NullabilityCheck is in Mode: {SafeOnly, All}, we should build CFG. + bool EnableNullabilityCheck = getLangOpts().getNullabilityCheck() != LangOptions::NC_NONE; + if (EnableOwnershipCheck || EnableNullabilityCheck) { + if (getDiagnostics().getNumErrors() == + getDiagnostics().getNumOwnershipErrors() + + getDiagnostics().getNumBorrowCheckErrors() + + getDiagnostics().getNumNullabilityCheckErrors()) { + bool DoAnalysis = true; + // Skip function template and class template + if (const auto *RD = dyn_cast(FD->getParent())) { + if (RD->getDescribedClassTemplate() != nullptr) + DoAnalysis = false; + } + auto md = dyn_cast_or_null(FD); + // Don't check ownership rules of destructor parameters + if (DoAnalysis && (!md || !md->isDestructor())) + BSCDataflowAnalysis(FD, EnableOwnershipCheck, + EnableNullabilityCheck); + } + } + // Desugar BSC Function. + DesugarDestructorCall(FD); + } + } +#endif + // If any errors have occurred, clear out any temporaries that may have // been leftover. This ensures that these temporaries won't be picked up for // deletion in some later function. diff --git a/clang/test/BSC/Negative/Generic/StructAndFunction/redefinition_a_owned_struct/redefinition_a_owned_struct_1.cbs b/clang/test/BSC/Negative/Generic/StructAndFunction/redefinition_a_owned_struct/redefinition_a_owned_struct_1.cbs index f2db73bc627aedf3e972e045e482b0fb58db0a2a..6d318d82673d412083172197e80cf01357f3617e 100644 --- a/clang/test/BSC/Negative/Generic/StructAndFunction/redefinition_a_owned_struct/redefinition_a_owned_struct_1.cbs +++ b/clang/test/BSC/Negative/Generic/StructAndFunction/redefinition_a_owned_struct/redefinition_a_owned_struct_1.cbs @@ -5,7 +5,7 @@ // RUN: -I %S/../../../../../../../build/lib/clang/15.0.4/include/bsc_include/ \ // RUN: -internal-isystem %S/../../../../../../../lib/Headers/bsc_include \ // RUN: -internal-externc-isystem /usr/include/x86_64-linux-gnu \ -// RUN: -internal-externc-isystem /include -internal-externc-isystem /usr/include -verify +// RUN: -internal-externc-isystem /include -internal-externc-isystem /usr/include -verify #include"vec.hbs" diff --git a/clang/test/BSC/Negative/NullabilityCheck/if_stmt.cbs b/clang/test/BSC/Negative/NullabilityCheck/if_stmt.cbs index 7b38218ff2d6c08b890d67da5a2bb3633ea45778..229e9e52f5d5a448e761fe9a5f78a3f8c161570e 100644 --- a/clang/test/BSC/Negative/NullabilityCheck/if_stmt.cbs +++ b/clang/test/BSC/Negative/NullabilityCheck/if_stmt.cbs @@ -246,7 +246,7 @@ safe void test17(void) { if (p != nullptr) { p->a = 10; } else { - p->a = 20; // expected-error {{cannot access member througn nullable pointer}} + p->a = 20; // expected-error {{cannot access member through nullable pointer}} } - p->a = 30; // expected-error {{cannot access member througn nullable pointer}} + p->a = 30; // expected-error {{cannot access member through nullable pointer}} } \ 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 index b06640b6fe37c7cddd14147c8beb8fd4b4cf2221..5f6b80e48f591349991a7617abfee4beb2b8b367 100644 --- a/clang/test/BSC/Negative/NullabilityCheck/nested_struct.cbs +++ b/clang/test/BSC/Negative/NullabilityCheck/nested_struct.cbs @@ -10,8 +10,8 @@ struct N { 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}} + if (n1.b->a != nullptr) // expected-error {{cannot access member through nullable pointer}} + *n1.b->a = 10; // expected-error {{cannot access member through nullable pointer}} struct M m = { .a = nullptr }; struct N n2 = { .b = &mut m }; if (n2.b->a != nullptr) @@ -26,7 +26,7 @@ safe void test2(void) { 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}} + if (n1.b->a != nullptr && n1.b != nullptr) // expected-error {{cannot access member through nullable pointer}} *n1.b->a = 10; } @@ -43,16 +43,16 @@ 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}} + if (p->b->a != nullptr) // expected-error {{cannot access member through nullable pointer}} + *p->b->a = 10; // expected-error {{cannot access member through 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}} + if (p->b != nullptr && p->b->a != nullptr) // expected-error {{cannot access member through nullable pointer}} expected-error {{cannot access member through nullable pointer}} + *p->b->a = 10; // expected-error {{cannot access member through nullable pointer}} safe_free(p); } diff --git a/clang/test/BSC/Negative/NullabilityCheck/while_stmt.cbs b/clang/test/BSC/Negative/NullabilityCheck/while_stmt.cbs index 14b930f10504ae3d944f6c410c5c2acaeaaf2663..448b7b8d20b4c9e4762fb7c49d067e817351f352 100644 --- a/clang/test/BSC/Negative/NullabilityCheck/while_stmt.cbs +++ b/clang/test/BSC/Negative/NullabilityCheck/while_stmt.cbs @@ -79,5 +79,5 @@ safe void test7(void) { p->a = 10; p = get_random_ptr_S(&mut a); } - p->a = 20; // expected-error {{cannot access member througn nullable pointer}} + p->a = 20; // expected-error {{cannot access member through 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 index 1ba70d31d74db6153b8d3110427802f2ccac4c96..09f9c74ec0e12368135c6af82243f85e7138d545 100644 --- a/clang/test/BSC/Negative/NullabilityCheck/while_stmt_struct.cbs +++ b/clang/test/BSC/Negative/NullabilityCheck/while_stmt_struct.cbs @@ -8,7 +8,8 @@ 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 = get_random_ptr(&mut a); // expected-error {{cannot borrow `a` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} } *s.p = 20; } @@ -41,8 +42,10 @@ safe void test4(void) { 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 = get_random_ptr(&mut a); // expected-error {{cannot borrow `a` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} + s2.p = get_random_ptr(&mut b); // expected-error {{cannot borrow `b` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} } *s1.p = 20; *s2.p = 20; diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/assign_to_borrowed_with_many_loops/assign_to_borrowed_with_many_loops.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/assign_to_borrowed_with_many_loops/assign_to_borrowed_with_many_loops.cbs new file mode 100644 index 0000000000000000000000000000000000000000..9abba063af89508bc16e67386fc964890cdf1e20 --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/assign_to_borrowed_with_many_loops/assign_to_borrowed_with_many_loops.cbs @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -verify %s + +void test() { + int a = 1; + if (a == 1) { a = 1; } + if (a == 2) { a = 2; } + if (a == 3) { a = 3; } + if (a == 4) { a = 4; } + if (a == 5) { a = 5; } + if (a == 6) { a = 6; } + if (a == 7) { a = 7; } + if (a == 8) { a = 8; } + if (a == 9) { a = 9; } + if (a == 10) { a = 10; } + if (a == 11) { a = 11; } + if (a == 12) { a = 12; } + if (a == 13) { a = 13; } + if (a == 14) { a = 14; } + if (a == 15) { a = 15; } + if (a == 16) { a = 16; } + if (a == 17) { a = 17; } + if (a == 18) { a = 18; } + if (a == 19) { a = 19; } + if (a == 20) { a = 20; } + if (a == 21) { a = 21; } + int *borrow p = &mut a; // expected-note {{`a` is borrowed here}} + a = 2; // expected-error {{cannot assign to `a` because it is borrowed}} + *p = 3; +} diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs index f0516009a18f6abdac436b449ce4c6cab945e8ac..80410d7b65f0d2a7aaf6689821c7ac7313f5462f 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs @@ -1,193 +1,248 @@ -// RUN: %clang_cc1 -ast-dump -verify %s +// RUN: %clang_cc1 -verify %s -void use_mut(int *borrow p) {} -void use_immut(const int *borrow p) {} -T* owned safe_malloc(T value); -void free_owned(T* owned p); -void free(void*); +#define NULL ((void*)0) + +struct S { + int a; +}; + +owned struct M { +public: + int a; +}; + +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +void use_two_borrow(int *borrow p1, int *borrow p2); +void useM_mut(M *borrow m); +void useM_immut(const M *borrow m); +T *owned safe_malloc(T value); +void free_owned(T *owned p); +void free(void *); void test1() { int local = 42; - int *borrow p1 = &mut local; - int *borrow p2 = &mut local; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_mut(p2); + int *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + int *borrow q = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + use_mut(p); + use_mut(q); } void test2() { int *owned oriPtr = safe_malloc(0); - int *borrow p1 = &mut *oriPtr; - int *borrow p2 = &mut *oriPtr; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'oriPtr' at the same time}} + int *borrow p1 = &mut *oriPtr; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut *oriPtr; // expected-error {{cannot borrow `*oriPtr` as mutable more than once at a time}} + use_mut(p1); use_mut(p2); free_owned(oriPtr); } -void test3(int* oriPtr) { - int *borrow p1 = &mut * oriPtr; - int *borrow p2 = &mut * oriPtr; - use_mut(p1); // error +void test3(int *oriPtr) { + int *borrow p1 = &mut *oriPtr; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut *oriPtr; // expected-error {{cannot borrow `*oriPtr` as mutable more than once at a time}} + use_mut(p1); free(oriPtr); } void test4() { - int local = 5; - int *borrow p = &mut local; - int *borrow p1 = &mut local; - const int *borrow p2 = &const *p; // expected-note {{previous borrow is here}} - // expected-note@-1 {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} + int local = 42; + int *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + int *borrow p1 = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + const int *borrow p2 = &const *p; // expected-note {{immutable borrow occurs here}} + use_mut(p); // expected-error {{cannot borrow `*p` as mutable because it is also borrowed as immutable}} + use_mut(p1); use_immut(p2); } void test5() { - int local = 5; - int *borrow p1 = &mut local; - const int *borrow p2 = &const local; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_immut(p2); + int local = 42; + int *borrow p = &mut local; // expected-note {{mutable borrow occurs here}} + const int *borrow q = &const local; // expected-error {{cannot borrow `local` as immutable because it is also borrowed as mutable}} + use_mut(p); + use_immut(q); } -void test6(int* borrow p) { +void test6(int *borrow p) { int local = 5; p = &mut local; - const int *borrow p1 = &const *p; // expected-note {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} + const int *borrow p1 = &const *p; // expected-note {{immutable borrow occurs here}} + use_mut(p); // expected-error {{cannot borrow `*p` as mutable because it is also borrowed as immutable}} use_immut(p1); } -void test7(int* borrow p) { - int *borrow p1 = p; // expected-note {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'p' at the same time}} +void test7(int *borrow p) { + int *borrow p1 = p; // expected-note {{`*p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + use_mut(p); // expected-error {{cannot use `p` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*p` as mutable more than once at a time}} use_mut(p1); } void test8() { int arr[5]; - int *borrow p = &mut arr[0]; - int *borrow p1 = &mut arr[1]; // expected-note {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'arr' at the same time}} - use_mut(p1); + int *borrow p = &mut arr[0]; // expected-note {{first mut borrow occurs here}} + int *borrow q = &mut arr[1]; // expected-error {{cannot borrow `arr` as mutable more than once at a time}} + use_mut(p); + use_mut(q); } -void use_two_borrow(int *borrow p1, int *borrow p2) {} void test9() { int local = 42; - int *borrow p1 = &mut local; - int *borrow p2 = &mut local; // expected-note {{previous borrow is here}} - use_two_borrow(p1, p2); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} + int *borrow p1 = &mut local; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + use_two_borrow(p1, p2); } void test10() { int local = 5; int *borrow p1 = &mut local; - const int *borrow p2 = &const local; // expected-note {{previous borrow is here}} + const int *borrow p2 = &const local; int *owned oriPtr = safe_malloc(0); if (local > 0) { - p1 = &mut *oriPtr; - p2 = &const *oriPtr; + p1 = &mut *oriPtr; // expected-note {{mutable borrow occurs here}} + p2 = &const *oriPtr; // expected-error {{cannot borrow `*oriPtr` as immutable because it is also borrowed as mutable}} } else { - p2 = &const *oriPtr; - p1 = &mut *oriPtr; + p2 = &const *oriPtr; // expected-note {{immutable borrow occurs here}} + p1 = &mut *oriPtr; // expected-error {{cannot borrow `*oriPtr` as mutable because it is also borrowed as immutable}} } - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'oriPtr' at the same time}} - use_immut(p2); // expected-error {{Can not use 'p2' because expired}} + use_mut(p1); + use_immut(p2); free_owned(oriPtr); } void test11() { int local = 5; int *borrow p1 = &mut local; - const int *borrow p2 = &const local; // expected-note {{previous borrow is here}} + const int *borrow p2 = &const local; int *owned oriPtr = safe_malloc(0); if (1) { - p1 = &mut *oriPtr; - p2 = &const *oriPtr; - } else { //Dead code - p1 = &mut *oriPtr; - p2 = &const *oriPtr; + p1 = &mut *oriPtr; // expected-note {{mutable borrow occurs here}} + p2 = &const *oriPtr; // expected-error {{cannot borrow `*oriPtr` as immutable because it is also borrowed as mutable}} + } else { + p1 = &mut *oriPtr; // expected-note {{mutable borrow occurs here}} + p2 = &const *oriPtr; // expected-error {{cannot borrow `*oriPtr` as immutable because it is also borrowed as mutable}} } - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'oriPtr' at the same time}} + use_mut(p1); use_immut(p2); free_owned(oriPtr); } -struct S { - int a; -}; - void test12() { - struct S s = { .a = 5 }; - const struct S *borrow p1 = &const s; - struct S *borrow p2 = &mut s; // expected-note {{previous borrow is here}} - struct S s1 = *p1; // expected-error {{There should be at most one mutable borrow targeting to 's' at the same time}} - struct S s2 = *p2; + struct S s = { .a = 5 }; + const struct S *borrow p1 = &const s; // expected-note {{immutable borrow occurs here}} + struct S *borrow p2 = &mut s; // expected-error {{cannot borrow `s` as mutable because it is also borrowed as immutable}} + struct S s1 = *p1; + struct S s2 = *p2; } -owned struct M { -public: - int a; -}; -void useM_mut(M * borrow m) {} -void useM_immut(const M * borrow m) {} - void test13() { - int local = 1; - M m = { .a = local }; - const M * borrow p1 = &const m; - M * borrow p2 = &mut m; // expected-note {{previous borrow is here}} - useM_immut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'm' at the same time}} - useM_mut(p2); + int local = 1; + M m = { .a = local }; + const M *borrow p1 = &const m; // expected-note {{immutable borrow occurs here}} + M *borrow p2 = &mut m; // expected-error {{cannot borrow `m` as mutable because it is also borrowed as immutable}} + useM_immut(p1); + useM_mut(p2); } -#define NULL ((void*)0) void test14(int *a) { - int *borrow p1 = (int *borrow)a; - int *borrow p2 = (int *borrow)a; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - use_mut(p2); + int *borrow p1 = (int *borrow)a; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = (int *borrow)a; // expected-error {{cannot borrow `*a` as mutable more than once at a time}} + use_mut(p1); + use_mut(p2); } void test15(int *a) { - int *borrow p1 = (int *borrow)NULL; - int *borrow p2 = (int *borrow)NULL; // expected-note {{previous borrow is here}} - p1 = (int *borrow)a; - p2 = (int *borrow)a; - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - use_mut(p2); + int *borrow p1 = (int *borrow)NULL; + int *borrow p2 = (int *borrow)NULL; + p1 = (int *borrow)a; // expected-note {{first mut borrow occurs here}} + p2 = (int *borrow)a; // expected-error {{cannot borrow `*a` as mutable more than once at a time}} + use_mut(p1); + use_mut(p2); } void test16() { - int a = 5; - int *b = &a; - int *borrow p1 = &mut *(&(*(&a))); - int *borrow p2 = &mut *(&(*(&a))); // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - use_mut(p2); - int *borrow p3 = (int* borrow)(&*b); - int *borrow p4 = (int* borrow)(&*b);// expected-note {{previous borrow is here}} - use_mut(p3); // expected-error {{There should be at most one mutable borrow targeting to 'b' at the same time}} - use_mut(p4); -} - -int main() { - int local = 5; - test1(); - test2(); - test3(&local); - test4(); - test5(); - test6(&mut local); - test7(&mut local); - test8(); - test9(); - test10(); - test11(); - test12(); - test13(); - test14(&local); - test15(&local); - test16(); - return 0; + int a = 5; + int *b = &a; + int *borrow p1 = &mut *(&(*(&a))); // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut *(&(*(&a))); // expected-error {{cannot borrow `a` as mutable more than once at a time}} + use_mut(p1); + use_mut(p2); + int *borrow p3 = (int *borrow)(&*b); // expected-note {{first mut borrow occurs here}} + int *borrow p4 = (int *borrow)(&*b); // expected-error {{cannot borrow `*b` as mutable more than once at a time}} + use_mut(p3); + use_mut(p4); +} + +void test17(int *a) { + int *borrow p1 = &mut *a; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut *a; // expected-error {{cannot borrow `*a` as mutable more than once at a time}} + use_mut(p1); + use_mut(p2); +} + +void test18(int *a) { + int *borrow p1 = (int *borrow)NULL; + int *borrow p2 = (int *borrow)NULL; + p1 = &mut *a; // expected-note {{first mut borrow occurs here}} + p2 = &mut *a; // expected-error {{cannot borrow `*a` as mutable more than once at a time}} + use_mut(p1); + use_mut(p2); +} + +void test19() { + int local = 42; + int *borrow p = &mut local, *borrow q = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} + use_mut(p); + use_mut(q); +} + +void test20() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = p; + p = q; // expected-note {{`*q` is borrowed here}} + int temp = *q; // expected-error {{cannot use `*q` because it was mutably borrowed}} +} + +void test21() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = &mut *p; + p = &mut *q; // expected-note {{`*q` is borrowed here}} + int temp = *q; // expected-error {{cannot use `*q` because it was mutably borrowed}} +} + +void test22() { + int local = 42; + int temp = 2; + int *borrow p1 = &mut local; + int *borrow p2 = &mut temp; + p2 = p1; // expected-note {{`*p1` is borrowed here}} + *p1 = 2; // expected-error {{cannot assign to `*p1` because it is borrowed}} + temp = *p2; +} + +void test23() { + int local = 42; + int *borrow p = &mut local; + const int *borrow q = &const *p; // expected-note {{`*p` is borrowed here}} + *p = 3; // expected-error {{cannot assign to `*p` because it is borrowed}} + use_immut(q); +} + +void test24() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = p; // expected-note {{`*p` is borrowed here}} + *p = 3; // expected-error {{cannot assign to `*p` because it is borrowed}} + use_mut(q); +} + +void test25() { + int local = 42; + int *borrow p1 = &mut local; + const int *borrow p2 = &const *p1; // expected-note {{immutable borrow occurs here}} + int *borrow p3 = p1; // expected-error {{cannot borrow `*p1` as mutable because it is also borrowed as immutable}} + use_immut(p2); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrow_member_func.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrow_member_func.cbs new file mode 100644 index 0000000000000000000000000000000000000000..f76a3e1d068ae9e22e7b2995c59b49a3c20db6e2 --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrow_member_func.cbs @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -verify %s + +int *borrow int::get_mut_borrow(This *borrow this) { + return this; +} + +const int *borrow int::get_immut_borrow(const This *borrow this) { + return this; +} + +void use_mut(int *borrow p); +void use_immut(const int *borrow p); + +void test1() { + int a = 5; + int *borrow p = a.get_mut_borrow(); // expected-note {{first mut borrow occurs here}} + int *borrow q = a.get_mut_borrow(); // expected-error {{cannot borrow `a` as mutable more than once at a time}} + use_mut(p); + use_mut(q); +} + +void test2() { + int a = 5; + const int *borrow p = a.get_immut_borrow(); // expected-note {{immutable borrow occurs here}} + int *borrow q = a.get_mut_borrow(); // expected-error {{cannot borrow `a` as mutable because it is also borrowed as immutable}} + use_immut(p); + use_mut(q); +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrowat_most_one_mut_borrow_member_func.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrowat_most_one_mut_borrow_member_func.cbs deleted file mode 100644 index b93f3653e4997f255efcbd7f59dd5edef04e0795..0000000000000000000000000000000000000000 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/at_most_one_mut_borrow_member_func/at_most_one_mut_borrowat_most_one_mut_borrow_member_func.cbs +++ /dev/null @@ -1,29 +0,0 @@ -// RUN: %clang_cc1 -ast-dump -verify %s - -int *borrow int::get_mut_borrow(This * borrow this) { - return this; -} - -const int *borrow int::get_immut_borrow(const This * borrow this) { - return this; -} - -void use_mut(int *borrow p) {} -void use_immut(const int *borrow p) {} - -void test1() { - int a = 5; - int * borrow p = a.get_mut_borrow(); // expected-note {{previous borrow is here}} - int * borrow q = a.get_mut_borrow(); // expected-note {{previous borrow is here}} expected-error {{Can not modify 'a' because be borrowed}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - use_mut(q); -} - -void test2() { - int a = 5; - const int * borrow p = a.get_immut_borrow(); // expected-note {{previous borrow is here}} - int * borrow q = a.get_mut_borrow(); // expected-note {{previous borrow is here}} expected-error {{Can not modify 'a' because be borrowed}} - use_immut(p); // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - use_mut(q); -} - diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_another_borrow/borrow_field_of_another_borrow.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_another_borrow/borrow_field_of_another_borrow.cbs index 0bfd27e01413773816d082d49f2a74fff70ec0e4..5c16737a48f9672db17a551049d2f6c35daa8684 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_another_borrow/borrow_field_of_another_borrow.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_another_borrow/borrow_field_of_another_borrow.cbs @@ -1,60 +1,60 @@ // RUN: %clang_cc1 -verify %s -void use_mut(int *borrow p) {} -void use_immut(const int *borrow p) {} - struct V { - int a; - int *b; + int a; + int *b; }; struct K { struct V v; }; +void use_mut(int *borrow p); +void use_immut(const int *borrow p); + void test1() { - int local = 5; - struct V v = { .a = 5, .b = &local }; - struct V * borrow p = &mut v; - int *borrow q1 = &mut p->a; - int *borrow q2 = &mut *p->b; - const int *borrow q3 = &const p->a; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - const int *borrow q4 = &const *p->b; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - use_mut(q1); // expected-error {{There should be at most one mutable borrow targeting to 'v.a' at the same time}} - use_mut(q2); // expected-error {{There should be at most one mutable borrow targeting to 'v.b' at the same time}} - use_immut(q3); - use_immut(q4); + int local = 5; + struct V v = { .a = 5, .b = &local }; + struct V *borrow p = &mut v; + int *borrow q1 = &mut p->a; // expected-note {{mutable borrow occurs here}} + int *borrow q2 = &mut *p->b; // expected-note {{mutable borrow occurs here}} + const int *borrow q3 = &const p->a; // expected-error {{cannot borrow `(*p).a` as immutable because it is also borrowed as mutable}} + const int *borrow q4 = &const *p->b; // expected-error {{cannot borrow `*(*p).b` as immutable because it is also borrowed as mutable}} + use_mut(q1); + use_mut(q2); + use_immut(q3); + use_immut(q4); - struct K k = { .v = v }; - p = &mut k.v; - q1 = &mut p->a; - q2 = &mut *p->b; - q3 = &const p->a; - q4 = &const *p->b; - use_mut(q1); // expected-error {{There should be at most one mutable borrow targeting to 'k.v.a' at the same time}} - use_mut(q2); // expected-error {{There should be at most one mutable borrow targeting to 'k.v.b' at the same time}} - use_immut(q3); - use_immut(q4); + struct K k = { .v = v }; + p = &mut k.v; + q1 = &mut p->a; // expected-note {{mutable borrow occurs here}} + q2 = &mut *p->b; // expected-note {{mutable borrow occurs here}} + q3 = &const p->a; // expected-error {{cannot borrow `(*p).a` as immutable because it is also borrowed as mutable}} + q4 = &const *p->b; // expected-error {{cannot borrow `*(*p).b` as immutable because it is also borrowed as mutable}} + use_mut(q1); + use_mut(q2); + use_immut(q3); + use_immut(q4); } -void test2(struct V * borrow p) { - int *borrow q1 = &mut p->a; - int *borrow q2 = &mut *p->b; - const int *borrow q3 = &const p->a; // expected-note {{previous borrow is here}} - const int *borrow q4 = &const *p->b; // expected-note {{previous borrow is here}} - use_mut(q1); // expected-error {{There should be at most one mutable borrow targeting to 'p.a' at the same time}} - use_mut(q2); // expected-error {{There should be at most one mutable borrow targeting to 'p.b' at the same time}} - use_immut(q3); - use_immut(q4); +void test2(struct V *borrow p) { + int *borrow q1 = &mut p->a; // expected-note {{mutable borrow occurs here}} + int *borrow q2 = &mut *p->b; // expected-note {{mutable borrow occurs here}} + const int *borrow q3 = &const p->a; // expected-error {{cannot borrow `(*p).a` as immutable because it is also borrowed as mutable}} + const int *borrow q4 = &const *p->b; // expected-error {{cannot borrow `*(*p).b` as immutable because it is also borrowed as mutable}} + use_mut(q1); + use_mut(q2); + use_immut(q3); + use_immut(q4); } void struct V::foo(This *borrow this) { - int *borrow q1 = &mut this->a; - int *borrow q2 = &mut *this->b; - const int *borrow q3 = &const this->a; // expected-note {{previous borrow is here}} - const int *borrow q4 = &const *this->b; // expected-note {{previous borrow is here}} - use_mut(q1); // expected-error {{There should be at most one mutable borrow targeting to 'this.a' at the same time}} - use_mut(q2); // expected-error {{There should be at most one mutable borrow targeting to 'this.b' at the same time}} - use_immut(q3); - use_immut(q4); + int *borrow q1 = &mut this->a; // expected-note {{mutable borrow occurs here}} + int *borrow q2 = &mut *this->b; // expected-note {{mutable borrow occurs here}} + const int *borrow q3 = &const this->a; // expected-error {{cannot borrow `(*this).a` as immutable because it is also borrowed as mutable}} + const int *borrow q4 = &const *this->b; // expected-error {{cannot borrow `*(*this).b` as immutable because it is also borrowed as mutable}} + use_mut(q1); + use_mut(q2); + use_immut(q3); + use_immut(q4); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_struct/borrow_field_of_struct.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_struct/borrow_field_of_struct.cbs index d2e2c1a48cdb05a68f1b1ab58f6d05b031f5f228..1acac3dbef2c184d2e156a445296b42be5fae268 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_struct/borrow_field_of_struct.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_field_of_struct/borrow_field_of_struct.cbs @@ -1,58 +1,77 @@ // RUN: %clang_cc1 -verify %s -void use(int *borrow p) {} typedef struct G { - int a; + int a; } G; -void test1() { - G g; - G * pg = &g; - int * borrow p1 = &mut g.a; // expected-note {{previous borrow is here}} - int * borrow p2 = &mut pg->a; // expected-note {{previous borrow is here}} - g.a = 5; // expected-error {{Can not modify 'g.a' because be borrowed}} - pg->a = 5; // expected-error {{Can not modify 'pg.a' because be borrowed}} - use(p1); - use(p2); -} - struct S { G g; }; + struct K { - G* g; + G *g; }; +struct M { + struct S *borrow ps; + struct K *borrow pk; +}; + +void use(int *borrow p) {} +void useM(struct M m) {} + +void test1() { + G g = { .a = 1 }; + int *borrow p1 = &mut g.a; // expected-note {{`g.a` is borrowed here}} + g.a = 5; // expected-error {{cannot assign to `g.a` because it is borrowed}} + use(p1); +} + void test2() { - struct S s1; - struct S *s = &s1; - int *borrow p1 = &mut s->g.a; // expected-note {{previous borrow is here}} - int *borrow p2 = &mut s1.g.a; // expected-note {{previous borrow is here}} - s->g.a = 5; // expected-error {{Can not modify 's.g.a' because be borrowed}} - s1.g.a = 5; // expected-error {{Can not modify 's1.g.a' because be borrowed}} - use(p1); - use(p2); - - struct K k1; - struct K *k = &k1; - int *borrow q1 = &mut k->g->a; // expected-note {{previous borrow is here}} - int *borrow q2 = &mut k1.g->a; // expected-note {{previous borrow is here}} - k->g->a = 5; // expected-error {{Can not modify 'k.g.a' because be borrowed}} - k1.g->a = 5; // expected-error {{Can not modify 'k1.g.a' because be borrowed}} - use(q1); - use(q2); + G g; + G *pg = &g; + int *borrow p2 = &mut pg->a; // expected-note {{`(*pg).a` is borrowed here}} + pg->a = 5; // expected-error {{cannot assign to `(*pg).a` because it is borrowed}} + use(p2); } -struct M { - struct S *borrow ps; - struct K *borrow pk; -}; -void useM(struct M m) {}; void test3() { - struct S s1; - struct K k1; - struct M m = { .ps = &mut s1, .pk = &mut k1 }; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - s1.g.a = 5; // expected-error {{Can not modify 's1.g.a' because be borrowed}} - k1.g->a = 5; // expected-error {{Can not modify 'k1.g.a' because be borrowed}} - useM(m); + G g; + G *pg = &g; + int *borrow p1 = &mut g.a; // expected-note {{`g.a` is borrowed here}} + int *borrow p2 = &mut pg->a; // expected-note {{`(*pg).a` is borrowed here}} + g.a = 5; // expected-error {{cannot assign to `g.a` because it is borrowed}} + pg->a = 5; // expected-error {{cannot assign to `(*pg).a` because it is borrowed}} + use(p1); + use(p2); +} + +void test4() { + struct S s1; + struct S* s = &s1; + int *borrow p1 = &mut s->g.a; // expected-note {{`(*s).g.a` is borrowed here}} + int *borrow p2 = &mut s1.g.a; // expected-note {{`s1.g.a` is borrowed here}} + s->g.a = 5; // expected-error {{cannot assign to `(*s).g.a` because it is borrowed}} + s1.g.a = 5; // expected-error {{cannot assign to `s1.g.a` because it is borrowed}} + use(p1); + use(p2); + + struct K k1; + struct K *k = &k1; + int *borrow q1 = &mut k->g->a; // expected-note {{`(*(*k).g).a` is borrowed here}} + int *borrow q2 = &mut k1.g->a; // expected-note {{`(*k1.g).a` is borrowed here}} + k->g->a = 5; // expected-error {{cannot assign to `(*(*k).g).a` because it is borrowed}} + k1.g->a = 5; // expected-error {{cannot assign to `(*k1.g).a` because it is borrowed}} + use(q1); + use(q2); +} + +void test5() { + struct S s1; + struct K k1; + struct M m = { .ps = &mut s1, .pk = &mut k1 }; // expected-note {{`s1.g.a` is borrowed here}} + // expected-note@-1 {{`(*k1.g).a` is borrowed here}} + s1.g.a = 5; // expected-error {{cannot assign to `s1.g.a` because it is borrowed}} + k1.g->a = 5; // expected-error {{cannot assign to `(*k1.g).a` because it is borrowed}} + useM(m); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer/borrow_live_longer.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer/borrow_live_longer.cbs index 45fea8c9d8348bb0b7cb033a9faf2949765d6680..015ca4eb9513280f5d115ab6623e89134210859f 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer/borrow_live_longer.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer/borrow_live_longer.cbs @@ -1,134 +1,138 @@ // RUN: %clang_cc1 -verify %s -void use_mut(int *borrow p) {} -T* owned safe_malloc(T value); -void free_owned(T* owned p); +struct S1 { int a; }; +struct S2 { int *owned b; }; +struct S3 { struct S1 c; }; +struct S4 { + int m; + int *borrow p; +}; + +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +T *owned safe_malloc(T value); +void free_owned(T *owned p); void free(void*); void test1() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} use_mut(p); } -void test2(int* borrow p) { // expected-error {{borrow pointer variable 'p' lives longer than target variable}} +void test2(int *borrow p) { { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} use_mut(p); } void test3() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; if (1) { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} use_mut(p); } void test4() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; if (local > 0) { int local1 = 42; - p = &mut local1; - } else { + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } else { // expected-note {{`local1` dropped here while still borrowed}} int local2 = 42; - p = &mut local2; - } + p = &mut local2; // expected-error {{`local2` does not live long enough}} + } // expected-note {{`local2` dropped here while still borrowed}} use_mut(p); } void test5() { int *owned oriPtr = safe_malloc(0); - int *borrow p = &mut * oriPtr; // expected-note {{previous borrow is here}} - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} - use_mut(p); + const int *borrow p = &const *oriPtr; // expected-note {{`*oriPtr` is borrowed here}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} + use_immut(p); } void test6(int *owned oriPtr) { - int *borrow p = &mut * oriPtr; // expected-note {{previous borrow is here}} - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + int *borrow p = &mut *oriPtr; // expected-note {{`*oriPtr` is borrowed here}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} use_mut(p); } void test7() { int a = 5; int *owned oriPtr = safe_malloc(0); - int *borrow p = &mut* oriPtr; // expected-note {{previous borrow is here}} - // expected-note@-1 {{previous borrow is here}} + int *borrow p = &mut *oriPtr; // expected-note {{`*oriPtr` is borrowed here}} + // expected-note@-1 {{`*oriPtr` is borrowed here}} if (a > 0) { - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} } else { - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} } use_mut(p); } -struct S1 { int a; }; void test8() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; { - struct S1 s = { .a = 42, }; - p = &mut s.a; - } + struct S1 s = { .a = 42 }; + p = &mut s.a; // expected-error {{`s.a` does not live long enough}} + } // expected-note {{`s.a` dropped here while still borrowed}} use_mut(p); } -struct S2 { int* owned b; }; void test9() { int local = 42; - int *borrow p = &mut local; // expected-note {{previous borrow is here}} expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; { struct S2 s = { .b = safe_malloc(5) }; - p = &mut *s.b; - free_owned(s.b); // expected-error {{Can not modify 's.b' because be borrowed}} - } + p = &mut *s.b; // expected-error {{`*s.b` does not live long enough}} expected-note {{`*s.b` is borrowed here}} + free_owned(s.b); // expected-error {{cannot move out of `s.b` because it is borrowed}} + } // expected-note {{`*s.b` dropped here while still borrowed}} use_mut(p); } -struct S3 { struct S1 c; }; void test10() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; { struct S1 s1 = { .a = 42 }; struct S3 s2 = { .c = s1 }; - p = &mut s2.c.a; - } + p = &mut s2.c.a; // expected-error {{`s2.c.a` does not live long enough}} + } // expected-note {{`s2.c.a` dropped here while still borrowed}} use_mut(p); } -struct S4 { - int m; - int * borrow p; -}; - -void test11() { - struct S4 s = { .m = 0, .p = &mut s.m };// expected-error {{borrow pointer variable 's' lives longer than target variable}} +void test11(int a) { + const int *borrow q = &const a; + { + int foo = 42; + { + const int *borrow p = &const foo; // expected-error {{`foo` does not live long enough}} + q = &const *p; + } + } // expected-note {{`foo` dropped here while still borrowed}} + use_immut(q); } -int main() { - int local = 5; - int *owned oriPtr = safe_malloc(0); - test1(); - test2(&mut local); - test3(); - test4(); - test5(); - test6(oriPtr); - test7(); - test8(); - test9(); - test10(); - test11(); - return 0; +// Note: V1.0不报错,更合理 +void test12(int foo) { + int local = 42; + const int *borrow q = &const local; + { + int *borrow p = &mut foo; // expected-note {{`foo` is borrowed here}} + q = &const *p; + } + local = foo; // expected-error {{cannot use `foo` because it was mutably borrowed}} + use_immut(q); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer_loop/borrow_live_longer_loop.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer_loop/borrow_live_longer_loop.cbs index ecce012cc4de8e451e4d6c6ef1a1a37dc413d32d..929b5c57196808f7cd318d7d1b4ecb53fef8f903 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer_loop/borrow_live_longer_loop.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_live_longer_loop/borrow_live_longer_loop.cbs @@ -1,62 +1,68 @@ // RUN: %clang_cc1 -verify %s + +struct S1 { int a; }; +struct S2 { int *owned b; }; +struct S3 { struct S1 c; }; + int rand(); -void use_mut(int *borrow p) {} -T* owned safe_malloc(T value); -void free_owned(T* owned p); +void use_mut(int *borrow p); +T *owned safe_malloc(T value); +void free_owned(T *owned p); void free(void*); void test1() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; while (rand()) { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} use_mut(p); } void test2() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} - for (int i = 0; i < 5; i++) { + int *borrow p = &mut local; + for (int i = 0; i < 5; ++i) { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} use_mut(p); } -void test3(int* borrow p) { // expected-error {{borrow pointer variable 'p' lives longer than target variable}} +void test3(int *borrow p) { while (rand()) { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} use_mut(p); } void test4() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; while (rand()) { if (rand()) { int local1 = 42; - p = &mut local1; - } else { + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } else { // expected-note {{`local1` dropped here while still borrowed}} int local2 = 42; - p = &mut local2; - } + p = &mut local2; // expected-error {{`local2` does not live long enough}} + } // expected-note {{`local2` dropped here while still borrowed}} } use_mut(p); } void test5() { int *owned oriPtr = safe_malloc(0); - int *borrow p = &mut * oriPtr; // expected-note {{previous borrow is here}} + int *borrow p = &mut *oriPtr; // expected-note {{`*oriPtr` is borrowed here}} while (rand()) { if (rand()) { - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} use_mut(p); - } else + } else { free_owned(oriPtr); + } return; } free_owned(oriPtr); @@ -64,13 +70,13 @@ void test5() { void test6() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; while (rand()) { while (rand()) { - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; ++i) { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} } } use_mut(p); @@ -78,70 +84,51 @@ void test6() { void test7() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; while (rand()) { if (rand()) { - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; ++i) { int local1 = 42; - p = &mut local1; - } + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } // expected-note {{`local1` dropped here while still borrowed}} } else { - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; ++i) { int local2 = 42; - p = &mut local2; - } + p = &mut local2; // expected-error {{`local2` does not live long enough}} + } // expected-note {{`local2` dropped here while still borrowed}} } } use_mut(p); } -struct S1 { int a; }; void test8() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; while (rand()) { - struct S1 s = { .a = 42, }; - p = &mut s.a; - } + struct S1 s = { .a = 42 }; + p = &mut s.a; // expected-error {{`s.a` does not live long enough}} + } // expected-note {{`s.a` dropped here while still borrowed}} use_mut(p); } -struct S2 { int* owned b; }; void test9() { int local = 42; - int *borrow p = &mut local; /* expected-error {{borrow pointer variable 'p' lives longer than target variable}} - expected-note {{previous borrow is here}} */ + int *borrow p = &mut local; while (rand()) { struct S2 s = { .b = safe_malloc(5) }; - p = &mut *s.b; - free_owned(s.b); // expected-error {{Can not modify 's.b' because be borrowed}} - } + p = &mut *s.b; // expected-error {{`*s.b` does not live long enough}} expected-note {{`*s.b` is borrowed here}} + free_owned(s.b); // expected-error {{cannot move out of `s.b` because it is borrowed}} + } // expected-note {{`*s.b` dropped here while still borrowed}} use_mut(p); } -struct S3 { struct S1 c; }; void test10() { int local = 42; - int *borrow p = &mut local; // expected-error {{borrow pointer variable 'p' lives longer than target variable}} + int *borrow p = &mut local; while (rand()) { struct S1 s1 = { .a = 42 }; struct S3 s2 = { .c = s1 }; - p = &mut s2.c.a; - } + p = &mut s2.c.a; // expected-error {{`s2.c.a` does not live long enough}} + } // expected-note {{`s2.c.a` dropped here while still borrowed}} use_mut(p); -} - -int main() { - int local = 5; - test1(); - test2(); - test3(&mut local); - test4(); - test5(); - test6(); - test7(); - test8(); - test9(); - test10(); - return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_same_var_in_params/borrow_same_var_in_params.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_same_var_in_params/borrow_same_var_in_params.cbs new file mode 100644 index 0000000000000000000000000000000000000000..4cdfe3aefff810227a3711300fdbb1f013b1b769 --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_same_var_in_params/borrow_same_var_in_params.cbs @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -verify %s + +void foo1(int *borrow, int *borrow); +void foo2(int, int *borrow); +void foo3(int *borrow, int); + +void test1(int local) { + foo1(&mut local, &mut local); // expected-error {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} +} + +void test2(int local) { + int *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + int *borrow q = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + foo1(p, q); +} + +void test3(int local) { + int *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + foo1(p, &mut local); // expected-error {{cannot borrow `local` as mutable more than once at a time}} +} + +void test4(int local) { + int *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + foo1(&mut local, p); // expected-error {{cannot borrow `local` as mutable more than once at a time}} +} + +void test5(int local) { + foo2(local, &mut local); // expected-error {{cannot use `local` because it was mutably borrowed}} + // expected-note@-1 {{`local` is borrowed here}} +} + +void test6(int local) { + int *borrow p = &mut local; // expected-note {{`local` is borrowed here}} + foo2(local, p); // expected-error {{cannot use `local` because it was mutably borrowed}} +} + +void test7(int local) { + foo3(&mut local, local); // expected-error {{cannot use `local` because it was mutably borrowed}} + // expected-note@-1 {{`local` is borrowed here}} +} + +void test8(int local) { + int *borrow p = &mut local; // expected-note {{`local` is borrowed here}} + foo3(p, local); // expected-error {{cannot use `local` because it was mutably borrowed}} +} diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_struct_borrow_field/borrow_struct_borrow_field.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_struct_borrow_field/borrow_struct_borrow_field.cbs index 33c8127a875000aea40b00fe36aba737a58fbd9d..dd709cb4fb8a767642601282116c6e04d46a2677 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_struct_borrow_field/borrow_struct_borrow_field.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_struct_borrow_field/borrow_struct_borrow_field.cbs @@ -1,98 +1,117 @@ // RUN: %clang_cc1 -verify %s typedef struct A { - int * borrow p; - int * borrow q; - int c; + int *borrow p; + int *borrow q; + int c; } A; -void useSA(A); - -void test1(A a) { - A b = a; // expected-note {{previous borrow is here}} - useSA(a); // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - useSA(b); -} - typedef struct B { - A a; - int d; + A a; + int d; } B; +struct V { + int a; +}; + +struct M { + int *borrow ma; + const int *borrow na; +}; + +void useSA(A); void useSB(B); +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +int get_by_mut(int *borrow p); + +void test1(A a) { + A b = a; // expected-note {{`*a.p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + // expected-note@-2 {{first mut borrow occurs here}} + useSA(a); // expected-error {{cannot use `a` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*a.p` as mutable more than once at a time}} + // expected-error@-2 {{cannot borrow `*a.q` as mutable more than once at a time}} + useSA(b); +} void test2(B b) { - A m = b.a; // expected-note {{previous borrow is here}} - useSB(b); // expected-error {{There should be at most one mutable borrow targeting to 'b' at the same time}} - useSA(m); + A m = b.a; // expected-note {{`*b.a.p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + // expected-note@-2 {{first mut borrow occurs here}} + useSB(b); // expected-error {{cannot use `b` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*b.a.p` as mutable more than once at a time}} + // expected-error@-2 {{cannot borrow `*b.a.q` as mutable more than once at a time}} + useSA(m); } void test3(B b) { - A m = b.a; // expected-note {{previous borrow is here}} - useSA(b.a); // expected-error {{There should be at most one mutable borrow targeting to 'b' at the same time}} - useSA(m); + A m = b.a; // expected-note {{`*b.a.p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + // expected-note@-2 {{first mut borrow occurs here}} + useSA(b.a); // expected-error {{cannot use `b.a` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*b.a.p` as mutable more than once at a time}} + // expected-error@-2 {{cannot borrow `*b.a.q` as mutable more than once at a time}} + useSA(m); } -void use_mut(int *borrow p); -void use_immut(const int *borrow p); - -struct V { - int a; -}; - -struct M { - int *borrow ma; - const int *borrow na; -}; - void test4() { - struct V v = { .a = 5 }; - struct V * borrow p = &mut v; - int *borrow q1 = &mut p->a; - const int *borrow q2 = &const p->a; - struct M m = { .ma = q1, .na = q2 }; // expected-note {{previous borrow is here}} - use_mut(m.ma); - use_immut(m.na); // expected-error {{Can not use 'm.na' because expired}} + struct V v = { .a = 5 }; + struct V *borrow p = &mut v; + int *borrow q1 = &mut p->a; // expected-note {{mutable borrow occurs here}} + const int *borrow q2 = &const p->a; // expected-error {{cannot borrow `(*p).a` as immutable because it is also borrowed as mutable}} + struct M m = { .ma = q1, .na = q2 }; + use_mut(m.ma); + use_immut(m.na); - // reassign - struct V v1 = { .a = 5 }; - p = &mut v1; - m.ma = &mut p->a; - m.na = &const p->a; - use_mut(m.ma); // expected-error {{There should be at most one mutable borrow targeting to 'v1.a' at the same time}} - use_immut(m.na); + struct V v1 = { .a = 5 }; + p = &mut v1; + m.ma = &mut p->a; // expected-note {{mutable borrow occurs here}} + m.na = &const p->a; // expected-error {{cannot borrow `(*p).a` as immutable because it is also borrowed as mutable}} + use_mut(m.ma); + use_immut(m.na); } void test5() { - struct V v = { .a = 5 }; - struct V * borrow p = &mut v; - int *borrow q1 = &mut p->a; - const int *borrow q2 = &const p->a; - struct M m = { .ma = q1, .na = q2 }; // expected-note {{previous borrow is here}} - use_mut(m.ma); - use_immut(m.na); // expected-error {{Can not use 'm.na' because expired}} + struct V v = { .a = 5 }; + struct V *borrow p = &mut v; + int *borrow q1 = &mut p->a; // expected-note {{mutable borrow occurs here}} + const int *borrow q2 = &const p->a; // expected-error {{cannot borrow `(*p).a` as immutable because it is also borrowed as mutable}} + struct M m = { .ma = q1, .na = q2 }; + use_mut(m.ma); + use_immut(m.na); - // reassign - struct V v1 = { .a = 5 }; - p = &mut v1; - m.na = &const p->a; - m.ma = &mut p->a; - use_immut(m.na); // expected-error {{There should be at most one mutable borrow targeting to 'v1.a' at the same time}} - use_mut(m.ma); + struct V v1 = { .a = 5 }; + p = &mut v1; + m.na = &const p->a; // expected-note {{immutable borrow occurs here}} + m.ma = &mut p->a; // expected-error {{cannot borrow `(*p).a` as mutable because it is also borrowed as immutable}} + use_immut(m.na); + use_mut(m.ma); } void test6() { int a = 0; int b = 0; struct M m = { &mut a, &const b }; - struct M * mp = &m; + struct M *mp = &m; int *borrow p1 = mp->ma; - int *borrow p2 = mp->ma; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'mp.ma' at the same time}} + int *borrow p2 = mp->ma; + use_mut(p1); use_mut(p2); - struct M ** mpp = ∓ + struct M **mpp = ∓ int *borrow p3 = (*mpp)->ma; - int *borrow p4 = (*mpp)->ma; // expected-note {{previous borrow is here}} - use_mut(p3); // expected-error {{There should be at most one mutable borrow targeting to 'mpp.ma' at the same time}} - use_mut(p4); + int *borrow p4 = (*mpp)->ma; + use_mut(p3); + use_mut(p4); +} + +void test7() { + int local = 2; + int *borrow p = &mut local; + int *borrow q = &mut *p; // expected-note {{`*p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + int y = get_by_mut(p) + 2; // expected-error {{cannot use `p` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*p` as mutable more than once at a time}} + use_mut(q); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_assign/borrow_var_assign.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_assign/borrow_var_assign.cbs new file mode 100644 index 0000000000000000000000000000000000000000..bf3e3178e7cd3e3f7081083a4b2c246b191aeebd --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_assign/borrow_var_assign.cbs @@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -verify %s + +struct S { + int a; +}; + +void use_mut(int* borrow p); +void use_immut(const int* borrow p); +T* owned safe_malloc(T value); +void free_owned(T* owned p); +void free(void*); + +void test1() { + int local = 42; + int* borrow p = &mut local; // expected-note {{`local` is borrowed here}} + int* borrow q = p; + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + use_mut(q); +} + +void test2() { + int* owned oriPtr = safe_malloc(0); + int* borrow p1 = &mut *oriPtr; // expected-note {{`*oriPtr` is borrowed here}} + int* borrow p2 = p1; + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} + use_mut(p2); +} + +void test3() { + int local = 42; + int* borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + // expected-note@-1 {{`local` is borrowed here}} + int* borrow q = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + q = p; + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + use_mut(q); +} + +void test4(int* oriPtr) { + int* borrow p1 = &mut *oriPtr; // expected-note {{first mut borrow occurs here}} + // expected-note@-1 {{`*oriPtr` is borrowed here}} + int* borrow p2 = &mut *oriPtr; // expected-error {{cannot borrow `*oriPtr` as mutable more than once at a time}} + free(oriPtr); // expected-error {{cannot use `oriPtr` because it was mutably borrowed}} + use_mut(p1); +} + +void test5() { + int local = 42; + const int* borrow p = &const local; // expected-note {{`local` is borrowed here}} + const int* borrow q = p; + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + use_immut(q); +} + +void test6() { + int arr[5]; + int* borrow p = &mut arr[0]; // expected-note {{`arr` is borrowed here}} + int* borrow q = p; + int temp = arr[2]; // expected-error {{cannot use `arr` because it was mutably borrowed}} + use_mut(q); +} + +void test7() { + struct S s = { .a = 5 }; + struct S* borrow p1 = &mut s; // expected-note {{`s` is borrowed here}} + struct S* borrow p2 = p1; + struct S temp = s; // expected-error {{cannot use `s` because it was mutably borrowed}} + struct S s2 = *p2; +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_expired/borrow_var_expired.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_expired/borrow_var_expired.cbs index 016f402082aaa98180c2105a017ce4693962daee..122106a3b16b34e600613b7999d3f793b34f941e 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_expired/borrow_var_expired.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_var_expired/borrow_var_expired.cbs @@ -1,147 +1,147 @@ -// RUN: %clang_cc1 -ast-dump -verify %s +// RUN: %clang_cc1 -verify %s -void borrow_expired_01() { - int a = 1; - int * borrow p1 = &mut a; - int * borrow p2 = &mut a; - int b = *p2; - int c = *p1; // expected-error {{Can not use 'p1' because expired}} -} - -void borrow_expired_02() { - int a = 1; - int * borrow p1 = &mut a; - const int * borrow p2 = &const a; - int b = *p2; - int c = *p1; // ok -} - -void borrow_expired_03() { - int a = 1; - const int * borrow p1 = &const a; - int * borrow p2 = &mut a; - int b = *p2; - int c = *p1; // expected-error {{Can not use 'p1' because expired}} -} - -void borrow_expired_04() { - int a = 1; - const int * borrow p1 = &const a; - const int * borrow p2 = &const a; - int b = *p2; - int c = *p1; // ok -} - -struct S -{ - int a; - int b; +struct S { + int a; + int b; }; -void borrow_expired_05() { - struct S s = {1, 1}; - int * borrow p1 = &mut s.a; - int * borrow p2 = &mut s.b; - int b = *p2; - int c = *p1; //ok -} - -void borrow_expired_06() { - struct S s = {1, 1}; - int * borrow p1 = &mut s.a; - const int * borrow p2 = &const s.b; - int b = *p2; - int c = *p1; // ok -} - -void borrow_expired_07() { - struct S s = {1, 1}; - const int * borrow p1 = &const s.a; - int * borrow p2 = &mut s.b; - int b = *p2; - int c = *p1; // ok -} - -void borrow_expired_08() { - struct S s = {1, 1}; - const int * borrow p1 = &const s.a; - const int * borrow p2 = &const s.b; - int b = *p2; - int c = *p1; // ok -} +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +void use_S_mut(struct S *borrow a); +void use_S_immut(const struct S *borrow a); -void use_test01(struct S * borrow a){} -void use_test02(const struct S * borrow a){} - -void borrow_expired_09() { - struct S s = {1, 1}; - struct S * borrow p1 = &mut s; - int * borrow p2 = &mut s.b; - int b = *p2; - use_test01(p1); // expected-error {{Can not use 'p1' because expired}} +void test1() { + int a = 1; + int *borrow p1 = &mut a; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut a; // expected-error {{cannot borrow `a` as mutable more than once at a time}} + int b = *p2; + int c = *p1; } -void borrow_expired_10() { - struct S s = {1, 1}; - struct S * borrow p1 = &mut s; - const int * borrow p2 = &const s.b; - int b = *p2; - use_test01(p1); // ok +void test2() { + int a = 1; + int *borrow p1 = &mut a; // expected-note {{mutable borrow occurs here}} + const int *borrow p2 = &const a; // expected-error {{cannot borrow `a` as immutable because it is also borrowed as mutable}} + int b = *p2; + int c = *p1; } -void borrow_expired_11() { - struct S s = {1, 1}; - const struct S * borrow p1 = &const s; - int * borrow p2 = &mut s.b; - int b = *p2; - use_test02(p1); // expected-error {{Can not use 'p1' because expired}} +void test3() { + int a = 1; + const int *borrow p1 = &const a; // expected-note {{immutable borrow occurs here}} + int *borrow p2 = &mut a; // expected-error {{cannot borrow `a` as mutable because it is also borrowed as immutable}} + int b = *p2; + int c = *p1; } -void borrow_expired_12() { - struct S s = {1, 1}; - const struct S * borrow p1 = &const s; - const int * borrow p2 = &const s.b; - int b = *p2; // ok - use_test02(p1); +void test4() { + int a = 1; + const int *borrow p1 = &const a; + const int *borrow p2 = &const a; + int b = *p2; + int c = *p1; } -void borrow_expired_13() { - struct S s = {1, 1}; - int * borrow p1 = &mut s.b; - struct S * borrow p2 = &mut s; - use_test01(p2); - int b = *p1; // expected-error {{Can not use 'p1' because expired}} -} +void test5() { + struct S s = { 1, 1 }; + int *borrow p1 = &mut s.a; + int *borrow p2 = &mut s.b; + int b = *p2; + int c = *p1; +} + +void test6() { + struct S s = { 1, 1 }; + int *borrow p1 = &mut s.a; + const int *borrow p2 = &const s.b; + int b = *p2; + int c = *p1; +} + +void test7() { + struct S s = { 1, 1 }; + const int *borrow p1 = &const s.a; + int *borrow p2 = &mut s.b; + int b = *p2; + int c = *p1; +} + +void test8() { + struct S s = { 1, 1 }; + const int *borrow p1 = &const s.a; + const int *borrow p2 = &const s.b; + int b = *p2; + int c = *p1; +} + +void test9() { + struct S s = { 1, 1 }; + struct S *borrow p1 = &mut s; // expected-note {{first mut borrow occurs here}} + int *borrow p2 = &mut s.b; // expected-error {{cannot borrow `s.b` as mutable more than once at a time}} + int b = *p2; + use_S_mut(p1); +} + +void test10() { + struct S s = {1, 1}; + struct S *borrow p1 = &mut s; // expected-note {{mutable borrow occurs here}} + const int *borrow p2 = &const s.b; // expected-error {{cannot borrow `s.b` as immutable because it is also borrowed as mutable}} + int b = *p2; + use_S_mut(p1); +} + +void test11() { + struct S s = {1, 1}; + const struct S *borrow p1 = &const s; // expected-note {{immutable borrow occurs here}} + int *borrow p2 = &mut s.b; // expected-error {{cannot borrow `s.b` as mutable because it is also borrowed as immutable}} + int b = *p2; + use_S_immut(p1); +} + +void test12() { + struct S s = { 1, 1 }; + const struct S *borrow p1 = &const s; + const int *borrow p2 = &const s.b; + int b = *p2; + use_S_immut(p1); +} + +void test13() { + struct S s = { 1, 1 }; + int *borrow p1 = &mut s.b; // expected-note {{first mut borrow occurs here}} + struct S *borrow p2 = &mut s; // expected-error {{cannot borrow `s` as mutable more than once at a time}} + use_S_mut(p2); + int b = *p1; +} -void borrow_expired_14() { - struct S s = {1, 1}; - const int * borrow p1 = &const s.b; - struct S * borrow p2 = &mut s; - use_test01(p2); - int b = *p1; // expected-error {{Can not use 'p1' because expired}} -} +void test14() { + struct S s = { 1, 1 }; + const int *borrow p1 = &const s.b; // expected-note {{immutable borrow occurs here}} + struct S *borrow p2 = &mut s; // expected-error {{cannot borrow `s` as mutable because it is also borrowed as immutable}} + use_S_mut(p2); + int b = *p1; +} -void borrow_expired_15() { - struct S s = {1, 1}; - int * borrow p1 = &mut s.b; - const struct S * borrow p2 = &const s; - use_test02(p2); - int b = *p1; -} +void test15() { + struct S s = { 1, 1 }; + int *borrow p1 = &mut s.b; // expected-note {{mutable borrow occurs here}} + const struct S *borrow p2 = &const s; // expected-error {{cannot borrow `s` as immutable because it is also borrowed as mutable}} + use_S_immut(p2); + int b = *p1; +} -void borrow_expired_16() { - struct S s = {1, 1}; - const int * borrow p1 = &const s.b; - const struct S * borrow p2 = &const s; - use_test02(p2); - int b = *p1; // ok -} +void test16() { + struct S s = { 1, 1 }; + const int *borrow p1 = &const s.b; + const struct S *borrow p2 = &const s; + use_S_immut(p2); + int b = *p1; +} -void use_mut(int *borrow p) {} -void borrow_expired_17() { - int local = 5; - int *borrow p = &mut local; - int *borrow p1 = &mut *p; - use_mut(p1); - use_mut(&mut *p); // expected-error {{Can not use 'p' because expired}} +void test17() { + int local = 5; + int *borrow p = &mut local; + int *borrow p1 = &mut *p; + use_mut(p1); + use_mut(&mut *p); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_with_complex_control_flow/borrow_with_complex_control_flow.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_with_complex_control_flow/borrow_with_complex_control_flow.cbs new file mode 100644 index 0000000000000000000000000000000000000000..1e5ff2523608872532032a0b6ad3d0415bb2502e --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/borrow_with_complex_control_flow/borrow_with_complex_control_flow.cbs @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -verify %s + +void use(int *borrow); + +void test(int flag) { + int local = 3, a = 3, b = 4; + int *borrow p = &mut a; + int *borrow q = &mut b; + for (int i = 0; i < 3; ++i) { + if (flag) { + p = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} + } else { + q = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-1 {{first mut borrow occurs here}} + } + flag = ~flag; + } + use(p); + use(q); +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_field/field_borrow_field.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_field/field_borrow_field.cbs index 141ca05dad6121749aa1530a993e01f50759a083..63cc36093b5e36e616076f433100ebbd3f8dc955 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_field/field_borrow_field.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_field/field_borrow_field.cbs @@ -1,53 +1,53 @@ // RUN: %clang_cc1 -verify %s typedef struct R { - int a; - int b; + int a; + int b; } R; typedef struct S { - int * borrow p; - int * borrow q;; + int *borrow p; + int *borrow q; } S; -void use_mutI(int * borrow); -void use_constI(const int * borrow); +void use_mutI(int *borrow); +void use_constI(const int *borrow); void test1() { - R r = {.a = 1, .b = 2}; - S s = {.p = &mut r.a, .q = &mut r.b}; - use_mutI(s.p); - use_mutI(s.q); + R r = { .a = 1, .b = 2 }; + S s = { .p = &mut r.a, .q = &mut r.b }; + use_mutI(s.p); + use_mutI(s.q); } void test2() { - R r = {.a = 1, .b = 2}; - S s = {.p = &mut r.a, .q = &mut r.b}; - int * borrow pp = &mut r.a; // expected-note {{previous borrow is here}} - use_mutI(s.p); // expected-error {{There should be at most one mutable borrow targeting to 'r.a' at the same time}} - use_mutI(pp); + R r = { .a = 1, .b = 2 }; + S s = { .p = &mut r.a, .q = &mut r.b }; // expected-note {{first mut borrow occurs here}} + int *borrow pp = &mut r.a; // expected-error {{cannot borrow `r.a` as mutable more than once at a time}} + use_mutI(s.p); + use_mutI(pp); } void test3() { - R r = {.a = 1, .b = 2}; - S s = {.p = &mut r.a, .q = &mut r.b}; - int * borrow p1 = s.p; // expected-note {{previous borrow is here}} - use_mutI(s.p); // expected-error {{There should be at most one mutable borrow targeting to 'r.a' at the same time}} - int * borrow p2 = p1; + R r = { .a = 1, .b = 2 }; + S s = { .p = &mut r.a, .q = &mut r.b }; + int *borrow p1 = s.p; // expected-note {{`*s.p` is borrowed here}} + int temp = *s.p; // expected-error {{cannot use `*s.p` because it was mutably borrowed}} + int *borrow p2 = p1; } void test4() { - R r = {.a = 1, .b = 2}; - S s = {.p = &mut r.a, .q = &mut r.b}; // expected-note {{previous borrow is here}} - r.a = 5; // expected-error {{Can not modify 'r.a' because be borrowed}} - s.q = s.p; + R r = { .a = 1, .b = 2 }; + S s = { .p = &mut r.a, .q = &mut r.b }; // expected-note {{`r.a` is borrowed here}} + r.a = 5; // expected-error {{cannot assign to `r.a` because it is borrowed}} + s.q = s.p; } void test5() { - R r = {.a = 1, .b = 2}; - S s = {.p = &mut r.a, .q = &mut r.b}; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - r.a = 5; // expected-error {{Can not modify 'r.a' because be borrowed}} - s.q = s.p; - r.a = 5; // expected-error {{Can not modify 'r.a' because be borrowed}} - int *borrow p = s.q; + R r = { .a = 1, .b = 2 }; + S s = { .p = &mut r.a, .q = &mut r.b }; // expected-note {{`r.a` is borrowed here}} + r.a = 5; // expected-error {{cannot assign to `r.a` because it is borrowed}} + s.q = s.p; + r.a = 5; + int *borrow p = s.q; } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_var/field_borrow_var.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_var/field_borrow_var.cbs index 9aeb93889c8e20d2c53a1ee341dcba1dda1b60aa..9383ec73003903cd823ff63865287d182269ba19 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_var/field_borrow_var.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/field_borrow_var/field_borrow_var.cbs @@ -1,38 +1,39 @@ // RUN: %clang_cc1 -verify %s typedef struct R { - int a; - int b; + int a; + int b; } R; typedef struct S { - R * borrow r; - int num; + R *borrow r; + int num; } S; -void use_mut(R * borrow); -void use_const(const R * borrow); +void use_mut(R *borrow); +void use_const(const R *borrow); void test1() { - R local_r = {.a = 1, .b = 2}; - S s = {.r = &mut local_r, .num = 3}; // expected-note {{previous borrow is here}} - const R * borrow const_r = &const local_r; - local_r.a = 4; // expected-error {{Can not modify 'local_r.a' because be borrowed}} - use_mut(s.r); + R local_r = { .a = 1, .b = 2 }; + S s = { .r = &mut local_r, .num = 3 }; // expected-note {{`local_r.a` is borrowed here}} + // expected-note@-1 {{mutable borrow occurs here}} + const R *borrow const_r = &const local_r; // expected-error {{cannot borrow `local_r` as immutable because it is also borrowed as mutable}} + local_r.a = 4; // expected-error {{cannot assign to `local_r.a` because it is borrowed}} + use_mut(s.r); } void test2() { - R local_r = {.a = 1, .b = 2}; - S s = {.r = &mut local_r, .num = 3}; - const R * borrow const_r = &const local_r; // expected-note {{previous borrow is here}} - local_r.a = 4; // expected-error {{Can not modify 'local_r.a' because be borrowed}} - use_const(const_r); + R local_r = { .a = 1, .b = 2 }; + S s = { .r = &mut local_r, .num = 3 }; + const R *borrow const_r = &const local_r; // expected-note {{`local_r.a` is borrowed here}} + local_r.a = 4; // expected-error {{cannot assign to `local_r.a` because it is borrowed}} + use_const(const_r); } void test3() { - R local_r = {.a = 1, .b = 2}; - S s = {.r = &mut local_r, .num = 3}; - const R * borrow const_r = &const local_r; - int y = local_r.a; - use_const(const_r); + R local_r = { .a = 1, .b = 2 }; + S s = { .r = &mut local_r, .num = 3 }; + const R *borrow const_r = &const local_r; + int y = local_r.a; + use_const(const_r); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/kill_loans_by_write_to_path/kill_loans_by_write_to_path.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/kill_loans_by_write_to_path/kill_loans_by_write_to_path.cbs new file mode 100644 index 0000000000000000000000000000000000000000..4a71f8cba02839506aee7a83fb87f6e7ff5357fb --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/kill_loans_by_write_to_path/kill_loans_by_write_to_path.cbs @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -verify %s + +struct S { + int a; +}; + +void use_mut(int* borrow p); +void use_immut(const int* borrow p); +void use_mut_s(struct S* borrow p); + +void test1() { + struct S s1 = { .a = 1 }; + struct S s2 = { .a = 2 }; + struct S* borrow sp = &mut s1; // expected-note {{`s1.a` is borrowed here}} + // expected-note@-1 {{`s1.a` is borrowed here}} + int* borrow p = &mut sp->a; + sp = &mut s2; + sp->a = 3; + s1.a = 2; // expected-error {{cannot assign to `s1.a` because it is borrowed}} + use_mut(p); + s1.a = 2; // expected-error {{cannot assign to `s1.a` because it is borrowed}} + use_mut_s(sp); +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/member_function/member_function.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/member_function/member_function.cbs index 031ce34cda2d71b112d95fd78de9d37456ecbbc8..69363fffb277faaa6ea9cd53a56a14c9c4c4cadd 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/member_function/member_function.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/member_function/member_function.cbs @@ -1,42 +1,38 @@ // RUN: %clang_cc1 -verify %s -void int::setA(int* this) { - *this = 2; -} +struct A { + int m; + int n; + int c; +}; -void use_mut(int * borrow); -void use_const(const int * borrow); +void int::setA(int *this); +void use_mut(int *borrow); +void use_const(const int *borrow); +void use_mut_A(struct A *borrow); -void test1() { - int a = 1; - int * borrow p = &mut a; // expected-note {{previous borrow is here}} - a.setA(); // expected-error {{Can not modify 'a' because be borrowed}} - use_mut(p); +void struct A::use(struct A *this) { + this->m = 2; + this->n = 3; } - -void test2() { - int a = 1; - const int * borrow p = &const a; // expected-note {{previous borrow is here}} - a.setA(); // expected-error {{Can not modify 'a' because be borrowed}} - use_const(p); +void test1() { + int a = 1; + int *borrow p = &mut a; // expected-note {{`a` is borrowed here}} + a.setA(); // expected-error {{cannot use `a` because it was mutably borrowed}} + use_mut(p); } -struct A { - int m; - int n; - int c; -}; - -void struct A::use(struct A* this) { - this->m = 2; - this->n = 3; +// FIXME: not proper now +void test2() { + int a = 1; + const int *borrow p = &const a; + a.setA(); + use_const(p); } -void use_mut_A(struct A * borrow); - void test3(struct A a) { - struct A * borrow p = &mut a; // expected-note {{previous borrow is here}} - a.use(); // expected-error {{Can not modify 'a' because be borrowed}} - use_mut_A(p); + struct A *borrow p = &mut a; // expected-note {{`a` is borrowed here}} + a.use(); // expected-error {{cannot use `a` because it was mutably borrowed}} + use_mut_A(p); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/nested_call_and_member/nested_call_and_member.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/nested_call_and_member/nested_call_and_member.cbs index 524bdf00f2b2e84487a020f53ba8fbed9096e963..21e9bf0d960d8a401c3270e37956ea83f09b087f 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/nested_call_and_member/nested_call_and_member.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/nested_call_and_member/nested_call_and_member.cbs @@ -12,28 +12,30 @@ public: owned struct OptionRefMut { public: - ListNode* borrow inner; + ListNode *borrow inner; }; -OptionRefMut socket_table_find(int* borrow); -ListNode* borrow OptionRefMut::unwrap(OptionRefMut* borrow this); -ListNode* owned AllocSocket(); -void accept(int, struct sockaddr *, int *); -void SkAcceptInfoSet(const v5_adpt_socket_t* borrow, v5_adpt_socket_t* borrow); +OptionRefMut socket_table_find(int *borrow); +ListNode *borrow OptionRefMut::unwrap(OptionRefMut *borrow this); +ListNode *owned AllocSocket(); +void accept(int, struct sockaddr *, int *); +void SkAcceptInfoSet(const v5_adpt_socket_t *borrow, v5_adpt_socket_t *borrow); void CloseSocket(v5_adpt_socket_t *); -void use(ListNode* borrow); +void use(ListNode *borrow); -void v5_accept(int fd, void* addr, int* addrLen, int* borrow p) { +void v5_accept(int fd, void *addr, int *addrLen, int *borrow p) { OptionRefMut o = socket_table_find(p); - const v5_adpt_socket_t* borrow ls = &const o.unwrap()->inner; // expected-note {{previous borrow is here}} - use(o.inner); // expected-error {{There should be at most one mutable borrow targeting to 'p' at the same time}} - ListNode* owned t = AllocSocket(); + const v5_adpt_socket_t *borrow ls = &const o.unwrap()->inner; // expected-note {{`o` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + use(o.inner); // expected-error {{cannot use `o.inner` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*o.inner` as mutable more than once at a time}} + ListNode *owned t = AllocSocket(); { - v5_adpt_socket_t* borrow s = &mut t->inner; + v5_adpt_socket_t *borrow s = &mut t->inner; accept(fd, (struct sockaddr *)addr, addrLen); SkAcceptInfoSet(ls, s); } - v5_adpt_socket_t* borrow s = &mut t->inner; - ListNode* t1 = (ListNode *)t; - CloseSocket((v5_adpt_socket_t*)s); + v5_adpt_socket_t *borrow s = &mut t->inner; // expected-note {{`(*t).inner` is borrowed here}} + ListNode *t1 = (ListNode *)t; // expected-error {{cannot move out of `t` because it is borrowed}} + CloseSocket((v5_adpt_socket_t *)s); } diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow/return_borrow.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow/return_borrow.cbs index e688d6b91de34d34a2ef4bc49b6f7f27a6d617f8..b99876c87afe176d4f1d154e00799b60175bc2ed 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow/return_borrow.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow/return_borrow.cbs @@ -1,60 +1,83 @@ // RUN: %clang_cc1 -verify %s -T* owned safe_malloc(T value); -void free_owned(T* owned p); -void free(void*); +struct S { + int *borrow p; +}; + +struct G { + struct S s; +}; + +struct P { + int *b; +}; + +struct Q { + struct P g; +}; + +T *owned safe_malloc(T value); +void free_owned(T *owned p); +void free(void *); int *borrow test1(int *borrow p) { int local = 42; - return &mut local; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + return &mut local; // expected-error {{`local` does not live long enough}} + // expected-note@-1 {{`local` dropped here while still borrowed}} } int *borrow test2(int *borrow p) { int local = 42; - p = &mut local; - return p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + p = &mut local; // expected-error {{`local` does not live long enough}} + return p; // expected-note {{`local` dropped here while still borrowed}} } +// FIXME: parameters lakes CFGScopeEnd int *borrow test3(int *borrow p, int a) { - return &mut a; //expected-error{{Return value cannot be a borrow from local variable 'a'}} + return &mut a; // expected-error {{`a` does not live long enough}} + // expected-note@-1 {{`a` dropped here while still borrowed}} } +// FIXME: parameters lakes CFGScopeEnd int *borrow test4(int *borrow p, int a) { - p = &mut a; - return p; //expected-error{{Return value cannot be a borrow from local variable 'a'}} + p = &mut a; // expected-error {{`a` does not live long enough}} + return p; // expected-note {{`a` dropped here while still borrowed}} } int *borrow test5(int *borrow p) { int local = 5; - p = &mut local; - int* borrow p1 = &mut* p; - return p1; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + p = &mut local; // expected-error {{`local` does not live long enough}} + int* borrow p1 = &mut *p; + return p1; // expected-note {{`local` dropped here while still borrowed}} } int *borrow test6(int *borrow p) { int local = 5; - p = &mut local; - return &mut* p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + p = &mut local; // expected-error {{`local` does not live long enough}} + return &mut *p; // expected-note {{`local` dropped here while still borrowed}} } -int* borrow test7(int* borrow p) { // expected-note {{previous borrow is here}} +int *borrow test7(int *borrow p) { int *owned oriPtr = safe_malloc(0); - p = &mut * oriPtr; - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} - return p; // expected-error {{Return value cannot be a borrow from local variable 'oriPtr'}} + p = &mut *oriPtr; // expected-error {{`*oriPtr` does not live long enough}} + // expected-note@-1 {{`*oriPtr` is borrowed here}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} + return p; // expected-note {{`*oriPtr` dropped here while still borrowed}} } -int* borrow test8(int *owned oriPtr, int* borrow p) { // expected-note {{previous borrow is here}} - p = &mut * oriPtr; - free_owned(oriPtr); // expected-error {{Can not modify 'oriPtr' because be borrowed}} - return p; // expected-error {{Return value cannot be a borrow from local variable 'oriPtr'}} +// FIXME: parameters lakes CFGScopeEnd +int *borrow test8(int *owned oriPtr, int *borrow p) { + p = &mut *oriPtr; // expected-note {{`*oriPtr` is borrowed here}} + // expected-error@-1 {{`*oriPtr` does not live long enough}} + free_owned(oriPtr); // expected-error {{cannot move out of `oriPtr` because it is borrowed}} + return p; // expected-note {{`*oriPtr` dropped here while still borrowed}} } int *borrow test9(int *borrow p) { int local = 42; if (local > 0) { - p = &mut local; - return &mut *p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + p = &mut local; // expected-error {{`local` does not live long enough}} + return &mut *p; // expected-note {{`local` dropped here while still borrowed}} } else return p; } @@ -62,10 +85,11 @@ int *borrow test9(int *borrow p) { int *borrow test10(int *borrow p) { int local = 42; if (local > 0) - return &mut local; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + return &mut local; // expected-error {{`local` does not live long enough}} + // expected-note@-1 {{`local` dropped here while still borrowed}} else { - p = &mut local; - return p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + p = &mut local; // expected-error {{`local` does not live long enough}} + return p; // expected-note {{`local` dropped here while still borrowed}} } } @@ -74,78 +98,70 @@ int *borrow test11(int *borrow p) { if (local > 0) { int local2 = 42; if (local2 > 0) - return &mut local2; //expected-error{{Return value cannot be a borrow from local variable 'local2'}} + return &mut local2; // expected-error {{`local2` does not live long enough}} + // expected-note@-1 {{`local2` dropped here while still borrowed}} else return p; } else { int local3 = 42; if (local3 > 0) - return &mut local3; //expected-error{{Return value cannot be a borrow from local variable 'local3'}} + return &mut local3; // expected-error {{`local3` does not live long enough}} + // expected-note@-1 {{`local3` dropped here while still borrowed}} else return p; } } -int* borrow test12(int* borrow p) { +int *borrow test12(int *borrow p) { int local = 42; int *owned oriPtr = safe_malloc(0); if (local > 0) { free_owned(oriPtr); - return &mut * oriPtr; // expected-error {{use of moved value: `oriPtr`}} + return &mut *oriPtr; // expected-error {{use of moved value: `oriPtr`}} } free_owned(oriPtr); return p; } -int* borrow test13(int* borrow p) { +int *borrow test13(int *borrow p) { int *owned oriPtr = safe_malloc(0); if (1) { free_owned(oriPtr); - return &mut * oriPtr; // expected-error {{use of moved value: `oriPtr`}} + return &mut *oriPtr; // expected-error {{use of moved value: `oriPtr`}} } - return &mut * oriPtr; //Dead Code + return &mut *oriPtr; } -struct S { int * borrow p; }; struct S test14(struct S s) { int local = 5; - s.p = &mut local; - return s; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + s.p = &mut local; // expected-error {{`local` does not live long enough}} + return s; // expected-note {{`local` dropped here while still borrowed}} } -struct G { struct S s; }; struct S test15(struct G g) { int local = 5; - g.s.p = &mut local; - return g.s; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + g.s.p = &mut local; // expected-error {{`local` does not live long enough}} + return g.s; // expected-note {{`local` dropped here while still borrowed}} } struct G test16(struct G g) { int local = 5; - g.s.p = &mut local; - return g; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + g.s.p = &mut local; // expected-error {{`local` does not live long enough}} + return g; // expected-note {{`local` dropped here while still borrowed}} } -int main() { - int local = 5; - int *owned oriPtr = safe_malloc(0); - test1(&mut local); - test2(&mut local); - test3(&mut local, 5); - test4(&mut local, 5); - test5(&mut local); - test6(&mut local); - test7(&mut local); - test8(oriPtr, &mut local); - test9(&mut local); - test10(&mut local); - test11(&mut local); - test12(&mut local); - test13(&mut local); - struct S s = { .p = &mut local }; - struct G g = { .s = s }; // expected-note {{previous borrow is here}} - test14(s); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - test15(g); - test16(g); - return 0; +int *borrow test17(int *borrow p, int *borrow q) { + int local = 42; + p = &mut local; // expected-error {{`local` does not live long enough}} + int a = 3; + q = &mut a; // expected-error {{`a` does not live long enough}} + return p; // expected-note {{`a` dropped here while still borrowed}} + // expected-note@-1 {{`local` dropped here while still borrowed}} +} + +int *borrow test18(int *borrow p) { + struct Q q; + struct Q *borrow q1 = &mut q; // expected-error {{`q` does not live long enough}} + int *borrow r = &mut *q1->g.b; + return r; // expected-note {{`q` dropped here while still borrowed}} } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow_loop/return_borrow_loop.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow_loop/return_borrow_loop.cbs index b5cbd07e7a9d561af1f946ee22f0e802e0884ba1..87d598b4439a93c6d6978517f926ff313dd21ef2 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow_loop/return_borrow_loop.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/return_borrow_loop/return_borrow_loop.cbs @@ -1,79 +1,81 @@ // RUN: %clang_cc1 -verify %s + +struct S { + int *borrow p; +}; + +struct G { + struct S s; +}; + int rand(); -T* owned safe_malloc(T value); -void free_owned(T* owned p); -void free(void*); +T *owned safe_malloc(T value); +void free_owned(T *owned p); +void free(void *); int *borrow test1(int *borrow p) { int local = 42; while (rand()) - return &mut local; //expected-error{{Return value cannot be a borrow from local variable 'local'}} - return &mut local; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + return &mut local; // expected-error {{`local` does not live long enough}} + // expected-note@-1 {{`local` dropped here while still borrowed}} + return &mut local; // expected-error {{`local` does not live long enough}} + // expected-note@-1 {{`local` dropped here while still borrowed}} } int *borrow test2(int *borrow p) { int local = 42; while (0) - return &mut local; - return &mut local; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + return &mut local; // expected-error {{`local` does not live long enough}} + // expected-note@-1 {{`local` dropped here while still borrowed}} + return &mut local; // expected-error {{`local` does not live long enough}} + // expected-note@-1 {{`local` dropped here while still borrowed}} } int *borrow test3(int *borrow p) { int local = 42; while (rand()) - p = &mut local; - return p; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + p = &mut local; // expected-error {{`local` does not live long enough}} + return p; // expected-note {{`local` dropped here while still borrowed}} } -int *borrow test4(int *borrow p) { //expected-error{{borrow pointer variable 'p' lives longer than target variable}} +int *borrow test4(int *borrow p) { while (rand()) { for (int i = 0; i < 5; i++) { if (rand()) { int local1 = 5; - p = &mut local1; - } else { + p = &mut local1; // expected-error {{`local1` does not live long enough}} + } else { // expected-note {{`local1` dropped here while still borrowed}} int local2 = 5; - p = &mut local2; - } + p = &mut local2; // expected-error {{`local2` does not live long enough}} + } // expected-note {{`local2` dropped here while still borrowed}} } } - return p; /* expected-error{{Return value cannot be a borrow from local variable 'local1'}} - expected-error{{Return value cannot be a borrow from local variable 'local2'}} */ + return p; } -struct S { int * borrow p; }; struct S test5(struct S s) { int local = 5; while (rand()) - s.p = &mut local; - return s; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + s.p = &mut local; // expected-error {{`local` does not live long enough}} + // expected-error@-1 {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-2 {{first mut borrow occurs here}} + return s; // expected-note {{`local` dropped here while still borrowed}} } -struct G { struct S s; }; struct S test6(struct G g) { int local = 5; while (rand()) - g.s.p = &mut local; - return g.s; //expected-error{{Return value cannot be a borrow from local variable 'local'}} + g.s.p = &mut local; // expected-error {{`local` does not live long enough}} + // expected-error@-1 {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-2 {{first mut borrow occurs here}} + return g.s; // expected-note {{`local` dropped here while still borrowed}} } struct G test7(struct G g) { int local = 5; while (rand()) - g.s.p = &mut local; - return g; //expected-error{{Return value cannot be a borrow from local variable 'local'}} -} - -int main() { - int local = 5; - test1(&mut local); - test2(&mut local); - test3(&mut local); - test4(&mut local); - struct S s = { .p = &mut local }; - struct G g = { .s = s }; // expected-note {{previous borrow is here}} - test5(s); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - test6(g); - test7(g); - return 0; + g.s.p = &mut local; // expected-error {{`local` does not live long enough}} + // expected-error@-1 {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-2 {{first mut borrow occurs here}} + return g; // expected-note {{`local` dropped here while still borrowed}} } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_has_own_struct_type_as_field/struct_has_own_struct_type_as_field.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_has_own_struct_type_as_field/struct_has_own_struct_type_as_field.cbs index 0d6ae04d905feeab213bb69c853e81e3199f65ad..675377a104b22743a614057813384e2f821697b3 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_has_own_struct_type_as_field/struct_has_own_struct_type_as_field.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_has_own_struct_type_as_field/struct_has_own_struct_type_as_field.cbs @@ -2,76 +2,86 @@ owned struct G; owned struct G { - public: - G* g; + public: + G *g; }; -void use_G_mut(G * borrow g); -void use_G_const(const G * borrow g); - -void test1(G g1) { - G g = { .g = &g1 }; - G *borrow p1 = &mut g; - const G *borrow p2 = &const g; - G *borrow p3 = &mut *g.g; - const G *borrow p4 = &const *g.g; - G *borrow p5 = &mut *g.g->g; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - const G *borrow p6 = &const *g.g->g; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - use_G_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'g' at the same time}} - use_G_const(p2); // expected-error {{There should be at most one mutable borrow targeting to 'g' at the same time}} - use_G_mut(p3); // expected-error {{There should be at most one mutable borrow targeting to 'g.g.g' at the same time}} - use_G_const(p4);// expected-error {{There should be at most one mutable borrow targeting to 'g.g.g' at the same time}} - use_G_mut(p5); // expected-error {{There should be at most one mutable borrow targeting to 'g.g.g' at the same time}} - use_G_const(p6); -} - -void test2(G g1) { - G g = { .g = &g1 }; - G *borrow p1 = &mut g; - G *borrow p2 = &mut *g.g; - G *borrow p3 = &mut *g.g->g; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - use_G_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'g' at the same time}} - use_G_mut(p2); // expected-error {{There should be at most one mutable borrow targeting to 'g.g.g' at the same time}} - use_G_mut(p3); -} - owned struct K; owned struct S { - public: - K* k; + public: + K *k; }; + owned struct K { - public: - S* s; + public: + S *s; }; -void use_S_mut(S * borrow s); -void use_S_const(const S * borrow s); -void use_K_mut(K * borrow k); -void use_K_const(const K * borrow k); +void use_G_mut(G *borrow g); +void use_G_const(const G *borrow g); +void use_S_mut(S *borrow s); +void use_S_const(const S *borrow s); +void use_K_mut(K *borrow k); +void use_K_const(const K *borrow k); + +void test1(G g1) { + G g = { .g = &g1 }; + G *borrow p1 = &mut g; // expected-note {{mutable borrow occurs here}} + // expected-note@-1 {{first mut borrow occurs here}} + // expected-note@-2 {{mutable borrow occurs here}} + // expected-note@-3 {{first mut borrow occurs here}} + // expected-note@-4 {{mutable borrow occurs here}} + const G *borrow p2 = &const g; // expected-error {{cannot borrow `g` as immutable because it is also borrowed as mutable}} + G *borrow p3 = &mut *g.g; // expected-error {{cannot borrow `*g.g` as mutable more than once at a time}} + const G *borrow p4 = &const *g.g; // expected-error {{cannot borrow `*g.g` as immutable because it is also borrowed as mutable}} + G *borrow p5 = &mut *g.g->g; // expected-error {{cannot borrow `*(*g.g).g` as mutable more than once at a time}} + const G *borrow p6 = &const *g.g->g; // expected-error {{cannot borrow `*(*g.g).g` as immutable because it is also borrowed as mutable}} + use_G_mut(p1); + use_G_const(p2); + use_G_mut(p3); + use_G_const(p4); + use_G_mut(p5); + use_G_const(p6); +} + +void test2(G g1) { + G g = { .g = &g1 }; + G *borrow p1 = &mut g; // expected-note {{first mut borrow occurs here}} + // expected-note@-1 {{first mut borrow occurs here}} + G *borrow p2 = &mut *g.g; // expected-error {{cannot borrow `*g.g` as mutable more than once at a time}} + G *borrow p3 = &mut *g.g->g; // expected-error {{cannot borrow `*(*g.g).g` as mutable more than once at a time}} + use_G_mut(p1); + use_G_mut(p2); + use_G_mut(p3); +} void test3(S s1) { - K k = { .s = &s1 }; - K *borrow p1 = &mut k; - const K *borrow p2 = &const k; - S *borrow p3 = &mut *k.s; - const S *borrow p4 = &const *k.s; - K *borrow p5 = &mut *k.s->k;// expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - const K *borrow p6 = &const *k.s->k;// expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - use_K_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'k' at the same time}} - use_K_const(p2);// expected-error {{There should be at most one mutable borrow targeting to 'k' at the same time}} - use_S_mut(p3); // expected-error {{There should be at most one mutable borrow targeting to 'k.s.k' at the same time}} - use_S_const(p4);// expected-error {{There should be at most one mutable borrow targeting to 'k.s.k' at the same time}} - use_K_mut(p5); // expected-error {{There should be at most one mutable borrow targeting to 'k.s.k' at the same time}} - use_K_const(p6); + K k = { .s = &s1 }; + K *borrow p1 = &mut k; // expected-note {{mutable borrow occurs here}} + // expected-note@-1 {{first mut borrow occurs here}} + // expected-note@-2 {{mutable borrow occurs here}} + // expected-note@-3 {{first mut borrow occurs here}} + // expected-note@-4 {{mutable borrow occurs here}} + const K *borrow p2 = &const k; // expected-error {{cannot borrow `k` as immutable because it is also borrowed as mutable}} + S *borrow p3 = &mut *k.s; // expected-error {{cannot borrow `*k.s` as mutable more than once at a time}} + const S *borrow p4 = &const *k.s; // expected-error {{cannot borrow `*k.s` as immutable because it is also borrowed as mutable}} + K *borrow p5 = &mut *k.s->k; // expected-error {{cannot borrow `*(*k.s).k` as mutable more than once at a time}} + const K *borrow p6 = &const *k.s->k; // expected-error {{cannot borrow `*(*k.s).k` as immutable because it is also borrowed as mutable}} + use_K_mut(p1); + use_K_const(p2); + use_S_mut(p3); + use_S_const(p4); + use_K_mut(p5); + use_K_const(p6); } void test4(S s1) { - K k = { .s = &s1 }; - K *borrow p1 = &mut k; - S *borrow p2 = &mut *k.s; - K *borrow p3 = &mut *k.s->k;// expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - use_K_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'k' at the same time}} - use_S_mut(p2); // expected-error {{There should be at most one mutable borrow targeting to 'k.s.k' at the same time}} - use_K_mut(p3); + K k = { .s = &s1 }; + K *borrow p1 = &mut k; // expected-note {{first mut borrow occurs here}} + // expected-note@-1 {{first mut borrow occurs here}} + S *borrow p2 = &mut *k.s; // expected-error {{cannot borrow `*k.s` as mutable more than once at a time}} + K *borrow p3 = &mut *k.s->k; // expected-error {{cannot borrow `*(*k.s).k` as mutable more than once at a time}} + use_K_mut(p1); + use_S_mut(p2); + use_K_mut(p3); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_with_borrow_fields/struct_with_borrow_fields.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_with_borrow_fields/struct_with_borrow_fields.cbs new file mode 100644 index 0000000000000000000000000000000000000000..9b9d001e75a07297a7873fb89d0197e0838418ec --- /dev/null +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/struct_with_borrow_fields/struct_with_borrow_fields.cbs @@ -0,0 +1,181 @@ +// RUN: %clang_cc1 -verify %s + +typedef struct A { + int *borrow p; +} A; + +typedef struct B { + int m; + int n; +} B; + +typedef struct C { + B b; + int *borrow p; +} C; + +typedef struct D { + B b; + int *borrow p; + int *borrow q; +} D; + +typedef struct E { + C c; + int x; + int *borrow p; +} E; + +void use_mut(int *borrow); +void useE(E); +void useC(C); + +void test1() { + int local = 42; + A a = { .p = &mut local }; // expected-note {{`local` is borrowed here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + use_mut(a.p); +} + +void test2(A a) { + int local = 42; + a.p = &mut local; // expected-note {{`local` is borrowed here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + int x = *a.p; +} + +void test3() { + int local = 42; + C c = { .p = &mut local }; // expected-note {{`local` is borrowed here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + int temp = c.b.m; +} + +void test4(C c) { + int local = 42; + c = (struct C) { .p = &mut local }; // expected-note {{`local` is borrowed here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + int temp = c.b.m; +} + +void test5() { + int local = 42; + D d = { .p = &mut local, .q = &mut local }; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-1 {{`local` is borrowed here}} + // expected-note@-2 {{first mut borrow occurs here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + int temp = d.b.m; +} + +void test6() { + int local = 42; + E e = { .c = (struct C) { .p = &mut local }, .p = &mut local }; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + // expected-note@-1 {{`local` is borrowed here}} + // expected-note@-2 {{first mut borrow occurs here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + int temp = e.c.b.m; +} + +void test7(A a) { + { + int local = 42; + a = (struct A) { .p = &mut local }; // expected-error {{`local` does not live long enough}} + } // expected-note {{`local` dropped here while still borrowed}} + use_mut(a.p); +} + +void test8(A a) { + { + int local = 42; + a = (struct A) { .p = &mut local }; // expected-note {{`local` is borrowed here}} + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + } + use_mut(a.p); +} + +void test9(A a1) { + int local = 42; + A a2 = { .p = &mut local }; // expected-note {{`local` is borrowed here}} + a1 = a2; + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + use_mut(a1.p); +} + +void test10() { + int local = 42; + A a1 = { .p = &mut local }; // expected-note {{`local` is borrowed here}} + A a2 = a1; + local = 43; // expected-error {{cannot assign to `local` because it is borrowed}} + use_mut(a2.p); +} + +void test11() { + int local = 42; + C c = { .p = &mut local }; + int *borrow q = c.p; // expected-note {{`*c.p` is borrowed here}} + int temp = *c.p; // expected-error {{cannot use `*c.p` because it was mutably borrowed}} + use_mut(q); +} + +void test12() { + int local = 42; + C c1 = { .p = &mut local }; + C c2 = c1; // expected-note {{`*c1.p` is borrowed here}} + int temp = *c1.p; // expected-error {{cannot use `*c1.p` because it was mutably borrowed}} + use_mut(c2.p); +} + +void test13() { + int local1 = 42; + int local2 = 42; + E e1 = { .c = (struct C) { .p = &mut local1 }, .p = &mut local2 }; + E e2 = e1; // expected-note {{`*e1.c.p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + // expected-note@-2 {{first mut borrow occurs here}} + E temp = e1; // expected-error {{cannot use `e1` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*e1.c.p` as mutable more than once at a time}} + // expected-error@-2 {{cannot borrow `*e1.p` as mutable more than once at a time}} + useE(e2); +} + +void test14() { + int local1 = 42; + int local2 = 42; + E e1 = { .c = (struct C) { .p = &mut local1 }, .p = &mut local2 }; + C c = e1.c; + useE(e1); +} + +void test15() { + int local1 = 42; + int local2 = 42; + int local3 = 42; + E e1 = { .c = (struct C) { .p = &mut local1 }, .p = &mut local2 }; + C c = e1.c; + e1.c = (struct C) { .p = &mut local3 }; + useE(e1); +} + +void test16() { + int local1 = 42; + int local2 = 42; + E e1 = { .c = (struct C) { .p = &mut local1 }, .p = &mut local2 }; + int *borrow p = e1.p; + useE(e1); +} + +void test17() { + int local1 = 42; + int local2 = 42; + E e = { .c = (struct C) { .p = &mut local1 }, .p = &mut local2 }; + C c = e.c; // expected-note {{`*e.c.p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + int *borrow p1 = e.p; // expected-note {{`*e.p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + int *borrow p2 = e.p; // expected-error {{cannot use `e.p` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*e.p` as mutable more than once at a time}} + use_mut(p1); + useE(e); // expected-error {{cannot use `e` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*e.c.p` as mutable more than once at a time}} + useC(c); +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/target_is_raw_pointer/target_is_raw_pointer.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/target_is_raw_pointer/target_is_raw_pointer.cbs similarity index 33% rename from clang/test/BSC/Positive/Ownership/borrow_check_rules/target_is_raw_pointer/target_is_raw_pointer.cbs rename to clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/target_is_raw_pointer/target_is_raw_pointer.cbs index 940b493a6a0d7222ca1faa36fb8976928aaa750a..f4a1cfd4bab22c66656419210a5933927fef4d0e 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/target_is_raw_pointer/target_is_raw_pointer.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/target_is_raw_pointer/target_is_raw_pointer.cbs @@ -1,27 +1,35 @@ -// RUN: %clang %s -o %test.output -// RUN: %test.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 +// RUN: %clang_cc1 -verify %s + +struct N { + int b; + int *owned c; +}; + +struct M { + int *a; + struct N *n; +}; + +struct K { + int *borrow p; +}; + +void use_mut(int *borrow p) {} -void use_mut(int* borrow p) {} -struct N { int b; int *owned c; }; -struct M { int* a; struct N* n; }; -struct K { int* borrow p; }; void test() { int a = 5; int *borrow p1 = &mut a; int *borrow p2 = &mut a; - struct K k = { .p = &mut a }; - int* owned o = (int* owned)&a; + struct K k = { .p = &mut a }; // expected-note {{`a` is borrowed here}} + // expected-note@-1 {{`a` is borrowed here}} + int *owned o = (int* owned)&a; // expected-error {{cannot use `a` because it was mutably borrowed}} { struct N n = { .b = 5, .c = o }; - struct M m = { .a = &a, .n = &n }; + struct M m = { .a = &a, .n = &n }; // expected-error {{cannot use `a` because it was mutably borrowed}} p1 = &mut *m.a; p2 = &mut m.n->b; k.p = &mut *m.n->c; - int * d = (int*)n.c; + int *d = (int *)n.c; } use_mut(p1); use_mut(k.p); diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/use_borrow_in_condition/use_borrow_in_condition.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/use_borrow_in_condition/use_borrow_in_condition.cbs index 75215394becd2c83fb3c504ac8a01337baca5c2c..a6e95ab638cb04850453e20e582439281dc9959c 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/use_borrow_in_condition/use_borrow_in_condition.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/use_borrow_in_condition/use_borrow_in_condition.cbs @@ -1,142 +1,150 @@ -// RUN: %clang_cc1 -ast-dump -verify %s +// RUN: %clang_cc1 -verify %s -void use_mut(int *borrow p) {} -void use_immut(const int *borrow p) {} -T* owned safe_malloc(T value); -void free_owned(T* owned p); -void free(void*); +struct S { + int a; + int *owned b; +}; + +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +T *owned safe_malloc(T value); +void free_owned(T *owned p); void test1() { - int e = 1; - const int * borrow p1 = &const e; // expected-note {{previous borrow is here}} - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - if (*p1 == 2) { - e = 5; - } + int e = 1; + const int *borrow p1 = &const e; // expected-note {{`e` is borrowed here}} + e = 2; // expected-error {{cannot assign to `e` because it is borrowed}} + if (*p1 == 2) { + e = 5; + } - int f = 1; - const int * borrow p2 = &const f; // expected-note {{previous borrow is here}} - f = 2; //expected-error{{Can not modify 'f' because be borrowed}} - if (*p2) { - f = 5; - } + int f = 1; + const int *borrow p2 = &const f; // expected-note {{`f` is borrowed here}} + f = 2; // expected-error {{cannot assign to `f` because it is borrowed}} + if (*p2) { + f = 5; + } - int g = 1; - const int * borrow p3 = &const g; // expected-note {{previous borrow is here}} - g = 2; //expected-error{{Can not modify 'g' because be borrowed}} - if (*p3 < 0 || *p3 > 5 || *p3 + *p3 > 10) { - g = 5; - } + int g = 1; + const int *borrow p3 = &const g; // expected-note {{`g` is borrowed here}} + g = 2; // expected-error {{cannot assign to `g` because it is borrowed}} + if (*p3 < 0 || *p3 > 5 || *p3 + *p3 > 10) { + g = 5; + } - int h = 1; - int * borrow p4 = &mut h; // expected-note {{previous borrow is here}} - int k = h; //expected-error{{Can not read 'h' because be mut borrowed}} - if (*p4) { - h = 5; - } + int h = 1; + int *borrow p4 = &mut h; // expected-note {{`h` is borrowed here}} + int k = h; // expected-error {{cannot use `h` because it was mutably borrowed}} + if (*p4) { + h = 5; + } } -struct S { - int a; - int *owned b; -}; void test2() { - int local = 5; - struct S s = { .a = 1, .b = safe_malloc(1) }; - const struct S* borrow p = &const s;// expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - s.a = 2; //expected-error{{Can not modify 's.a' because be borrowed}} - free_owned(s.b); //expected-error{{Can not modify 's.b' because be borrowed}} - if (p->a == 2 && *(p->b) == 2) { - s.a = 5; - } + int local = 5; + struct S s = { .a = 1, .b = safe_malloc(1) }; + const struct S *borrow p = &const s; // expected-note {{`s.a` is borrowed here}} + // expected-note@-1 {{`s` is borrowed here}} + s.a = 2; // expected-error {{cannot assign to `s.a` because it is borrowed}} + free_owned(s.b); // expected-error {{cannot move out of `s.b` because it is borrowed}} + if (p->a == 2 && *(p->b) == 2) { + s.a = 5; + } } void test3() { - int e = 1; - const int * borrow p = &const e; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - while (*p > 0) { - e--; //expected-error{{Can not modify 'e' because be borrowed}} - } + int e = 1; + int *borrow p = &mut e; // expected-note {{`e` is borrowed here}} + // expected-note@-1 {{`e` is borrowed here}} + while (*p > 0) { + --e; // expected-error {{cannot assign to `e` because it is borrowed}} + // expected-error@-1 {{cannot use `e` because it was mutably borrowed}} + } } void test4() { - int e = 1; - const int * borrow p = &const e; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - for (int i = 0; i < *p; i++) { - e--; //expected-error{{Can not modify 'e' because be borrowed}} - } + int e = 1; + const int *borrow p = &const e; // expected-note {{`e` is borrowed here}} + for (int i = 0; i < *p; ++i) { + --e; // expected-error {{cannot assign to `e` because it is borrowed}} + } } void test5() { - int e = 1; - const int * borrow p = &const e; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - do { - e--; //expected-error{{Can not modify 'e' because be borrowed}} - } while (*p > 0); + int e = 1; + const int *borrow p = &const e; // expected-note {{`e` is borrowed here}} + e = 2; // expected-error {{cannot assign to `e` because it is borrowed}} + do { + --e; + } while (*p > 0); } void test6() { - int e = 1; - int f = 2; - const int * borrow p1 = &const e; // expected-note {{previous borrow is here}} - const int * borrow p2 = &const f; - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - for ( ; ; ) { - while (p2 == p1) - return; - } + int e = 1; + int f = 2; + const int *borrow p1 = &const e; // expected-note {{`e` is borrowed here}} + const int *borrow p2 = &const f; + e = 2; // expected-error {{cannot assign to `e` because it is borrowed}} + for ( ; ; ) { + while (p2 == p1) + return; + } } void test7() { - int e = 1; - int f = 2; - const int * borrow p1 = &const e; // expected-note {{previous borrow is here}} - const int * borrow p2 = &const f; - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - for ( ; ; ) { - while (p2 == p1) - return; - } + int e = 1; + const int *borrow p = &const e; // expected-note {{`e` is borrowed here}} + e = 2; // expected-error {{cannot assign to `e` because it is borrowed}} + while (*p > 0) { + --e; + } } void test8() { - int e = 1; - int f = 2; - const int * borrow p1 = &const e; // expected-note {{previous borrow is here}} - const int * borrow p2 = &const f; // expected-note {{previous borrow is here}} - e = 2; //expected-error{{Can not modify 'e' because be borrowed}} - for (p2 = p1; *p2 > 0; ) { - e--; //expected-error{{Can not modify 'e' because be borrowed}} - } + int e = 1; + int f = 2; + const int *borrow p1 = &const e; // expected-note {{`e` is borrowed here}} + const int *borrow p2 = &const f; + for (p2 = p1; *p2 > 0; ) { + --e; // expected-error {{cannot assign to `e` because it is borrowed}} + } } void test9() { - int e = 1; - int f = 2; - const int * borrow p1 = &const e; - const int * borrow p2 = &const f; // expected-note {{previous borrow is here}} - for ( ; e = 2, f = 2; ) { //expected-error{{Can not modify 'f' because be borrowed}} - if (e) { - return; - } else { - p1 = p2; - } + int e = 1; + int f = 2; + const int *borrow p1 = &const e; + const int *borrow p2 = &const f; // expected-note {{`f` is borrowed here}} + for ( ; e = 2, f = 2; ) { // expected-error {{cannot assign to `f` because it is borrowed}} + if (e) { + return; + } else { + p1 = p2; } + } } void test10() { - int e = 1; - int f = 2; - const int * borrow p1 = &const e; // expected-note {{previous borrow is here}} - const int * borrow p2 = &const f; // expected-note {{previous borrow is here}} - for ( ; e = 2, f = 2; ) { //expected-error{{Can not modify 'f' because be borrowed}} expected-error{{Can not modify 'e' because be borrowed}} - if (p1 == p2) { - return; - } else { - p1 = p2; - } + int e = 1; + int f = 2; + const int *borrow p1 = &const e; // expected-note {{`e` is borrowed here}} + const int *borrow p2 = &const f; // expected-note {{`f` is borrowed here}} + for ( ; e = 2, f = 2; ) { // expected-error {{cannot assign to `e` because it is borrowed}} + // expected-error@-1 {{cannot assign to `f` because it is borrowed}} + if (p1 == p2) { + return; + } else { + p1 = p2; } + } +} + +void test11() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = &mut *p; + while (1) { + use_mut(p); + } + use_mut(q); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_field/var_borrow_field.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_field/var_borrow_field.cbs index 18697a85ad769ccc5dc72d31f0495564d915e031..66e58b2d8c312987cc1662d1c6ef3b56afe29393 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_field/var_borrow_field.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_field/var_borrow_field.cbs @@ -1,38 +1,64 @@ // RUN: %clang_cc1 -verify %s +#define NULL ((void *)0) + typedef struct S { - int a; - int b; + int a; + int b; } S; -void use_mutI(int * borrow); -void use_constI(const int * borrow); -void use_mutS(S * borrow); -void use_constS(const S * borrow); +typedef struct Foo { + int *borrow field; +} Foo; + +void use_mutI(int *borrow); +void use_constI(const int *borrow); +void use_mutS(S *borrow); +void use_constS(const S *borrow); T* owned safe_malloc(T value); void free_owned(T* owned p); -void free(void*); +void free(void *); void test1() { - S local = {.a = 1, .b = 2}; - int * borrow p = &mut local.a; - S * borrow s = &mut local; // expected-note {{previous borrow is here}} - use_mutI(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_mutS(s); + S local = { .a = 1, .b = 2 }; + int *borrow p = &mut local.a; // expected-note {{first mut borrow occurs here}} + S *borrow s = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + use_mutI(p); + use_mutS(s); } void test2() { - S local = {.a = 1, .b = 2}; - const int * borrow p = &const local.a; - S * borrow s = &mut local; // expected-note {{previous borrow is here}} - use_constI(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_mutS(s); + S local = { .a = 1, .b = 2 }; + const int *borrow p = &const local.a; // expected-note {{immutable borrow occurs here}} + S *borrow s = &mut local; // expected-error {{cannot borrow `local` as mutable because it is also borrowed as immutable}} + use_constI(p); + use_mutS(s); } void test3() { - S local = {.a = 1, .b = 2}; - int * borrow p = &mut local.a; - const S * borrow s = &const local; // expected-note {{previous borrow is here}} - use_mutI(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_constS(s); + S local = { .a = 1, .b = 2 }; + int *borrow p = &mut local.a; // expected-note {{mutable borrow occurs here}} + const S *borrow s = &const local; // expected-error {{cannot borrow `local` as immutable because it is also borrowed as mutable}} + use_mutI(p); + use_constS(s); +} + +void test4(Foo a) { + int *borrow b = &mut *a.field; // expected-note {{`*a.field` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + Foo temp = a; // expected-error {{cannot use `a` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*a.field` as mutable more than once at a time}} + use_mutI(b); +} + +void test5(Foo a) { + int *borrow b = &mut *a.field; + a = (Foo) { .field = (int *borrow)NULL }; + use_mutI(b); +} + +void test6(Foo a) { + const int *borrow b = &const *a.field; + a = (Foo) { .field = (int *borrow)NULL }; + use_constI(b); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_struct/var_borrow_struct.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_struct/var_borrow_struct.cbs index 71c2b91dd430e3b47158d841791e54bf4eb07462..7da40fbe7984873c51c7b5976946629f775ad569 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_struct/var_borrow_struct.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_check_rules/var_borrow_struct/var_borrow_struct.cbs @@ -1,98 +1,106 @@ // RUN: %clang_cc1 -verify %s typedef struct S { - int a; - int b; + int a; + int b; } S; -void use_mut(S * borrow s); -void use_const(const S * borrow s); -T* owned safe_malloc(T value); -void free_owned(T* owned p); +void use_mut(S *borrow s); +void use_const(const S *borrow s); +T *owned safe_malloc(T value); +void free_owned(T *owned p); void free(void*); void test1() { - S s = { .a = 1, .b = 2 }; - S * borrow p = &mut s; - S * borrow q = &mut s; // expected-note {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 's' at the same time}} - use_mut(q); + S s = { .a = 1, .b = 2 }; + S *borrow p = &mut s; // expected-note {{first mut borrow occurs here}} + S *borrow q = &mut s; // expected-error {{cannot borrow `s` as mutable more than once at a time}} + use_mut(p); + use_mut(q); } void test2() { S tmp = {.a = 1, .b = 2}; S *owned oriPtr = safe_malloc(tmp); - S *borrow p1 = &mut *oriPtr; - S *borrow p2 = &mut *oriPtr; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'oriPtr' at the same time}} + S *borrow p1 = &mut *oriPtr; // expected-note {{first mut borrow occurs here}} + S *borrow p2 = &mut *oriPtr; // expected-error {{cannot borrow `*oriPtr` as mutable more than once at a time}} + use_mut(p1); use_mut(p2); free_owned(oriPtr); } -void test3(S * oriPtr) { - S *borrow p1 = &mut * oriPtr; - S *borrow p2 = &mut * oriPtr; +void test3(S *oriPtr) { + S *borrow p1 = &mut *oriPtr; // expected-note {{first mut borrow occurs here}} + S *borrow p2 = &mut *oriPtr; // expected-error {{cannot borrow `*oriPtr` as mutable more than once at a time}} use_mut(p1); free(oriPtr); } void test4() { - S local = {.a = 1, .b = 2}; - S *borrow p = &mut local; - S *borrow p1 = &mut local; - const S *borrow p2 = &const *p; // expected-note {{previous borrow is here}} - // expected-note@-1 {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} + S local = { .a = 1, .b = 2 }; + S *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + S *borrow p1 = &mut local; // expected-error {{cannot borrow `local` as mutable more than once at a time}} + const S *borrow p2 = &const *p; // expected-note {{immutable borrow occurs here}} + use_mut(p); // expected-error {{cannot borrow `*p` as mutable because it is also borrowed as immutable}} + use_mut(p1); use_const(p2); } void test5() { - S local = {.a = 1, .b = 2}; - S *borrow p1 = &mut local; - const S *borrow p2 = &const local; // expected-note {{previous borrow is here}} - use_mut(p1); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} + S local = { .a = 1, .b = 2 }; + S *borrow p1 = &mut local; // expected-note {{mutable borrow occurs here}} + const S *borrow p2 = &const local; // expected-error {{cannot borrow `local` as immutable because it is also borrowed as mutable}} + use_mut(p1); use_const(p2); } -void test6(S* borrow p) { - S local = {.a = 1, .b = 2}; +void test6(S *borrow p) { + S local = { .a = 1, .b = 2 }; p = &mut local; - const S *borrow p1 = &const *p; // expected-note {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'local' at the same time}} + const S *borrow p1 = &const *p; // expected-note {{immutable borrow occurs here}} + use_mut(p); // expected-error {{cannot borrow `*p` as mutable because it is also borrowed as immutable}} use_const(p1); } -void test7(S* borrow p) { - S *borrow p1 = p; // expected-note {{previous borrow is here}} - use_mut(p); // expected-error {{There should be at most one mutable borrow targeting to 'p' at the same time}} +void test7(S *borrow p) { + S *borrow p1 = p; // expected-note {{`*p` is borrowed here}} + // expected-note@-1 {{first mut borrow occurs here}} + use_mut(p); // expected-error {{cannot use `p` because it was mutably borrowed}} + // expected-error@-1 {{cannot borrow `*p` as mutable more than once at a time}} use_mut(p1); } void test8() { - S local = {.a = 1, .b = 2}; - S * borrow p = &mut local; // expected-note {{previous borrow is here}} - local.b = 3; // expected-error {{Can not modify 'local.b' because be borrowed}} + S local = { .a = 1, .b = 2 }; + S *borrow p = &mut local; // expected-note {{`local.b` is borrowed here}} + local.b = 3; // expected-error {{cannot assign to `local.b` because it is borrowed}} use_mut(p); } void test9() { - S local = {.a = 1, .b = 2}; - const S * borrow p = &const local; // expected-note {{previous borrow is here}} - local.b = 3; // expected-error {{Can not modify 'local.b' because be borrowed}} + S local = { .a = 1, .b = 2}; + const S *borrow p = &const local; // expected-note {{`local.b` is borrowed here}} + local.b = 3; // expected-error {{cannot assign to `local.b` because it is borrowed}} use_const(p); } void test10() { - S local = {.a = 1, .b = 2}; - S * borrow p = &mut local; // expected-note {{previous borrow is here}} - int x = local.b; // expected-error {{Can not modify 'local.b' because be borrowed}} + S local = { .a = 1, .b = 2 }; + S *borrow p = &mut local; // expected-note {{`local` is borrowed here}} + int x = local.b; // expected-error {{cannot use `local.b` because it was mutably borrowed}} use_mut(p); } void test11() { - S local = {.a = 1, .b = 2}; - int * borrow p1 = &mut local.a; // expected-note {{previous borrow is here}} - local.a = 5; // expected-error {{Can not modify 'local.a' because be borrowed}} - int * borrow p2 = p1; + S local = { .a = 1, .b = 2 }; + int *borrow p1 = &mut local.a; // expected-note {{`local.a` is borrowed here}} + local.a = 5; // expected-error {{cannot assign to `local.a` because it is borrowed}} + int *borrow p2 = p1; +} + +void test12() { + S foo = { .a = 1, .b = 2 }; + S *borrow p = &mut foo; + use_mut(p); + S temp = foo; } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_arr_freeze/borrow_arr_freeze.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_arr_freeze/borrow_arr_freeze.cbs index b00d2d0f5a97e7b95422107a4f52a442a5efbd73..e3e6186b89fac809862c983a7f126b8165deac47 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_arr_freeze/borrow_arr_freeze.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_arr_freeze/borrow_arr_freeze.cbs @@ -4,16 +4,14 @@ void test1(int *p) {} void test_freeze_arrow_var1() { int arr[10] = {0}; - int * borrow c = &mut arr[1]; // expected-note {{previous borrow is here}} - // expected-note@-1 {{previous borrow is here}} - // expected-note@-2 {{previous borrow is here}} - // expected-note@-3 {{previous borrow is here}} - // expected-note@-4 {{previous borrow is here}} - int a = arr[1]; // expected-error {{Can not read 'arr' because be mut borrowed}} - int b = arr[2]; // expected-error {{Can not read 'arr' because be mut borrowed}} - arr[1] = 2; // expected-error {{Can not modify 'arr' because be borrowed}} - arr[2] = 2; // expected-error {{Can not modify 'arr' because be borrowed}} - test1(arr); // expected-error {{Can not modify 'arr' because be borrowed}} + int * borrow c = &mut arr[1]; // expected-note {{`arr` is borrowed here}} + // expected-note@-1 {{`arr` is borrowed here}} + // expected-note@-2 {{`arr` is borrowed here}} + int a = arr[1]; // expected-error {{cannot use `arr` because it was mutably borrowed}} + int b = arr[2]; // expected-error {{cannot use `arr` because it was mutably borrowed}} + arr[1] = 2; // expected-error {{cannot assign to `arr` because it is borrowed}} + arr[2] = 2; + test1(arr); int arr2[10] = {}; arr2[arr[1]] = 5; int m = *c; @@ -23,22 +21,19 @@ void test2(int p[10][10]) {} void test_freeze_arrow_var2() { int arr[10][10] = {0}; - int * borrow k = &mut arr[1][1]; // expected-note {{previous borrow is here}} - // expected-note@-1 {{previous borrow is here}} - // expected-note@-2 {{previous borrow is here}} - // expected-note@-3 {{previous borrow is here}} - // expected-note@-4 {{previous borrow is here}} - // expected-note@-5 {{previous borrow is here}} - // expected-note@-6 {{previous borrow is here}} - // expected-note@-7 {{previous borrow is here}} - int a = arr[1][1]; // expected-error {{Can not read 'arr' because be mut borrowed}} - int b = arr[1][2]; // expected-error {{Can not read 'arr' because be mut borrowed}} - int c = arr[2][2]; // expected-error {{Can not read 'arr' because be mut borrowed}} - test1(arr[1]); // expected-error {{Can not modify 'arr' because be borrowed}} - arr[1][1] = 2; // expected-error {{Can not modify 'arr' because be borrowed}} - arr[1][2] = 2; // expected-error {{Can not modify 'arr' because be borrowed}} - arr[2][2] = 2; // expected-error {{Can not modify 'arr' because be borrowed}} - test2(arr); // expected-error {{Can not modify 'arr' because be borrowed}} + int * borrow k = &mut arr[1][1]; // expected-note {{`arr` is borrowed here}} + // expected-note@-1 {{`arr` is borrowed here}} + // expected-note@-2 {{`arr` is borrowed here}} + // expected-note@-3 {{`arr` is borrowed here}} + // expected-note@-4 {{`arr` is borrowed here}} + int a = arr[1][1]; // expected-error {{cannot use `arr` because it was mutably borrowed}} + int b = arr[1][2]; // expected-error {{cannot use `arr` because it was mutably borrowed}} + int c = arr[2][2]; // expected-error {{cannot use `arr` because it was mutably borrowed}} + test1(arr[1]); // expected-error {{cannot use `arr` because it was mutably borrowed}} + arr[1][1] = 2; // expected-error {{cannot assign to `arr` because it is borrowed}} + arr[1][2] = 2; + arr[2][2] = 2; + test2(arr); int m = *k; } diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_ident_freeze/borrow_ident_freeze.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_ident_freeze/borrow_ident_freeze.cbs index ce997bcece218004f76862671d757ee65e1d6cab..6fcbbc2d310064231b9ec96ec480821e6016cb59 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_ident_freeze/borrow_ident_freeze.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_ident_freeze/borrow_ident_freeze.cbs @@ -1,140 +1,140 @@ // RUN: %clang_cc1 -ast-dump -verify %s void test_freeze_ident1() { - int a = 1; - int * borrow c = &mut a; - // we can modify "a" when c life time is end; - a = 2; + int a = 1; + int *borrow c = &mut a; + // we can modify "a" when c life time is end; + a = 2; } void test_freeze_ident2() { - int a = 1; - int * borrow c = &mut a; // expected-note {{previous borrow is here}} - a = 2; // expected-error {{Can not modify 'a' because be borrowed}} - int d = *c; + int a = 1; + int *borrow c = &mut a; // expected-note {{`a` is borrowed here}} + a = 2; // expected-error {{cannot assign to `a` because it is borrowed}} + int d = *c; } void test_freeze_ident2_0() { - int a = 1; - int * borrow c = &mut a; // expected-note {{previous borrow is here}} - int m = a; // expected-error {{Can not read 'a' because be mut borrowed}} - int d = *c; + int a = 1; + int *borrow c = &mut a; // expected-note {{`a` is borrowed here}} + int m = a; // expected-error {{cannot use `a` because it was mutably borrowed}} + int d = *c; } void test_freeze_ident3() { - int a = 1; - int b = 2; - int * borrow c = &mut a; - // "c" life time is end at before, and reactivate at after; - a = 2; - c = &mut b; + int a = 1; + int b = 2; + int *borrow c = &mut a; + // "c" life time is end at before, and reactivate at after; + a = 2; + c = &mut b; } void test_freeze_ident4() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = &mut a; + int a = 1; + int *borrow c = &mut a; + int *borrow d = &mut a; } void test_freeze_ident5() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = &mut a; - int e = *c; + int a = 1; + int *borrow c = &mut a; // expected-note {{first mut borrow occurs here}} + int *borrow d = &mut a; // expected-error {{cannot borrow `a` as mutable more than once at a time}} + int e = *c; } void test_freeze_ident6() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = &mut a; - int e = *d; + int a = 1; + int *borrow c = &mut a; + int *borrow d = &mut a; + int e = *d; } void test_freeze_ident6_0() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = &mut a; - int e = *d; - int f = *c; // expected-error {{Can not use 'c' because expired}} + int a = 1; + int *borrow c = &mut a; // expected-note {{first mut borrow occurs here}} + int *borrow d = &mut a; // expected-error {{cannot borrow `a` as mutable more than once at a time}} + int e = *d; + int f = *c; } void test_freeze_ident6_1() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = &mut a; // expected-note {{previous borrow is here}} - int e = *c; // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - int f = *d; + int a = 1; + int *borrow c = &mut a; // expected-note {{first mut borrow occurs here}} + int *borrow d = &mut a; // expected-error {{cannot borrow `a` as mutable more than once at a time}} + int e = *c; + int f = *d; } void test_freeze_ident6_2() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = c; // expected-note {{previous borrow is here}} - int e = *c; // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - int f = *d; + int a = 1; + int *borrow c = &mut a; + int *borrow d = c; // expected-note {{`*c` is borrowed here}} + int e = *c; // expected-error {{cannot use `*c` because it was mutably borrowed}} + int f = *d; } -void use(int * borrow a) {} +void use(int *borrow a) {} void test_freeze_ident6_3() { - int a = 1; - int * borrow c = &mut a; - int * borrow d = c; // expected-note {{previous borrow is here}} - int e = *c; // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - use(d); + int a = 1; + int *borrow c = &mut a; + int *borrow d = c; // expected-note {{`*c` is borrowed here}} + int e = *c; // expected-error {{cannot use `*c` because it was mutably borrowed}} + use(d); } void test_freeze_ident7() { - int a = 1; - int * borrow c = &mut a; - const int * borrow d = &const a; + int a = 1; + int *borrow c = &mut a; + const int *borrow d = &const a; } void test_freeze_ident8() { - int a = 1; - int * borrow c = &mut a; - const int * borrow d = &const a; - int e = *c; + int a = 1; + int *borrow c = &mut a; // expected-note {{mutable borrow occurs here}} + const int *borrow d = &const a; // expected-error {{cannot borrow `a` as immutable because it is also borrowed as mutable}} + int e = *c; } void test_freeze_ident9() { - int a = 1; - int * borrow c = &mut a; - const int * borrow d = &const a; - int e = *d; + int a = 1; + int *borrow c = &mut a; + const int *borrow d = &const a; + int e = *d; } void test_freeze_ident10() { - int a = 1; - int * borrow c = &mut a; - const int * borrow d = &const a; - int b = *d; - int e = *c; + int a = 1; + int *borrow c = &mut a; // expected-note {{mutable borrow occurs here}} + const int *borrow d = &const a; // expected-error {{cannot borrow `a` as immutable because it is also borrowed as mutable}} + int b = *d; + int e = *c; } -void const_use(const int * borrow a) {}; +void const_use(const int *borrow a) {}; void test_freeze_ident11() { - int a = 1; - int * borrow c = &mut a; - const int * borrow d = &const a; // expected-note {{previous borrow is here}} - int b = *c; // expected-error {{There should be at most one mutable borrow targeting to 'a' at the same time}} - const_use(d); + int a = 1; + int *borrow c = &mut a; // expected-note {{mutable borrow occurs here}} + const int *borrow d = &const a; // expected-error {{cannot borrow `a` as immutable because it is also borrowed as mutable}} + int b = *c; + const_use(d); } void test_freeze_ident12() { - int a = 1; - const int * borrow c = &const a; - const int * borrow d = &const a; - int b = *c; - int e = *d; + int a = 1; + const int *borrow c = &const a; + const int *borrow d = &const a; + int b = *c; + int e = *d; } void test_freeze_ident13() { - int a = 1; - const int * borrow c = &const a; // expected-note {{previous borrow is here}} - a = 2; // expected-error {{Can not modify 'a' because be borrowed}} - int b = *c; + int a = 1; + const int *borrow c = &const a; // expected-note {{`a` is borrowed here}} + a = 2; // expected-error {{cannot assign to `a` because it is borrowed}} + int b = *c; } int test_freeze_ident14(){ @@ -149,156 +149,156 @@ int test_freeze_ident14(){ return 0; } -int test_freeze_ident15(int * borrow m){ +int test_freeze_ident15(int *borrow m){ int a = 1; int b = 2; int *c, *d; c = &a; d = &b; - int * borrow e = &mut *c; - int * borrow f = &mut *c; // expected-note {{previous borrow is here}} - int g = f == e ? *f : *e; // expected-error {{There should be at most one mutable borrow targeting to 'c' at the same time}} + int *borrow e = &mut *c; // expected-note {{first mut borrow occurs here}} + int *borrow f = &mut *c; // expected-error {{cannot borrow `*c` as mutable more than once at a time}} + int g = f == e ? *f : *e; use(f); - use(e); // expected-error {{Can not use 'e' because expired}} + use(e); return 0; } struct S { int a; - int * borrow b; + int *borrow b; }; void test_freeze_ident17(){ - int a = 1; - int *owned b = (int *owned)&a; - int *borrow c = &mut *b; - int *d = (int *)b; + int a = 1; + int *owned b = (int *owned)&a; + int *borrow c = &mut *b; + int *d = (int *)b; } void test_freeze_ident18(){ - int a = 1; - int *owned b = (int *owned)&a; - int *borrow c = &mut *b; // expected-note {{previous borrow is here}} - int *d = (int *)b; // expected-error {{Can not read 'b' because be mut borrowed}} - use(c); + int a = 1; + int *owned b = (int *owned)&a; + int *borrow c = &mut *b; // expected-note {{`*b` is borrowed here}} + int *d = (int *)b; // expected-error {{cannot move out of `b` because it is borrowed}} + use(c); } void test_freeze_ident19(){ - int a = 1; - int *owned b = (int *owned)&a; - int *borrow c = &mut *b; // expected-note {{previous borrow is here}} - int *owned d = b; // expected-error {{Can not modify 'b' because be borrowed}} - use(c); - int *e = (int *)d; + int a = 1; + int *owned b = (int *owned)&a; + int *borrow c = &mut *b; // expected-note {{`*b` is borrowed here}} + int *owned d = b; // expected-error {{cannot move out of `b` because it is borrowed}} + use(c); + int *e = (int *)d; } void test_freeze_ident20(){ - int a = 1; - int *owned b = (int *owned)&a; - const int *borrow c = &const *b; // expected-note {{previous borrow is here}} - int *owned d = b; // expected-error {{Can not modify 'b' because be borrowed}} - const_use(c); - int * e = (int *)d; -} - -owned struct F -{ - public: - int a; + int a = 1; + int *owned b = (int *owned)&a; + const int *borrow c = &const *b; // expected-note {{`*b` is borrowed here}} + int *owned d = b; // expected-error {{cannot move out of `b` because it is borrowed}} + const_use(c); + int * e = (int *)d; +} + +owned struct F { +public: + int a; }; -void owend_struct_use1(F *borrow b){} +void owned_struct_use1(F *borrow b){} void test_freeze_ident21(){ - F f = {1}; - F *borrow b = &mut f; // expected-note {{previous borrow is here}} - F k = f; // expected-error {{Can not modify 'f' because be borrowed}} - owend_struct_use1(b); + F f = {1}; + F *borrow b = &mut f; // expected-note {{`f` is borrowed here}} + F k = f; // expected-error {{cannot move out of `f` because it is borrowed}} + owned_struct_use1(b); } -void owend_struct_use2(const F *borrow b){} +void owned_struct_use2(const F *borrow b){} void test_freeze_ident22(){ - F f = {1}; - const F *borrow b = &const f; // expected-note {{previous borrow is here}} - F k = f; // expected-error {{Can not modify 'f' because be borrowed}} - owend_struct_use2(b); + F f = {1}; + const F *borrow b = &const f; // expected-note {{`f` is borrowed here}} + F k = f; // expected-error {{cannot move out of `f` because it is borrowed}} + owned_struct_use2(b); } -struct H -{ - int *owned a; - int b; +struct H { + int *owned a; + int b; }; -void owend_struct_use3(struct H *borrow b){} +void owned_struct_use3(struct H *borrow b){} void test_freeze_ident23(){ - int a = 1; - struct H h = {(int *owned)&a, 2}; - struct H *borrow b = &mut h; // expected-note {{previous borrow is here}} - struct H g = h; // expected-error {{Can not modify 'h' because be borrowed}} - owend_struct_use3(b); - int *c = (int *)g.a; + int a = 1; + struct H h = {(int *owned)&a, 2}; + struct H *borrow b = &mut h; // expected-note {{`h` is borrowed here}} + struct H g = h; // expected-error {{cannot move out of `h` because it is borrowed}} + owned_struct_use3(b); + int *c = (int *)g.a; } -void owend_struct_use4(const struct H *borrow b){} +void owned_struct_use4(const struct H *borrow b){} void test_freeze_ident24(){ - int a = 1; - struct H h = {(int *owned)&a, 2}; - const struct H * borrow b = &const h; // expected-note {{previous borrow is here}} - struct H g = h; // expected-error {{Can not modify 'h' because be borrowed}} - owend_struct_use4(b); - int *c = (int *)g.a; + int a = 1; + struct H h = {(int *owned)&a, 2}; + const struct H *borrow b = &const h; // expected-note {{`h` is borrowed here}} + struct H g = h; // expected-error {{cannot move out of `h` because it is borrowed}} + owned_struct_use4(b); + int *c = (int *)g.a; } void test_freeze_ident25(){ - int a = 1; - int *borrow p1 = &mut a; // expected-note {{previous borrow is here}} - a = 5; // expected-error {{Can not modify 'a' because be borrowed}} - int *borrow p2 = p1; + int a = 1; + int *borrow p1 = &mut a; // expected-note {{`a` is borrowed here}} + a = 5; // expected-error {{cannot assign to `a` because it is borrowed}} + int *borrow p2 = p1; } void test_freeze_ident26(){ - int a = 1; - int *owned b = (int *owned)&a; - int *borrow p1 = &mut *b; // expected-note {{previous borrow is here}} - int *d = (int *)b; // expected-error {{Can not read 'b' because be mut borrowed}} - int *borrow p2 = p1; + int a = 1; + int *owned b = (int *owned)&a; + int *borrow p1 = &mut *b; // expected-note {{`*b` is borrowed here}} + int *d = (int *)b; // expected-error {{cannot move out of `b` because it is borrowed}} + int *borrow p2 = p1; } void test_freeze_ident27(){ - int a = 1; - int *owned b = (int *owned)&a; - int *borrow p1 = &mut *b; - int *borrow p2 = p1; // expected-note {{previous borrow is here}} - int *d = (int *)b; // expected-error {{Can not read 'b' because be mut borrowed}} - use(p2); + int a = 1; + int *owned b = (int *owned)&a; + int *borrow p1 = &mut *b; // expected-note {{`*b` is borrowed here}} + int *borrow p2 = p1; + int *d = (int *)b; // expected-error {{cannot move out of `b` because it is borrowed}} + use(p2); } void test_freeze_ident28(){ - int a = 1; - int *owned b = (int *owned)&a; - int *borrow p1 = &mut *b; // expected-note {{previous borrow is here}} - int *d = (int *)b; // expected-error {{Can not read 'b' because be mut borrowed}} - int *borrow p2 = p1; // expected-note {{previous borrow is here}} expected-note {{previous borrow is here}} - b = (int *owned)&a; // expected-error {{Can not modify 'b' because be borrowed}} - d = (int *)b; // expected-error {{Can not read 'b' because be mut borrowed}} - use(p2); + int a = 1; + int *owned b = (int *owned)&a; + int *borrow p1 = &mut *b; // expected-note {{`*b` is borrowed here}} + // expected-note@-1 {{`b` is borrowed here}} + int *d = (int *)b; // expected-error {{cannot move out of `b` because it is borrowed}} + int *borrow p2 = p1; + b = (int *owned)&a; // expected-error {{cannot assign to `b` because it is borrowed}} + d = (int *)b; + use(p2); } void test_freeze_ident29(){ - int a = 1; - int *borrow p1 = &mut a; // expected-note {{previous borrow is here}} - int *borrow p2 = &mut a; - a = 5; // expected-error {{Can not modify 'a' because be borrowed}} - p2 = p1; + int a = 1; + int *borrow p1 = &mut a; // expected-note {{first mut borrow occurs here}} + // expected-note@-1 {{`a` is borrowed here}} + int *borrow p2 = &mut a; // expected-error {{cannot borrow `a` as mutable more than once at a time}} + a = 5; // expected-error {{cannot assign to `a` because it is borrowed}} + p2 = p1; } void test_freeze_ident30(){ - int local = 5; - int *borrow p = &mut local; // expected-note {{previous borrow is here}} - use(&mut local); // expected-error {{Can not modify 'local' because be borrowed}} - use(p); + int local = 5; + int *borrow p = &mut local; // expected-note {{first mut borrow occurs here}} + use(&mut local); // expected-error {{cannot borrow `local` as mutable more than once at a time}} + use(p); } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_star_freeze/borrow_star_freeze.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_star_freeze/borrow_star_freeze.cbs index b450b3017def303f7c2471083c1949759cf2a191..131b3d3e04f4ab4985690c40c748dd39f33f8931 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_star_freeze/borrow_star_freeze.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/borrow_star_freeze/borrow_star_freeze.cbs @@ -1,13 +1,14 @@ // RUN: %clang_cc1 -ast-dump -verify %s +// expected-no-diagnostics void test1(int *p) {} void test_freeze_star_var1() { int a = 1; int * b = &a; - int * borrow c = &mut *b; // expected-note {{previous borrow is here}} + int * borrow c = &mut *b; a = 2; // ok; - b = &a; // expected-error {{Can not modify 'b' because be borrowed}} + b = &a; int m = *c; } diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/member_func_call_freeze/member_func_call_freeze.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/member_func_call_freeze/member_func_call_freeze.cbs index ff20d89994f003e384d43c2da6ac938efce0d4ef..f0aec908e48128fa21f03b35fdd1bc55084896b6 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/member_func_call_freeze/member_func_call_freeze.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_expr_freeze/member_func_call_freeze/member_func_call_freeze.cbs @@ -2,30 +2,31 @@ void use_mut(int *borrow p) {} void use_immut(const int *borrow p) {} -void free(void*); +void free(void *); int* malloc_int(int value); struct A { int *p; }; -int * borrow struct A::get_p_mut(This * borrow this) { +int * borrow struct A::get_p_mut(This *borrow this) { return &mut *(this->p); } -const int * borrow struct A::get_p_immut(This * borrow this) { +const int *borrow struct A::get_p_immut(This *borrow this) { return &const *(this->p); } -void struct A::free_p(This * borrow this) { - free((int*)this->p); +void struct A::free_p(This *borrow this) { + free((int *)this->p); } void test1() { struct A a = { .p = malloc_int(5) }; *a.p = 6; - int * borrow q1 = a.get_p_mut(); // expected-note {{previous borrow is here}} - const int * borrow q2 = a.get_p_immut(); // expected-note {{previous borrow is here}} expected-error {{Can not modify 'a' because be borrowed}} - a.free_p(); // expected-error {{Can not modify 'a' because be borrowed}} + int *borrow q1 = a.get_p_mut(); // expected-note {{first mut borrow occurs here}} + // expected-note@-1 {{first mut borrow occurs here}} + const int *borrow q2 = a.get_p_immut(); // expected-error {{cannot borrow `a` as mutable more than once at a time}} + a.free_p(); // expected-error {{cannot borrow `a` as mutable more than once at a time}} int tmp2 = *q2; int tmp1 = *q1; } @@ -33,7 +34,7 @@ void test1() { void test2() { struct A a = { .p = malloc_int(5) }; *a.p = 6; - int * borrow q = a.get_p_mut(); // expected-note {{previous borrow is here}} - a.free_p(); // expected-error {{Can not modify 'a' because be borrowed}} + int *borrow q = a.get_p_mut(); // expected-note {{first mut borrow occurs here}} + a.free_p(); // expected-error {{cannot borrow `a` as mutable more than once at a time}} int tmp = *q; } \ No newline at end of file diff --git a/clang/test/BSC/Negative/Ownership/Borrow/borrow_trait/borrow_trait.cbs b/clang/test/BSC/Negative/Ownership/Borrow/borrow_trait/borrow_trait.cbs index 5eabbefda4109e0688da1affd65c41ea7b1554b0..5535d154a18161a41d9577f27a95e16c3da7bcea 100644 --- a/clang/test/BSC/Negative/Ownership/Borrow/borrow_trait/borrow_trait.cbs +++ b/clang/test/BSC/Negative/Ownership/Borrow/borrow_trait/borrow_trait.cbs @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -verify %s +// expected-no-diagnostics trait T { static int f(This* this); @@ -13,9 +14,9 @@ impl trait T for int; int main() { int a = 1; int* t = &a; - trait T* borrow p = &mut *t; // expected-note {{previous borrow is here}} + trait T* borrow p = &mut *t; a = 2; - t = &a; // expected-error {{Can not modify 't' because be borrowed}} + t = &a; p->f(); return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Negative/SafeZone/unsafe_stmt_or_decl/unsafe_stmt_or_decl.cbs b/clang/test/BSC/Negative/SafeZone/unsafe_stmt_or_decl/unsafe_stmt_or_decl.cbs index 6dfcb05b0ed4e37d107c74fa1d93a16dc693d847..06c53b819d2c44e5f153e827b802a21641e98a69 100644 --- a/clang/test/BSC/Negative/SafeZone/unsafe_stmt_or_decl/unsafe_stmt_or_decl.cbs +++ b/clang/test/BSC/Negative/SafeZone/unsafe_stmt_or_decl/unsafe_stmt_or_decl.cbs @@ -3,10 +3,10 @@ safe int main(void) { int a; // expected-error {{uninitialized declarator is forbidden in the safe zone}} a = 0; - lab: // expected-error {{label statement is forbidden in the safe zone}} + lab: if (a < 10) { a = a + 1; - goto lab; // expected-error {{goto statement is forbidden in the safe zone}} + goto lab; } switch (a) { int b = 1; // expected-error {{variable declaration in the top-level switch block is forbidden in the safe zone}} diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/assign_borrowed/assign_borrowed.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/assign_borrowed/assign_borrowed.cbs new file mode 100644 index 0000000000000000000000000000000000000000..e139316b9ea06ffee43490ca86f6508683834c66 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/assign_borrowed/assign_borrowed.cbs @@ -0,0 +1,23 @@ +// RUN: %clang -Eno-assign-borrowed -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void test1() { + int local = 42; + int temp = 2; + int *borrow p1 = &mut local; + int *borrow p2 = &mut temp; + p2 = p1; + *p1 = 2; // cannot assign to `*p1` because it is borrowed + temp = *p2; +} + +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int temp = 2; +// CHECK-NEXT: int * p1 = &local; +// CHECK-NEXT: int * p2 = &temp; +// CHECK-NEXT: p2 = p1; +// CHECK-NEXT: *p1 = 2; +// CHECK-NEXT: temp = *p2; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/move_borrowed/move_borrowed.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/move_borrowed/move_borrowed.cbs new file mode 100644 index 0000000000000000000000000000000000000000..39202faee35f201ddf928b4aed6ede3b575bb20b --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/move_borrowed/move_borrowed.cbs @@ -0,0 +1,21 @@ +// RUN: %clang -Eno-move-borrowed -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void use_mut(int *borrow p); +void free_owned(T* owned p); +void test1(int* owned oriPtr) { + int *borrow p = &mut *oriPtr; + free_owned(oriPtr); // cannot move out of `oriPtr` because it is borrowed + use_mut(p); +} + +// CHECK: static void free_owned_int(int * p); +// CHECK-EMPTY: +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void test1(int * oriPtr) { +// CHECK-NEXT: int * p = &*oriPtr; +// CHECK-NEXT: free_owned_int(oriPtr); +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/repeated_borrow/repeated_borrow.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/repeated_borrow/repeated_borrow.cbs new file mode 100644 index 0000000000000000000000000000000000000000..4d8f7b4bba117b15fcb1a21057e0eda33f417bbc --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/repeated_borrow/repeated_borrow.cbs @@ -0,0 +1,57 @@ +// RUN: %clang -Eno-repeated-borrow -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +void test1() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = &mut local; // cannot borrow `local` as mutable more than once at a time + use_mut(p); + use_mut(q); +} + +void test2() { + int local = 42; + int *borrow p = &mut local; + const int *borrow q = &const local; // cannot borrow `local` as immutable because it is also borrowed as mutable + use_mut(p); + use_immut(q); +} + +void test3(int *borrow p) { + int local = 5; + p = &mut local; + const int *borrow p1 = &const *p; + use_mut(p); // cannot borrow `*p` as mutable because it is also borrowed as immutable + use_immut(p1); +} + +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void use_immut(const int * p); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int * p = &local; +// CHECK-NEXT: int * q = &local; +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: use_mut(q); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int * p = &local; +// CHECK-NEXT: const int * q = &local; +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: use_immut(q); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test3(int * p) { +// CHECK-NEXT: int local = 5; +// CHECK-NEXT: p = &local; +// CHECK-NEXT: const int * p1 = &*p; +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: use_immut(p1); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/short_life_borrow/short_life_borrow.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/short_life_borrow/short_life_borrow.cbs new file mode 100644 index 0000000000000000000000000000000000000000..5e770cff26f0f949727d4e274abcf4dcbbf4c7e5 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/short_life_borrow/short_life_borrow.cbs @@ -0,0 +1,22 @@ +// RUN: %clang -Eno-short-life-borrow -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void use_mut(int *borrow p); +void test1(int *borrow p) { + { + int local1 = 42; + p = &mut local1; // `local1` does not live long enough + } + use_mut(p); +} + +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void test1(int * p) { +// CHECK-NEXT: { +// CHECK-NEXT: int local1 = 42; +// CHECK-NEXT: p = &local1; +// CHECK-NEXT: } +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs new file mode 100644 index 0000000000000000000000000000000000000000..7041fc73cf966f148f79003d8c3c250833a321d4 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs @@ -0,0 +1,19 @@ +// RUN: %clang -Eno-use-mutably-borrowed -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void test1() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = p; + p = q; + int temp = *q; // cannot use `*q` because it was mutably borrowed +} + +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int * p = &local; +// CHECK-NEXT: int * q = p; +// CHECK-NEXT: p = q; +// CHECK-NEXT: int temp = *q; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_borrow/bsc_borrow.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_borrow/bsc_borrow.cbs new file mode 100644 index 0000000000000000000000000000000000000000..e37bc061f56959022f6806401931ab71c269c4bd --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_borrow/bsc_borrow.cbs @@ -0,0 +1,41 @@ +// RUN: %clang -Eno-bsc-borrow -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void test1() { + int local = 42; + int temp = 2; + int *borrow p1 = &mut local; + int *borrow p2 = &mut temp; + p2 = p1; + *p1 = 2; // cannot assign to `*p1` because it is borrowed + temp = *p2; +} + +void use_mut(int *borrow p); +void free_owned(T* owned p); +void test2(int* owned oriPtr) { + int *borrow p = &mut *oriPtr; + free_owned(oriPtr); // cannot move out of `oriPtr` because it is borrowed + use_mut(p); +} + +// CHECK: static void free_owned_int(int * p); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int temp = 2; +// CHECK-NEXT: int * p1 = &local; +// CHECK-NEXT: int * p2 = &temp; +// CHECK-NEXT: p2 = p1; +// CHECK-NEXT: *p1 = 2; +// CHECK-NEXT: temp = *p2; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void test2(int * oriPtr) { +// CHECK-NEXT: int * p = &*oriPtr; +// CHECK-NEXT: free_owned_int(oriPtr); +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_nullability/bsc_nullability.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_nullability/bsc_nullability.cbs new file mode 100644 index 0000000000000000000000000000000000000000..b826c0c484a1513d431af327fea617509dd87427 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_nullability/bsc_nullability.cbs @@ -0,0 +1,60 @@ +// RUN: %clang -Eno-bsc-nullability -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *borrow _Nullable foo1(int* borrow a); +safe void test1(void) { + int a = 10; + int *borrow p1 = foo1(&mut a); + int *borrow _Nullable p2 = foo1(&mut a); +} + +void test2(void) { + int *borrow p = nullptr; + unsafe { + int *borrow p2 = nullptr; + } +} + +struct S { + int a; +}; +safe void test3(void) { + struct S *borrow _Nullable p = nullptr; + if (p != nullptr) { + p->a = 10; + } else { + p->a = 20; + } + p->a = 30; +} + +// CHECK: struct S; +// CHECK-NEXT: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: int * foo1(int * a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int a = 10; +// CHECK-NEXT: int * p1 = foo1(&a); +// CHECK-NEXT: int * p2 = foo1(&a); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: { +// CHECK-NEXT: int * p2 = 0; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test3(void) { +// CHECK-NEXT: struct S * p = 0; +// CHECK-NEXT: if (p != 0) { +// CHECK-NEXT: p->a = 10; +// CHECK-NEXT: } else { +// CHECK-NEXT: p->a = 20; +// CHECK-NEXT: } +// CHECK-NEXT: p->a = 30; +// CHECK-NEXT: } \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_ownership/bsc_ownership.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_ownership/bsc_ownership.cbs new file mode 100644 index 0000000000000000000000000000000000000000..5ab69f47ea7a97da723d0335ca0a2a1eb39a9648 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_ownership/bsc_ownership.cbs @@ -0,0 +1,59 @@ +// RUN: %clang -Eno-bsc-ownership -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void free_owned_new(void * owned p); + +typedef struct A { + int * owned a; +} A; + +void test1(A * owned p) { + free_owned_new((void * owned)p); // invalid cast to `void * owned` of not all moved value: `p`, p.a is owned +} + +int * owned test2() { + int * owned a; + return a; // use of uninitialized value: `a` +} + +safe T *owned safe_malloc(T t); +safe void test3(void) { + int *owned _Nullable p = nullptr; + int cond = 1; + if (cond) { + p = safe_malloc(10); + *p = 10; + } + p = nullptr; //assign to owned value: `p` +} + +// CHECK: struct A; +// CHECK-NEXT: typedef struct A A; +// CHECK-EMPTY: +// CHECK: struct A { +// CHECK-NEXT: int * a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: static int * safe_malloc_int(int t); +// CHECK-EMPTY: +// CHECK: void free_owned_new(void * p); +// CHECK-EMPTY: +// CHECK: void test1(A * p) { +// CHECK-NEXT: free_owned_new((void *)p); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: int * test2(void) { +// CHECK-NEXT: int * a; +// CHECK-NEXT: return a; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test3(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: int cond = 1; +// CHECK-NEXT: if (cond) { +// CHECK-NEXT: p = safe_malloc_int(10); +// CHECK-NEXT: *p = 10; +// CHECK-NEXT: } +// CHECK-NEXT: p = 0; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_safety_check/bsc_safety_check.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_safety_check/bsc_safety_check.cbs new file mode 100644 index 0000000000000000000000000000000000000000..6957296523445ec919fa6c484db0636283514718 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Mixed/bsc_safety_check/bsc_safety_check.cbs @@ -0,0 +1,27 @@ +// RUN: %clang -Eno-bsc-safety-check -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *owned _Nonnull safe_malloc(int a); +safe void test1(void) { + int *owned _Nullable p1 = nullptr; + int *owned _Nonnull p2 = p1; // 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); // nonnull pointer cannot be assigned by nullable pointer + p5 = p1; // use of moved value: `p1` + p5 = p3; // memory leak of value: `p5` +} + +// CHECK: int * safe_malloc(int a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int * p1 = 0; +// CHECK-NEXT: int * p2 = p1; +// CHECK-NEXT: int * p3 = safe_malloc(5); +// CHECK-NEXT: int * p4 = p3; +// CHECK-NEXT: int * p5 = safe_malloc(5); +// CHECK-NEXT: p5 = p1; +// CHECK-NEXT: p5 = p3; +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nonnull/assign_nonnull.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nonnull/assign_nonnull.cbs new file mode 100644 index 0000000000000000000000000000000000000000..bfd88822dfc5f0d7b1273853459d1360ea8cc8cf --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nonnull/assign_nonnull.cbs @@ -0,0 +1,33 @@ +// RUN: %clang -Eno-assign-nonnull -nullability-check=all -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *borrow _Nullable foo1(int* borrow a); +safe void test1(void) { + int a = 10; + int *borrow p1 = foo1(&mut a); // nonnull pointer cannot be assigned by nullable pointer + int *borrow _Nullable p2 = foo1(&mut a); +} + +void test2(void) { + int *borrow p = nullptr; // nonnull pointer cannot be assigned by nullable pointer + unsafe { + int *borrow p2 = nullptr; // nonnull pointer cannot be assigned by nullable pointer + } +} + +// CHECK: int * foo1(int * a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int a = 10; +// CHECK-NEXT: int * p1 = foo1(&a); +// CHECK-NEXT: int * p2 = foo1(&a); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: { +// CHECK-NEXT: int * p2 = 0; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nullable/assign_nullable.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nullable/assign_nullable.cbs new file mode 100644 index 0000000000000000000000000000000000000000..887db09ec7793c4644d6c1018dadafac69ddc862 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/assign_nullable/assign_nullable.cbs @@ -0,0 +1,32 @@ +// RUN: %clang -Eno-assign-nullable -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +struct S { + int a; +}; +safe void test1(void) { + struct S *borrow _Nullable p = nullptr; + if (p != nullptr) { + p->a = 10; + } else { + p->a = 20; // cannot access member through nullable pointer + } + p->a = 30; // cannot access member through nullable pointer +} + +// CHECK: struct S; +// CHECK-NEXT: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: struct S * p = 0; +// CHECK-NEXT: if (p != 0) { +// CHECK-NEXT: p->a = 10; +// CHECK-NEXT: } else { +// CHECK-NEXT: p->a = 20; +// CHECK-NEXT: } +// CHECK-NEXT: p->a = 30; +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/cast_nullable/cast_nullable.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/cast_nullable/cast_nullable.cbs new file mode 100644 index 0000000000000000000000000000000000000000..e8018e27822f944b7ef0f86cb53405a2c2031481 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/cast_nullable/cast_nullable.cbs @@ -0,0 +1,31 @@ +// RUN: %clang -Eno-cast-nullable -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe T *owned safe_malloc(T t); +safe void safe_free(void *owned p); + +safe void test1(void) { + int *owned _Nullable p = nullptr; + int cond = 1; + if (cond) { + p = safe_malloc(10); + *p = 10; + } + safe_free((void *owned)p); // cannot cast nullable pointer to nonnull type +} + +// CHECK: static int * safe_malloc_int(int t); +// CHECK-EMPTY: +// CHECK: void safe_free(void * p); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: int cond = 1; +// CHECK-NEXT: if (cond) { +// CHECK-NEXT: p = safe_malloc_int(10); +// CHECK-NEXT: *p = 10; +// CHECK-NEXT: } +// CHECK-NEXT: safe_free((void *)p); +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/deref_nullable/deref_nullable.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/deref_nullable/deref_nullable.cbs new file mode 100644 index 0000000000000000000000000000000000000000..e72c5b980f3398230b2f723cd57e7856315245c5 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/deref_nullable/deref_nullable.cbs @@ -0,0 +1,33 @@ +// RUN: %clang -Eno-deref-nullable -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +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; // nullable pointer cannot be dereferenced + } + *s.p = 30; // nullable pointer cannot be dereferenced +} + +// CHECK: struct S; +// CHECK-NEXT: struct S { +// CHECK-NEXT: int * p; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: struct S s = {.p = 0}; +// CHECK-NEXT: if (s.p != 0) { +// CHECK-NEXT: *s.p = 10; +// CHECK-NEXT: } else { +// CHECK-NEXT: *s.p = 20; +// CHECK-NEXT: } +// CHECK-NEXT: *s.p = 30; +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/pass_nullable/pass_nullable.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/pass_nullable/pass_nullable.cbs new file mode 100644 index 0000000000000000000000000000000000000000..df0047f5a002e0bd272a3ec36c0be5e78d42f481 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/pass_nullable/pass_nullable.cbs @@ -0,0 +1,31 @@ +// RUN: %clang -Eno-pass-nullable -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +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); // cannot pass nullable pointer argument + foo1(&mut a); + foo1(nullptr); // cannot pass nullable pointer argument +} + +// CHECK: void foo1(int * a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int a = 10; +// CHECK-NEXT: int * p1 = &a; +// CHECK-NEXT: foo1(p1); +// CHECK-NEXT: int * p2 = &a; +// CHECK-NEXT: foo1(p2); +// CHECK-NEXT: int * p3 = 0; +// CHECK-NEXT: foo1(p3); +// CHECK-NEXT: foo1(&a); +// CHECK-NEXT: foo1(0); +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/return_nullable/return_nullable.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/return_nullable/return_nullable.cbs new file mode 100644 index 0000000000000000000000000000000000000000..ae342573b36f9d7f45f0e85e985fe2fffc267fa8 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Nullability/return_nullable/return_nullable.cbs @@ -0,0 +1,12 @@ +// RUN: %clang -Eno-return-nullable -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *borrow foo1(int *borrow _Nullable a) { + return a; // cannot return nullable pointer type +} + +// CHECK: int * foo1(int * a) { +// CHECK-NEXT: return a; +// CHECK-NEXT: } +// CHECK-EMPTY: diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/assign_owned/assign_owned.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/assign_owned/assign_owned.cbs new file mode 100644 index 0000000000000000000000000000000000000000..772480d26c8ab45886a4401cad7fc804c0ed14bb --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/assign_owned/assign_owned.cbs @@ -0,0 +1,26 @@ +// RUN: %clang -Eno-assign-owned -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe T *owned safe_malloc(T t); +safe void test1(void) { + int *owned _Nullable p = nullptr; + int cond = 1; + if (cond) { + p = safe_malloc(10); + *p = 10; + } + p = nullptr; // assign to owned value: `p` +} + +// CHECK: static int * safe_malloc_int(int t); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: int cond = 1; +// CHECK-NEXT: if (cond) { +// CHECK-NEXT: p = safe_malloc_int(10); +// CHECK-NEXT: *p = 10; +// CHECK-NEXT: } +// CHECK-NEXT: p = 0; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/cast_owned/cast_owned.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/cast_owned/cast_owned.cbs new file mode 100644 index 0000000000000000000000000000000000000000..090435aabd6482ff289f5e46a938cec3d7156db4 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/cast_owned/cast_owned.cbs @@ -0,0 +1,26 @@ +// RUN: %clang -Eno-cast-owned -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void free_owned_new(void * owned p); + +typedef struct A { + int * owned a; +} A; + +void test1(A * owned p) { + free_owned_new((void * owned)p); // invalid cast to `void * owned` of not all moved value: `p`, p.a is owned +} + +// CHECK: struct A; +// CHECK-NEXT: typedef struct A A; +// CHECK-EMPTY: +// CHECK: struct A { +// CHECK-NEXT: int * a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void free_owned_new(void * p); +// CHECK-EMPTY: +// CHECK: void test1(A * p) { +// CHECK-NEXT: free_owned_new((void *)p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/memory_leak/memory_leak.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/memory_leak/memory_leak.cbs new file mode 100644 index 0000000000000000000000000000000000000000..a37aee8c9ec922b3aa7263866ea135b6eb18fdac --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/memory_leak/memory_leak.cbs @@ -0,0 +1,70 @@ +// RUN: %clang -Eno-check-memory-leak -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +typedef int* owned myInt; +void test1() { + int x = 10; + int* p = &x; + myInt p1 = (myInt)p; +} // memory leak of value: `p1` + +owned struct S { +public: + int a; +}; + +trait T { +}; + +impl trait T for S; + +void test2(S* owned sp) { + trait T* owned t = sp; +} // field memory leak of value: `t`, t.data is leak + +// CHECK: struct S; +// CHECK-NEXT: struct __Trait_T_Vtable; +// CHECK-NEXT: struct __Trait_T; +// CHECK-NEXT: struct __Trait_T_Owned; +// CHECK-NEXT: struct __Trait_T_Borrow; +// CHECK-NEXT: typedef int * myInt; +// CHECK-EMPTY: +// CHECK: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Vtable { +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T { +// CHECK-NEXT: void *data; +// CHECK-NEXT: struct __Trait_T_Vtable *vtable; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Owned { +// CHECK-NEXT: void * data; +// CHECK-NEXT: struct __Trait_T_Vtable *vtable; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Borrow { +// CHECK-NEXT: void * data; +// CHECK-NEXT: struct __Trait_T_Vtable *vtable; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int x = 10; +// CHECK-NEXT: int *p = &x; +// CHECK-NEXT: myInt p1 = (myInt)p; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Vtable __owned_struct_S_trait_T = {}; +// CHECK-EMPTY: +// CHECK: void test2( struct S * sp) { +// CHECK-NEXT: struct __Trait_T_Owned t = {sp, &__owned_struct_S_trait_T}; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this) { +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_moved_owned/use_moved_owned.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_moved_owned/use_moved_owned.cbs new file mode 100644 index 0000000000000000000000000000000000000000..213843827ad40a04e2cc286718136890f83be315 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_moved_owned/use_moved_owned.cbs @@ -0,0 +1,113 @@ +// RUN: %clang -Eno-use-moved-owned -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +owned struct A { + public: + int x; + int y ; + ~A(A this){ + } +}; +int f(A a){ return 1;} + +int test1() { + A a = {1, 2}; + A b = a; + f(a); // use of moved value: `a` + return 0; +} + +owned struct S { +public: + int a; + int b; +}; + +owned struct T { +public: + S s; +}; + +struct B { + T t; + int c; +}; + +void test2(struct B sb) { + S s = sb.t.s; + T t = sb.t; // use of partially moved value: `sb.t`, sb.t.s is moved +} + +// CHECK: struct A; +// CHECK-NEXT: struct S; +// CHECK-NEXT: struct T; +// CHECK-NEXT: struct B; +// CHECK-NEXT: struct A { +// CHECK-NEXT: int x; +// CHECK-NEXT: int y; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: int b; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct T { +// CHECK-NEXT: struct S s; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct B { +// CHECK-NEXT: struct T t; +// CHECK-NEXT: int c; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: static void struct_A_D( struct A this); +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this); +// CHECK-EMPTY: +// CHECK: void struct_T_D( struct T this); +// CHECK-EMPTY: +// CHECK: int f( struct A a) { +// CHECK-NEXT: _Bool a_is_moved = 0; +// CHECK-NEXT: if (!a_is_moved) +// CHECK-NEXT: struct_A_D(a); +// CHECK-NEXT: return 1; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: int test1(void) { +// CHECK-NEXT: struct A a = {1, 2}; +// CHECK-NEXT: _Bool a_is_moved = 0; +// CHECK-NEXT: struct A b = a; +// CHECK-NEXT: _Bool b_is_moved = 0; +// CHECK-NEXT: a_is_moved = 1; +// CHECK-NEXT: f(a); +// CHECK-NEXT: a_is_moved = 1; +// CHECK-NEXT: if (!b_is_moved) +// CHECK-NEXT: struct_A_D(b); +// CHECK-NEXT: if (!a_is_moved) +// CHECK-NEXT: struct_A_D(a); +// CHECK-NEXT: return 0; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(struct B sb) { +// CHECK-NEXT: struct S s = sb.t.s; +// CHECK-NEXT: _Bool s_is_moved = 0; +// CHECK-NEXT: struct T t = sb.t; +// CHECK-NEXT: _Bool t_is_moved = 0; +// CHECK-NEXT: if (!t_is_moved) +// CHECK-NEXT: struct_T_D(t); +// CHECK-NEXT: if (!s_is_moved) +// CHECK-NEXT: struct_S_D(s); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: static void struct_A_D( struct A this) { +// CHECK-NEXT: _Bool this_is_moved = 0; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this) { +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void struct_T_D( struct T this) { +// CHECK-NEXT: struct_S_D(this.s); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_uninit_owned/use_uninit_owned.cbs b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_uninit_owned/use_uninit_owned.cbs new file mode 100644 index 0000000000000000000000000000000000000000..e783c03c2712564c8e2a96b5d87bb2f05871e2d3 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/CompOptions/Ownership/use_uninit_owned/use_uninit_owned.cbs @@ -0,0 +1,13 @@ +// RUN: %clang -Eno-use-uninit-owned -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +int * owned test() { + int * owned a; + return a; // use of uninitialized value: `a` +} + +// CHECK: int * test(void) { +// CHECK-NEXT: int * a; +// CHECK-NEXT: return a; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/assign_borrowed/assign_borrowed.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/assign_borrowed/assign_borrowed.cbs new file mode 100644 index 0000000000000000000000000000000000000000..d1ef1e2e94a70badb9c2082b8998984138512de3 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/assign_borrowed/assign_borrowed.cbs @@ -0,0 +1,26 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void test1() { + int local = 42; + int temp = 2; + int *borrow p1 = &mut local; + int *borrow p2 = &mut temp; + p2 = p1; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eassign-borrowed" + *p1 = 2; // cannot assign to `*p1` because it is borrowed + #pragma GCC diagnostic pop + temp = *p2; +} + +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int temp = 2; +// CHECK-NEXT: int * p1 = &local; +// CHECK-NEXT: int * p2 = &temp; +// CHECK-NEXT: p2 = p1; +// CHECK-NEXT: *p1 = 2; +// CHECK-NEXT: temp = *p2; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/move_borrowed/move_borrowed.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/move_borrowed/move_borrowed.cbs new file mode 100644 index 0000000000000000000000000000000000000000..a3211e6abfd19af7114212d3b25b20991014af71 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/move_borrowed/move_borrowed.cbs @@ -0,0 +1,24 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void use_mut(int *borrow p); +void free_owned(T* owned p); +void test1(int* owned oriPtr) { + int *borrow p = &mut *oriPtr; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Emove-borrowed" + free_owned(oriPtr); // cannot move out of `oriPtr` because it is borrowed + #pragma GCC diagnostic pop + use_mut(p); +} + +// CHECK: static void free_owned_int(int * p); +// CHECK-EMPTY: +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void test1(int * oriPtr) { +// CHECK-NEXT: int * p = &*oriPtr; +// CHECK-NEXT: free_owned_int(oriPtr); +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/repeated_borrow/repeated_borrow.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/repeated_borrow/repeated_borrow.cbs new file mode 100644 index 0000000000000000000000000000000000000000..887e88d5095e8b103a3bf88e32c5ba26f5261791 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/repeated_borrow/repeated_borrow.cbs @@ -0,0 +1,66 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void use_mut(int *borrow p); +void use_immut(const int *borrow p); +void test1() { + int local = 42; + int *borrow p = &mut local; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Erepeated-borrow" + int *borrow q = &mut local; // cannot borrow `local` as mutable more than once at a time + #pragma GCC diagnostic pop + use_mut(p); + use_mut(q); +} + +void test2() { + int local = 42; + int *borrow p = &mut local; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Erepeated-borrow" + const int *borrow q = &const local; // cannot borrow `local` as immutable because it is also borrowed as mutable + use_mut(p); + #pragma GCC diagnostic pop + use_immut(q); +} + +void test3(int *borrow p) { + int local = 5; + p = &mut local; + const int *borrow p1 = &const *p; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Erepeated-borrow" + use_mut(p); // cannot borrow `*p` as mutable because it is also borrowed as immutable + #pragma GCC diagnostic pop + use_immut(p1); +} + +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void use_immut(const int * p); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int * p = &local; +// CHECK-NEXT: int * q = &local; +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: use_mut(q); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int * p = &local; +// CHECK-NEXT: const int * q = &local; +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: use_immut(q); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test3(int * p) { +// CHECK-NEXT: int local = 5; +// CHECK-NEXT: p = &local; +// CHECK-NEXT: const int * p1 = &*p; +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: use_immut(p1); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/short_life_borrow/short_life_borrow.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/short_life_borrow/short_life_borrow.cbs new file mode 100644 index 0000000000000000000000000000000000000000..0d79bafe81f5cc0f116dda2eaf488305381822bf --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/short_life_borrow/short_life_borrow.cbs @@ -0,0 +1,25 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void use_mut(int *borrow p); +void test1(int *borrow p) { + { + int local1 = 42; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eshort-life-borrow" + p = &mut local1; // `local1` does not live long enough + #pragma GCC diagnostic pop + } + use_mut(p); +} + +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void test1(int * p) { +// CHECK-NEXT: { +// CHECK-NEXT: int local1 = 42; +// CHECK-NEXT: p = &local1; +// CHECK-NEXT: } +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs new file mode 100644 index 0000000000000000000000000000000000000000..e23459c3f2b15502b9bcb19197a4f06b5bd62568 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/BorrowCheck/use_mutably_borrowed/use_mutably_borrowed.cbs @@ -0,0 +1,22 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void test1() { + int local = 42; + int *borrow p = &mut local; + int *borrow q = p; + p = q; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Euse-mutably-borrowed" + int temp = *q; // cannot use `*q` because it was mutably borrowed + #pragma GCC diagnostic pop +} + +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int * p = &local; +// CHECK-NEXT: int * q = p; +// CHECK-NEXT: p = q; +// CHECK-NEXT: int temp = *q; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_borrow/bsc_borrow.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_borrow/bsc_borrow.cbs new file mode 100644 index 0000000000000000000000000000000000000000..b35e3ef0111c0f560a8229a96b2bba7c85d269c6 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_borrow/bsc_borrow.cbs @@ -0,0 +1,47 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void test1() { + int local = 42; + int temp = 2; + int *borrow p1 = &mut local; + int *borrow p2 = &mut temp; + p2 = p1; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-borrow" + *p1 = 2; // cannot assign to `*p1` because it is borrowed + temp = *p2; + #pragma GCC diagnostic pop +} + +void use_mut(int *borrow p); +void free_owned(T* owned p); +void test2(int* owned oriPtr) { + int *borrow p = &mut *oriPtr; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-borrow" + free_owned(oriPtr); // cannot move out of `oriPtr` because it is borrowed + use_mut(p); + #pragma GCC diagnostic pop +} + +// CHECK: static void free_owned_int(int * p); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int local = 42; +// CHECK-NEXT: int temp = 2; +// CHECK-NEXT: int * p1 = &local; +// CHECK-NEXT: int * p2 = &temp; +// CHECK-NEXT: p2 = p1; +// CHECK-NEXT: *p1 = 2; +// CHECK-NEXT: temp = *p2; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void use_mut(int * p); +// CHECK-EMPTY: +// CHECK: void test2(int * oriPtr) { +// CHECK-NEXT: int * p = &*oriPtr; +// CHECK-NEXT: free_owned_int(oriPtr); +// CHECK-NEXT: use_mut(p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_nullability/bsc_nullability.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_nullability/bsc_nullability.cbs new file mode 100644 index 0000000000000000000000000000000000000000..f1a872a2c88d44f5210b3a0e98fefbb1c627b471 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_nullability/bsc_nullability.cbs @@ -0,0 +1,66 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *borrow _Nullable foo1(int* borrow a); +safe void test1(void) { + int a = 10; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-nullability" + int *borrow p1 = foo1(&mut a); // nonnull pointer cannot be assigned by nullable pointer + int *borrow _Nullable p2 = foo1(&mut a); + #pragma GCC diagnostic pop +} + +void test2(void) { + int *borrow p = nullptr; + unsafe { + int *borrow p2 = nullptr; + } +} + +struct S { + int a; +}; +safe void test3(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-nullability" + struct S *borrow _Nullable p = nullptr; + if (p != nullptr) { + p->a = 10; // cannot access member through nullable pointer + } else { + p->a = 20; + } + p->a = 30; // cannot access member through nullable pointer + #pragma GCC diagnostic pop +} + +// CHECK: struct S; +// CHECK-NEXT: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: int * foo1(int * a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int a = 10; +// CHECK-NEXT: int * p1 = foo1(&a); +// CHECK-NEXT: int * p2 = foo1(&a); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: { +// CHECK-NEXT: int * p2 = 0; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test3(void) { +// CHECK-NEXT: struct S * p = 0; +// CHECK-NEXT: if (p != 0) { +// CHECK-NEXT: p->a = 10; +// CHECK-NEXT: } else { +// CHECK-NEXT: p->a = 20; +// CHECK-NEXT: } +// CHECK-NEXT: p->a = 30; +// CHECK-NEXT: } \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_ownership/bsc_ownership.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_ownership/bsc_ownership.cbs new file mode 100644 index 0000000000000000000000000000000000000000..444f38a93571d0356725f65a8a7fc0b9c8fce45f --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_ownership/bsc_ownership.cbs @@ -0,0 +1,68 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void free_owned_new(void * owned p); + +typedef struct A { + int * owned a; +} A; + +void test1(A * owned p) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-ownership" + free_owned_new((void * owned)p); // invalid cast to `void * owned` of not all moved value: `p`, p.a is owned + #pragma GCC diagnostic pop +} + +int * owned test2() { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-ownership" + int * owned a; + return a; // use of uninitialized value: `a` + #pragma GCC diagnostic pop +} + +safe T *owned safe_malloc(T t); +safe void test3(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-ownership" + int *owned _Nullable p = nullptr; + int cond = 1; + if (cond) { + p = safe_malloc(10); + *p = 10; + } + p = nullptr; //assign to owned value: `p` + #pragma GCC diagnostic pop +} + +// CHECK: struct A; +// CHECK-NEXT: typedef struct A A; +// CHECK-EMPTY: +// CHECK: struct A { +// CHECK-NEXT: int * a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: static int * safe_malloc_int(int t); +// CHECK-EMPTY: +// CHECK: void free_owned_new(void * p); +// CHECK-EMPTY: +// CHECK: void test1(A * p) { +// CHECK-NEXT: free_owned_new((void *)p); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: int * test2(void) { +// CHECK-NEXT: int * a; +// CHECK-NEXT: return a; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test3(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: int cond = 1; +// CHECK-NEXT: if (cond) { +// CHECK-NEXT: p = safe_malloc_int(10); +// CHECK-NEXT: *p = 10; +// CHECK-NEXT: } +// CHECK-NEXT: p = 0; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_safety_check/bsc_safety_check.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_safety_check/bsc_safety_check.cbs new file mode 100644 index 0000000000000000000000000000000000000000..aa447a1420108a789eb24319661c11c753482bbe --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Mixed/bsc_safety_check/bsc_safety_check.cbs @@ -0,0 +1,30 @@ +// RUN: %clang -Eno-bsc-safety-check -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *owned _Nonnull safe_malloc(int a); +safe void test1(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ebsc-safety-check" + int *owned _Nullable p1 = nullptr; + int *owned _Nonnull p2 = p1; // 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); // nonnull pointer cannot be assigned by nullable pointer + p5 = p1; // use of moved value: `p1` + p5 = p3; // memory leak of value: `p5` + #pragma GCC diagnostic pop +} + +// CHECK: int * safe_malloc(int a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int * p1 = 0; +// CHECK-NEXT: int * p2 = p1; +// CHECK-NEXT: int * p3 = safe_malloc(5); +// CHECK-NEXT: int * p4 = p3; +// CHECK-NEXT: int * p5 = safe_malloc(5); +// CHECK-NEXT: p5 = p1; +// CHECK-NEXT: p5 = p3; +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nonnull/assign_nonnull.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nonnull/assign_nonnull.cbs new file mode 100644 index 0000000000000000000000000000000000000000..f640d9fa9f5bd072dda3c6be8db08c5262c784cf --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nonnull/assign_nonnull.cbs @@ -0,0 +1,39 @@ +// RUN: %clang -nullability-check=all -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe int *borrow _Nullable foo1(int* borrow a); +safe void test1(void) { + int a = 10; + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eassign-nonnull" + int *borrow p1 = foo1(&mut a); // nonnull pointer cannot be assigned by nullable pointer + int *borrow _Nullable p2 = foo1(&mut a); + #pragma GCC diagnostic pop +} + +void test2(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eassign-nonnull" + int *borrow p = nullptr; // nonnull pointer cannot be assigned by nullable pointer + unsafe { + int *borrow p2 = nullptr; // nonnull pointer cannot be assigned by nullable pointer + } + #pragma GCC diagnostic pop +} + +// CHECK: int * foo1(int * a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int a = 10; +// CHECK-NEXT: int * p1 = foo1(&a); +// CHECK-NEXT: int * p2 = foo1(&a); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: { +// CHECK-NEXT: int * p2 = 0; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nullable/assign_nullable.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nullable/assign_nullable.cbs new file mode 100644 index 0000000000000000000000000000000000000000..c379cab9805754da51ac35edc160962f8c0bdaf2 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/assign_nullable/assign_nullable.cbs @@ -0,0 +1,35 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +struct S { + int a; +}; +safe void test1(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eassign-nullable" + struct S *borrow _Nullable p = nullptr; + if (p != nullptr) { + p->a = 10; + } else { + p->a = 20; // cannot access member through nullable pointer + } + p->a = 30; // cannot access member through nullable pointer + #pragma GCC diagnostic pop +} + +// CHECK: struct S; +// CHECK-NEXT: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: struct S * p = 0; +// CHECK-NEXT: if (p != 0) { +// CHECK-NEXT: p->a = 10; +// CHECK-NEXT: } else { +// CHECK-NEXT: p->a = 20; +// CHECK-NEXT: } +// CHECK-NEXT: p->a = 30; +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/cast_nullable/cast_nullable.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/cast_nullable/cast_nullable.cbs new file mode 100644 index 0000000000000000000000000000000000000000..f027ea98a167ca027842e03ba1ca51a5e3e7b190 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/cast_nullable/cast_nullable.cbs @@ -0,0 +1,34 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe T *owned safe_malloc(T t); +safe void safe_free(void *owned p); + +safe void test1(void) { + int *owned _Nullable p = nullptr; + int cond = 1; + if (cond) { + p = safe_malloc(10); + *p = 10; + } + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ecast-nullable" + safe_free((void *owned)p); // cannot cast nullable pointer to nonnull type + #pragma GCC diagnostic pop +} + +// CHECK: static int * safe_malloc_int(int t); +// CHECK-EMPTY: +// CHECK: void safe_free(void * p); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: int cond = 1; +// CHECK-NEXT: if (cond) { +// CHECK-NEXT: p = safe_malloc_int(10); +// CHECK-NEXT: *p = 10; +// CHECK-NEXT: } +// CHECK-NEXT: safe_free((void *)p); +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/deref_nullable/deref_nullable.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/deref_nullable/deref_nullable.cbs new file mode 100644 index 0000000000000000000000000000000000000000..125e86949b3005ecb2119c568ee73561a4c5995c --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/deref_nullable/deref_nullable.cbs @@ -0,0 +1,36 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +struct S { + int *borrow _Nullable p; +}; + +safe void test1(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ederef-nullable" + struct S s = { .p = nullptr }; + if (s.p != nullptr) { + *s.p = 10; + } else { + *s.p = 20; // nullable pointer cannot be dereferenced + } + *s.p = 30; // nullable pointer cannot be dereferenced + #pragma GCC diagnostic pop +} + +// CHECK: struct S; +// CHECK-NEXT: struct S { +// CHECK-NEXT: int * p; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: struct S s = {.p = 0}; +// CHECK-NEXT: if (s.p != 0) { +// CHECK-NEXT: *s.p = 10; +// CHECK-NEXT: } else { +// CHECK-NEXT: *s.p = 20; +// CHECK-NEXT: } +// CHECK-NEXT: *s.p = 30; +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/pass_nullable/pass_nullable.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/pass_nullable/pass_nullable.cbs new file mode 100644 index 0000000000000000000000000000000000000000..35f82e859a843bb9c59f06a726cb7e0edd935109 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/pass_nullable/pass_nullable.cbs @@ -0,0 +1,34 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe void foo1(int *borrow a); +safe void test1(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Epass-nullable" + 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); // cannot pass nullable pointer argument + foo1(&mut a); + foo1(nullptr); // cannot pass nullable pointer argument + #pragma GCC diagnostic pop +} + +// CHECK: void foo1(int * a); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int a = 10; +// CHECK-NEXT: int * p1 = &a; +// CHECK-NEXT: foo1(p1); +// CHECK-NEXT: int * p2 = &a; +// CHECK-NEXT: foo1(p2); +// CHECK-NEXT: int * p3 = 0; +// CHECK-NEXT: foo1(p3); +// CHECK-NEXT: foo1(&a); +// CHECK-NEXT: foo1(0); +// CHECK-NEXT: } +// CHECK-EMPTY: \ No newline at end of file diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/return_nullable/return_nullable.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/return_nullable/return_nullable.cbs new file mode 100644 index 0000000000000000000000000000000000000000..489c5a9fe96a5f90bd8d999c18f3923ef5fad133 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Nullability/return_nullable/return_nullable.cbs @@ -0,0 +1,14 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Ereturn-nullable" +safe int *borrow foo1(int *borrow _Nullable a) { + return a; // cannot return nullable pointer type +} +#pragma GCC diagnostic pop + +// CHECK: int * foo1(int * a) { +// CHECK-NEXT: return a; +// CHECK-NEXT: } +// CHECK-EMPTY: diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/assign_owned/assign_owned.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/assign_owned/assign_owned.cbs new file mode 100644 index 0000000000000000000000000000000000000000..586428073c6ce5f4cec32b50134145563a5d8efd --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/assign_owned/assign_owned.cbs @@ -0,0 +1,29 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +safe T *owned safe_malloc(T t); +safe void test1(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Eassign-owned" + int *owned _Nullable p = nullptr; + int cond = 1; + if (cond) { + p = safe_malloc(10); + *p = 10; + } + p = nullptr; //assign to owned value: `p` + #pragma GCC diagnostic pop +} + +// CHECK: static int * safe_malloc_int(int t); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int * p = 0; +// CHECK-NEXT: int cond = 1; +// CHECK-NEXT: if (cond) { +// CHECK-NEXT: p = safe_malloc_int(10); +// CHECK-NEXT: *p = 10; +// CHECK-NEXT: } +// CHECK-NEXT: p = 0; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/cast_owned/cast_owned.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/cast_owned/cast_owned.cbs new file mode 100644 index 0000000000000000000000000000000000000000..3bada756a1c8fe40fdb430942009d0957060667e --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/cast_owned/cast_owned.cbs @@ -0,0 +1,29 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +void free_owned_new(void * owned p); + +typedef struct A { + int * owned a; +} A; + +void test1(A * owned p) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Ecast-owned" + free_owned_new((void * owned)p); // invalid cast to `void * owned` of not all moved value: `p`, p.a is owned + #pragma GCC diagnostic pop +} + +// CHECK: struct A; +// CHECK-NEXT: typedef struct A A; +// CHECK-EMPTY: +// CHECK: struct A { +// CHECK-NEXT: int * a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void free_owned_new(void * p); +// CHECK-EMPTY: +// CHECK: void test1(A * p) { +// CHECK-NEXT: free_owned_new((void *)p); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/memory_leak/memory_leak.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/memory_leak/memory_leak.cbs new file mode 100644 index 0000000000000000000000000000000000000000..f921d475e1002949b678cd86808123ea9b7b47b3 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/memory_leak/memory_leak.cbs @@ -0,0 +1,76 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +typedef int* owned myInt; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Echeck-memory-leak" +void test1() { + int x = 10; + int* p = &x; + myInt p1 = (myInt)p; +} // memory leak of value: `p1` +#pragma GCC diagnostic pop + +owned struct S { +public: + int a; +}; + +trait T { +}; + +impl trait T for S; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Echeck-memory-leak" +void test2(S* owned sp) { + trait T* owned t = sp; +} // field memory leak of value: `t`, t.data is leak +#pragma GCC diagnostic pop + +// CHECK: struct S; +// CHECK-NEXT: struct __Trait_T_Vtable; +// CHECK-NEXT: struct __Trait_T; +// CHECK-NEXT: struct __Trait_T_Owned; +// CHECK-NEXT: struct __Trait_T_Borrow; +// CHECK-NEXT: typedef int * myInt; +// CHECK-EMPTY: +// CHECK: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Vtable { +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T { +// CHECK-NEXT: void *data; +// CHECK-NEXT: struct __Trait_T_Vtable *vtable; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Owned { +// CHECK-NEXT: void * data; +// CHECK-NEXT: struct __Trait_T_Vtable *vtable; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Borrow { +// CHECK-NEXT: void * data; +// CHECK-NEXT: struct __Trait_T_Vtable *vtable; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this); +// CHECK-EMPTY: +// CHECK: void test1(void) { +// CHECK-NEXT: int x = 10; +// CHECK-NEXT: int *p = &x; +// CHECK-NEXT: myInt p1 = (myInt)p; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: struct __Trait_T_Vtable __owned_struct_S_trait_T = {}; +// CHECK-EMPTY: +// CHECK: void test2( struct S * sp) { +// CHECK-NEXT: struct __Trait_T_Owned t = {sp, &__owned_struct_S_trait_T}; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this) { +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_moved_owned/use_moved_owned.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_moved_owned/use_moved_owned.cbs new file mode 100644 index 0000000000000000000000000000000000000000..104a307840e43180270cc89eab4e6813aa93f7d5 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_moved_owned/use_moved_owned.cbs @@ -0,0 +1,119 @@ +// RUN: %clang -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +owned struct A { + public: + int x; + int y ; + ~A(A this){ + } +}; +int f(A a){ return 1;} + +int test1() { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Euse-moved-owned" + A a = {1, 2}; + A b = a; + f(a); // use of moved value: `a` + return 0; + #pragma GCC diagnostic pop +} + +owned struct S { +public: + int a; + int b; +}; + +owned struct T { +public: + S s; +}; + +struct B { + T t; + int c; +}; + +void test2(struct B sb) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Euse-moved-owned" + S s = sb.t.s; + T t = sb.t; // use of partially moved value: `sb.t`, sb.t.s is moved + #pragma GCC diagnostic pop +} + +// CHECK: struct A; +// CHECK-NEXT: struct S; +// CHECK-NEXT: struct T; +// CHECK-NEXT: struct B; +// CHECK-NEXT: struct A { +// CHECK-NEXT: int x; +// CHECK-NEXT: int y; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct S { +// CHECK-NEXT: int a; +// CHECK-NEXT: int b; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct T { +// CHECK-NEXT: struct S s; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: struct B { +// CHECK-NEXT: struct T t; +// CHECK-NEXT: int c; +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK: static void struct_A_D( struct A this); +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this); +// CHECK-EMPTY: +// CHECK: void struct_T_D( struct T this); +// CHECK-EMPTY: +// CHECK: int f( struct A a) { +// CHECK-NEXT: _Bool a_is_moved = 0; +// CHECK-NEXT: if (!a_is_moved) +// CHECK-NEXT: struct_A_D(a); +// CHECK-NEXT: return 1; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: int test1(void) { +// CHECK-NEXT: struct A a = {1, 2}; +// CHECK-NEXT: _Bool a_is_moved = 0; +// CHECK-NEXT: struct A b = a; +// CHECK-NEXT: _Bool b_is_moved = 0; +// CHECK-NEXT: a_is_moved = 1; +// CHECK-NEXT: f(a); +// CHECK-NEXT: a_is_moved = 1; +// CHECK-NEXT: if (!b_is_moved) +// CHECK-NEXT: struct_A_D(b); +// CHECK-NEXT: if (!a_is_moved) +// CHECK-NEXT: struct_A_D(a); +// CHECK-NEXT: return 0; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void test2(struct B sb) { +// CHECK-NEXT: struct S s = sb.t.s; +// CHECK-NEXT: _Bool s_is_moved = 0; +// CHECK-NEXT: struct T t = sb.t; +// CHECK-NEXT: _Bool t_is_moved = 0; +// CHECK-NEXT: if (!t_is_moved) +// CHECK-NEXT: struct_T_D(t); +// CHECK-NEXT: if (!s_is_moved) +// CHECK-NEXT: struct_S_D(s); +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: static void struct_A_D( struct A this) { +// CHECK-NEXT: _Bool this_is_moved = 0; +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void struct_S_D( struct S this) { +// CHECK-NEXT: } +// CHECK-EMPTY: +// CHECK: void struct_T_D( struct T this) { +// CHECK-NEXT: struct_S_D(this.s); +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_uninit_owned/use_uninit_owned.cbs b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_uninit_owned/use_uninit_owned.cbs new file mode 100644 index 0000000000000000000000000000000000000000..97cd0bdd846dbf814a61e7b3153c51859e3d7454 --- /dev/null +++ b/clang/test/BSC/Positive/BlockingError/Pragma/Ownership/use_uninit_owned/use_uninit_owned.cbs @@ -0,0 +1,16 @@ +// RUN: %clang -Eno-use-uninit-owned -rewrite-bsc %s -o %t-rw.c +// RUN: FileCheck --input-file=%t-rw.c %s +// expected-no-diagnostics + +int * owned test() { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Euse-uninit-owned" + int * owned a; + return a; // use of uninitialized value: `a` + #pragma GCC diagnostic pop +} + +// CHECK: int * test(void) { +// CHECK-NEXT: int * a; +// CHECK-NEXT: return a; +// CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/Driver/rewrite-bsc/Ownership/rewrite_bsc_borrow/rewrite_bsc_borrow.cbs b/clang/test/BSC/Positive/Driver/rewrite-bsc/Ownership/rewrite_bsc_borrow/rewrite_bsc_borrow.cbs index 43b6165ff56ef1d21b3ffc242f68b1affbb46ca6..5d21dbcedd28c01ec1c9913e8f846b566d607dae 100644 --- a/clang/test/BSC/Positive/Driver/rewrite-bsc/Ownership/rewrite_bsc_borrow/rewrite_bsc_borrow.cbs +++ b/clang/test/BSC/Positive/Driver/rewrite-bsc/Ownership/rewrite_bsc_borrow/rewrite_bsc_borrow.cbs @@ -19,15 +19,15 @@ void test1() { int x1 = 0; int x = 0; float y = 1.0; + const int * borrow p2 = &const x; + const int * borrow p4= (const int * borrow)&x; + int * borrow p7 = &mut *(&x); int * borrow p = &mut x; float * borrow p1 = &mut y; - const int * borrow p2 = &const x; myInt p3 = p; - const int * borrow p4= (const int * borrow)&x; int * owned p5 = (int * owned)&x1; const int * borrow b6 = &const *p3; - int * borrow p7 = &mut *(&x); p7 = &mut *p; int * p8 = (int *)p5; } @@ -59,14 +59,14 @@ int main() { // CHECK-NEXT: int x1 = 0; // CHECK-NEXT: int x = 0; // CHECK-NEXT: float y = 1.; +// CHECK-NEXT: const int * p2 = &x; +// CHECK-NEXT: const int * p4 = (const int *)&x; +// CHECK-NEXT: int * p7 = &*(&x); // CHECK-NEXT: int * p = &x; // CHECK-NEXT: float * p1 = &y; -// CHECK-NEXT: const int * p2 = &x; // CHECK-NEXT: myInt p3 = p; -// CHECK-NEXT: const int * p4 = (const int *)&x; // CHECK-NEXT: int * p5 = (int *)&x1; // CHECK-NEXT: const int * b6 = &*p3; -// CHECK-NEXT: int * p7 = &*(&x); // CHECK-NEXT: p7 = &*p; // CHECK-NEXT: int *p8 = (int *)p5; // CHECK-NEXT: } diff --git a/clang/test/BSC/Positive/OperatorOverload/Subscript/Subscript.cbs b/clang/test/BSC/Positive/OperatorOverload/Subscript/Subscript.cbs index c866c90474a04bb0abe2af36caed58b1a14ab6e9..b2630f15530d3008641e788ca70008eaf81edc55 100644 --- a/clang/test/BSC/Positive/OperatorOverload/Subscript/Subscript.cbs +++ b/clang/test/BSC/Positive/OperatorOverload/Subscript/Subscript.cbs @@ -150,7 +150,9 @@ int test5() { struct DataArray dataArray; dataArray[0].data = 1; dataArray[1].data = 2; - if (dataArray[0].data + dataArray[1].data != 3) { + int data1 = dataArray[0].data; + int data2 = dataArray[1].data; + if (data1 + data2 != 3) { return -1; } return 0; @@ -309,7 +311,9 @@ int main() { // CHECK-NEXT: struct DataArray_int dataArray; // CHECK-NEXT: (*deref5_int(&dataArray, 0)).data = 1; // CHECK-NEXT: (*deref5_int(&dataArray, 1)).data = 2; -// CHECK-NEXT: if ((*deref5_int(&dataArray, 0)).data + (*deref5_int(&dataArray, 1)).data != 3) { +// CHECK-NEXT: int data1 = (*deref5_int(&dataArray, 0)).data; +// CHECK-NEXT: int data2 = (*deref5_int(&dataArray, 1)).data; +// CHECK-NEXT: if (data1 + data2 != 3) { // CHECK-NEXT: return -1; // CHECK-NEXT: } // CHECK-NEXT: return 0; diff --git a/clang/test/BSC/Positive/OperatorOverload/deref/deref.cbs b/clang/test/BSC/Positive/OperatorOverload/deref/deref.cbs index 04f9e46521059b194c17fba5308023f6fed99973..e73b847fa9c69d78ce49d6fcbf20c19dbd09bb9a 100644 --- a/clang/test/BSC/Positive/OperatorOverload/deref/deref.cbs +++ b/clang/test/BSC/Positive/OperatorOverload/deref/deref.cbs @@ -176,14 +176,12 @@ int main() { // CHECK-DAG: struct I { // CHECK-DAG-NEXT: int a; // CHECK-DAG-NEXT: }; -// CHECK: struct Rc_int { -// CHECK-NEXT: struct RcData_int *ptr; -// CHECK-NEXT: }; -// CHECK-EMPTY: -// CHECK: struct RcData_int { -// CHECK-NEXT: int data; -// CHECK-NEXT: }; -// CHECK-EMPTY: +// CHECK-DAG: struct Rc_int { +// CHECK-DAG-NEXT: struct RcData_int *ptr; +// CHECK-DAG-NEXT: }; +// CHECK-DAG: struct RcData_int { +// CHECK-DAG-NEXT: int data; +// CHECK-DAG-NEXT: }; // CHECK-DAG: struct F { // CHECK-DAG-NEXT: struct E e; // CHECK-DAG-NEXT: }; diff --git a/clang/test/BSC/Positive/OperatorOverload/member_ref/member_ref.cbs b/clang/test/BSC/Positive/OperatorOverload/member_ref/member_ref.cbs index e5aea663a858df32d5de77c14eed0b7ec196bcbd..eb67c744f55af603a86f50aa625a1c6f2eacc0c7 100644 --- a/clang/test/BSC/Positive/OperatorOverload/member_ref/member_ref.cbs +++ b/clang/test/BSC/Positive/OperatorOverload/member_ref/member_ref.cbs @@ -224,40 +224,35 @@ int main(){ // CHECK-NEXT: struct RcData_int; // CHECK-NEXT: struct Rc_int; // CHECK-NEXT: struct RcRecord_int; -// CHECK-DAG: struct A { +// CHECK-DAG: struct A { // CHECK-DAG-NEXT: int m; // CHECK-DAG-NEXT: }; // CHECK-DAG: struct AP { // CHECK-DAG-NEXT: struct A *a; // CHECK-DAG-NEXT: }; -// CHECK-DAG: struct B { -// CHECK-DAG-NEXT: int x; -// CHECK-DAG-NEXT: int y; -// CHECK-DAG-NEXT: }; +// CHECK-DAG-: struct B { +// CHECK-DAG--NEXT: int x; +// CHECK-DAG--NEXT: int y; +// CHECK-DAG--NEXT: }; // CHECK-DAG: struct BP { // CHECK-DAG-NEXT: struct A *a; // CHECK-DAG-NEXT: struct B *b; // CHECK-DAG-NEXT: }; -// CHECK: struct CP { -// CHECK-NEXT: struct A * a; -// CHECK-NEXT: }; -// CHECK-EMPTY: -// CHECK: struct DP { -// CHECK-NEXT: struct A * a; -// CHECK-NEXT: }; -// CHECK-EMPTY: -// CHECK: struct FP { -// CHECK-NEXT: struct A* a; -// CHECK-NEXT: }; -// CHECK-EMPTY: -// CHECK: struct Rc_int { -// CHECK-NEXT: struct RcData_int *ptr; -// CHECK-NEXT: }; -// CHECK-EMPTY: -// CHECK: struct RcData_int { -// CHECK-NEXT: int data; -// CHECK-NEXT: }; -// CHECK-EMPTY: +// CHECK-DAG: struct CP { +// CHECK-DAG-NEXT: struct A * a; +// CHECK-DAG-NEXT: }; +// CHECK-DAG: struct DP { +// CHECK-DAG-NEXT: struct A * a; +// CHECK-DAG-NEXT: }; +// CHECK-DAG: struct FP { +// CHECK-DAG-NEXT: struct A* a; +// CHECK-DAG-NEXT: }; +// CHECK-DAG: struct Rc_int { +// CHECK-DAG-NEXT: struct RcData_int *ptr; +// CHECK-DAG-NEXT: }; +// CHECK-DAG: struct RcData_int { +// CHECK-DAG-NEXT: int data; +// CHECK-DAG-NEXT: }; // CHECK: struct GP { // CHECK-NEXT: struct FP fp; // CHECK-NEXT: }; diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs index 800404859deaf66f90e4f33d5e3c81b229fdb504..2e689a722a869299fdc1bdb9399e914ade1b13b2 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/at_most_one_mut_borrow/at_most_one_mut_borrow.cbs @@ -43,11 +43,11 @@ void test2() { void test3() { int a = 5; - int b = 5; + int b = 5, c = 5; int *owned oriPtr = safe_malloc(0); int *borrow p1 = &mut a; int *borrow p2 = &mut b; - if (a > 0) + if (c > 0) p1 = &mut * oriPtr; else p2 = &mut * oriPtr; @@ -66,8 +66,8 @@ void test4() { p2 = &mut * oriPtr; p1 = &mut * oriPtr; } else { // Dead Code - p1 = &mut * oriPtr; p2 = &mut * oriPtr; + p1 = &mut * oriPtr; } use_mut(p1); // use_mut(p2); @@ -85,6 +85,7 @@ void test5() { // use_mut(p1); } + // CHECK-L: #line 11 "{{.*}}.cbs" // CHECK-L-NEXT: void use_mut(int * p) { // CHECK-L-NEXT: } @@ -116,13 +117,13 @@ void test5() { // CHECK-L: #line 44 "{{.*}}.cbs" // CHECK-L-NEXT: void test3(void) { // CHECK-L-NEXT: int a = 5; -// CHECK-L-NEXT: int b = 5; +// CHECK-L-NEXT: int b = 5, c = 5; // CHECK-L-NEXT: int * oriPtr = safe_malloc_int(0); // CHECK-L-NEXT: int * p1 = &a; // CHECK-L-NEXT: int * p2 = &b; -// CHECK-L-NEXT: if (a > 0) +// CHECK-L-NEXT: if (c > 0) // CHECK-L-NEXT: p1 = &*oriPtr; -// CHECK-L-NEXT: else +// CHECK-L-NEXT: else // CHECK-L-NEXT: p2 = &*oriPtr; // CHECK-L-NEXT: use_mut(p1); // CHECK-L-NEXT: use_mut(p2); @@ -140,8 +141,8 @@ void test5() { // CHECK-L-NEXT: p2 = &*oriPtr; // CHECK-L-NEXT: p1 = &*oriPtr; // CHECK-L-NEXT: } else { -// CHECK-L-NEXT: p1 = &*oriPtr; // CHECK-L-NEXT: p2 = &*oriPtr; +// CHECK-L-NEXT: p1 = &*oriPtr; // CHECK-L-NEXT: } // CHECK-L-NEXT: use_mut(p1); // CHECK-L-NEXT: free_owned_int(oriPtr); diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/borrow_with_assert/borrow_with_assert.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/borrow_with_assert/borrow_with_assert.cbs new file mode 100644 index 0000000000000000000000000000000000000000..554bc18c8dda04051f936cd457476f9e82a76a95 --- /dev/null +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/borrow_with_assert/borrow_with_assert.cbs @@ -0,0 +1,20 @@ +// RUN: %clang %s -o %test.output +// RUN: %test.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 + +void test() { + int x = 1; + int* borrow p = &mut x; + *p = 2; + assert(*p == 2); +} + +int main() { + test(); + return 0; +} diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/read_when_mut_borrowed/read_when_mut_borrowed.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/read_when_mut_borrowed/read_when_mut_borrowed.cbs new file mode 100644 index 0000000000000000000000000000000000000000..736c8111aff7a84231c94a8bf90d8f9c02e22c5c --- /dev/null +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/read_when_mut_borrowed/read_when_mut_borrowed.cbs @@ -0,0 +1,24 @@ +// RUN: %clang %s -o %test.output +// RUN: %test.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 + +struct Foo { + int a; +}; + +void use_immut(const struct Foo *borrow p) { + int b = p->a; +} + +int main() { + struct Foo foo = {.a = 1}; + const struct Foo *borrow foo_const_mut = &const foo; + printf("%d\n", foo.a); + use_immut(foo_const_mut); + return 0; +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_borrow.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_borrow.cbs index b9bf76fe82cec4f0322b16d8bf29d7d96ec97c23..df163c431250dcb3f0678eecc93f4834e8d88ee7 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_borrow.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_borrow.cbs @@ -72,7 +72,7 @@ int* borrow test11(int *borrow p, int a) { if (1) { return p; } - return &mut a; // Dead Code, this ReturnStmt is unreachable. + // return &mut a; // Dead Code, this ReturnStmt is unreachable. } int* borrow test12(int* borrow p) { @@ -81,7 +81,7 @@ int* borrow test12(int* borrow p) { free_owned(oriPtr); return p; } - return &mut * oriPtr; // Dead Code, this ReturnStmt is unreachable. +// return &mut * oriPtr; // Dead Code, this ReturnStmt is unreachable. } struct S { int * borrow p; }; @@ -96,16 +96,17 @@ struct S test14(struct S s, int* borrow p) { int main() { int local = 5; + int *p = &local; test1(&const local); test2(&mut local); test3(&const local); test4(&mut local); test5(&mut local); - test6(&mut local, &local); - test7(&mut local, &local); + test6(&mut local, p); + test7(&mut local, p); test8(&mut local); test9(&mut local); - test10(&mut local, &local); + test10(&mut local, p); test11(&mut local, 5); test12(&mut local); struct S s = { .p = &mut local }; diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_not_local_borrow.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_not_local_borrow.cbs index 6c5f9b336bf7a5f210eb45b9427ca1d5f6efaf41..b642b6835cf6a1e7af3a50f050f5fa3c4edc5902 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_not_local_borrow.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow/return_not_local_borrow.cbs @@ -7,11 +7,11 @@ struct S { - int* borrow a; + int *borrow a; }; struct S f(struct S ss) { - struct S* ptr = &ss; + struct S *ptr = &ss; struct S t = *ptr; return t; } @@ -26,4 +26,4 @@ int main() { struct S k = *m; *k.a = 10; struct S j = **n; -} +} \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow_from_raw_pointer/return_borrow_from_raw_pointer.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow_from_raw_pointer/return_borrow_from_raw_pointer.cbs index 48b98d930446fff40d3b59d6cfda85eb495b64fd..e163e8ead79b5acc750846f3d9c145baa015e0cd 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow_from_raw_pointer/return_borrow_from_raw_pointer.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/return_borrow_from_raw_pointer/return_borrow_from_raw_pointer.cbs @@ -14,108 +14,101 @@ struct Q { }; int *borrow test1(struct P *borrow p) { - return &mut *p->b; + return &mut *p->b; } int *borrow test2(struct P *p, int *borrow p1) { - return &mut *p->b; + return &mut *p->b; } int *borrow test3(struct P p, int *borrow p1) { - return &mut *p.b; + return &mut *p.b; } int *borrow test4(struct Q *borrow q) { - return &mut *q->g.b; + return &mut *q->g.b; } int *borrow test5(struct Q *q, int *borrow p) { - return &mut *q->g.b; + return &mut *q->g.b; } int *borrow test6(struct Q q, int *borrow p) { - return &mut *q.g.b; + return &mut *q.g.b; } int *borrow test7(int *borrow p) { - struct Q q; - int *borrow r = &mut *q.g.b; - return r; + struct Q q; + int *borrow r = &mut *q.g.b; + return r; } int *borrow test8(int *borrow p) { - struct Q q; - struct Q *q1 = &q; - int *borrow r = &mut *q1->g.b; - return r; -} - -int *borrow test9(int *borrow p) { - struct Q q; - struct Q *borrow q1 = &mut q; - int *borrow r = &mut *q1->g.b; - return r; + struct Q q; + struct Q *q1 = &q; + int *borrow r = &mut *q1->g.b; + return r; } int *borrow test10(struct P *borrow p, int index) { - return &mut p->b[index]; + return &mut p->b[index]; } int *borrow test11(struct P *p, int index, int *borrow p1) { - return &mut p->b[index]; + return &mut p->b[index]; } -int *borrow test12(struct P p, int index, int *borrow p1) { - return &mut p.b[index]; -} +// int *borrow test12(struct P p, int index, int *borrow p1) { +// return &mut p.b[index]; +// } int *borrow test13(struct Q *borrow q, int index) { - return &mut q->g.b[index]; + return &mut q->g.b[index]; } int *borrow test14(struct Q *q, int index, int *borrow p) { - return &mut q->g.b[index]; + return &mut q->g.b[index]; } -int *borrow test15(struct Q q, int index, int *borrow p) { - return &mut q.g.b[index]; -} +// int *borrow test15(struct Q q, int index, int *borrow p) { +// return &mut q.g.b[index]; +// } struct M { - int* owned a; + int *owned a; }; struct N { - struct M* m; + struct M *m; }; int *borrow test16(struct N n, int *borrow p) { - return &mut *n.m->a; + return &mut *n.m->a; } int *owned *borrow test17(struct N n, int *borrow p) { - return &mut n.m->a; + return &mut n.m->a; } int *borrow struct N::test18(This* borrow this) { - return &mut *this->m->a; + return &mut *this->m->a; } int *borrow struct N::test19(This* this, int* borrow p) { - return &mut *this->m->a; + return &mut *this->m->a; } owned struct S { public: - int *owned *borrow a; //成员变量为 owned borrow - int *owned *borrow test20(S *this) { - int *owned *borrow a = this->a; - return a; - } + int *owned *borrow a; + int *owned *borrow test20(S *this) { + int *owned *borrow a = this->a; + return a; + } }; int *owned *borrow S::test21(This* this) { - return this->a; + return this->a; } int main() { @@ -130,14 +123,13 @@ int main() { test6(q, &mut local); test7(&mut local); test8(&mut local); - test9(&mut local); test10(&mut p, 5); test11(&p, 5, &mut local); - test12(p, 5, &mut local); + // test12(p, 5, &mut local); test13(&mut q, 5); test14(&q, 5, &mut local); - test15(q, 5, &mut local); - int *owned c = (int* owned)&local; + // test15(q, 5, &mut local); + int *owned c = (int *owned)&local; struct M m = { .a = c }; struct N n = { .m = &m }; test16(n, &mut local); @@ -147,6 +139,6 @@ int main() { S s = { .a = &mut m.a }; s.test20(); s.test21(); - int* d = (int*) m.a; + int *d = (int *) m.a; return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call/struct_with_borrow_init_by_func_call.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call/struct_with_borrow_init_by_func_call.cbs index 9829bf01c94d85281a9d957c52c2746627f9bb63..b29e02bab75506ef91b2b63a6917a224ca19b6fa 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call/struct_with_borrow_init_by_func_call.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call/struct_with_borrow_init_by_func_call.cbs @@ -6,37 +6,39 @@ // expected-no-diagnostics #include + struct S { - int *borrow p1; - const int *borrow p2; + int *borrow p1; + const int *borrow p2; }; struct S struct S::new(int *borrow p1, const int *borrow p2) { - struct S s = { .p1 = p1, .p2 = p2 }; - return s; + struct S s = { .p1 = p1, .p2 = p2 }; + return s; } + void use(struct S s) {} int main() { - int a = 5; - int b = 10; - int* p1 = &a; - int* p2 = &b; - int* owned p3 = (int *owned)malloc(sizeof(int)); - int* owned p4 = (int *owned)malloc(sizeof(int)); - struct S s1 = struct S::new(&mut a, &const b); - struct S s2 = struct S::new(&mut *p1, &const *p2); - struct S s3 = struct S::new(&mut *p3, &const *p4); - use(s1); - use(s2); - use(s3); - s1 = struct S::new(&mut a, &const b); - s2 = struct S::new(&mut *p1, &const *p2); - s3 = struct S::new(&mut *p3, &const *p4); - use(s1); - use(s2); - use(s3); - free((int*)p3); - free((int*)p4); - return 0; + int a = 5; + int b = 10; + int *p1 = &a; + int *p2 = &b; + int *owned p3 = (int *owned)malloc(sizeof(int)); + int *owned p4 = (int *owned)malloc(sizeof(int)); + struct S s1 = struct S::new(&mut a, &const b); + struct S s2 = struct S::new(&mut *p1, &const *p2); + struct S s3 = struct S::new(&mut *p3, &const *p4); + use(s1); + use(s2); + use(s3); + s1 = struct S::new(&mut a, &const b); + s2 = struct S::new(&mut *p1, &const *p2); + s3 = struct S::new(&mut *p3, &const *p4); + use(s1); + use(s2); + use(s3); + free((int *)p3); + free((int *)p4); + return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call2/struct_with_borrow_init_by_func_call2.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call2/struct_with_borrow_init_by_func_call2.cbs index 610b39620784dd2a6b115284a832867b386e78fe..d1d8829346989e34dbdc4e3845efb19ae9b42e2c 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call2/struct_with_borrow_init_by_func_call2.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/struct_with_borrow_init_by_func_call2/struct_with_borrow_init_by_func_call2.cbs @@ -6,38 +6,37 @@ // expected-no-diagnostics #include + owned struct Option { public: - T * owned inner; - ~Option(This this) { - free((T*)this.inner); - } + T *owned inner; + ~Option(This this) { + free((T *)this.inner); + } }; owned struct OptionRefMut { public: - T *borrow p; + T *borrow p; }; safe OptionRefMut OptionRefMut::Some(T *borrow p) { - OptionRefMut rm = { .p = p }; - return rm; + OptionRefMut rm = { .p = p }; + return rm; }; -safe OptionRefMut Option::as_mut_1(Option* borrow this) -{ - OptionRefMut r = OptionRefMut::Some(&mut *this->inner); - return r; +safe OptionRefMut Option::as_mut_1(Option *borrow this) { + OptionRefMut r = OptionRefMut::Some(&mut *this->inner); + return r; } -safe OptionRefMut Option::as_mut_2(Option* borrow this) -{ - return OptionRefMut::Some(&mut *this->inner); +safe OptionRefMut Option::as_mut_2(Option *borrow this) { + return OptionRefMut::Some(&mut *this->inner); } int main() { - Option op = { .inner = (int *owned)malloc(sizeof(int)) }; - OptionRefMut rm1 = op.as_mut_1(); - OptionRefMut rm2 = op.as_mut_2(); - return 0; + Option op = { .inner = (int *owned)malloc(sizeof(int)) }; + OptionRefMut rm1 = op.as_mut_1(); + OptionRefMut rm2 = op.as_mut_2(); + return 0; } \ No newline at end of file diff --git a/clang/test/BSC/Positive/Ownership/borrow_check_rules/virtual_target_live_longer/virtual_target_live_longer.cbs b/clang/test/BSC/Positive/Ownership/borrow_check_rules/virtual_target_live_longer/virtual_target_live_longer.cbs index 778ef84031559e7bfc551767ec1ac27b7af3dec4..1c89e1837609e6c05e867b3cedf309446cdfea56 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_check_rules/virtual_target_live_longer/virtual_target_live_longer.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_check_rules/virtual_target_live_longer/virtual_target_live_longer.cbs @@ -4,37 +4,45 @@ // RUN: %clang %t-rw.c -o %t-rw.output // RUN: %t-rw.output // expected-no-diagnostics + #include + +struct S { + int a; + int *borrow p1; + int *owned p2; +}; + void use_mut(int *borrow p) {} -T* owned safe_malloc(T value) { - T * p = (T *) malloc( sizeof(T) ); + +T *owned safe_malloc(T value) { + T *p = (T *) malloc( sizeof(T) ); *p = value; - return (T* owned)p; + return (T *owned)p; } -void free_owned(T* owned p) { - free( (T*)p ); +void free_owned(T *owned p) { + free( (T *)p ); } void test1() { int *a = (int *)malloc(sizeof(int)); - int *borrow p = &mut * a; + int *borrow p = &mut *a; use_mut(p); free(a); } -void test2(int* a) { - int *borrow p = &mut * a; +void test2(int *a) { + int *borrow p = &mut *a; use_mut(p); free(a); } void test3(int *borrow p) { - int *borrow p1 = &mut * p; + int *borrow p1 = &mut *p; use_mut(p1); } -struct S { int a; int * borrow p1; int* owned p2; }; void test4(struct S s) { int *borrow p3 = &mut s.a; use_mut(p3); diff --git a/clang/test/BSC/Positive/Ownership/borrow_serialization/borrow_serialization.cbs b/clang/test/BSC/Positive/Ownership/borrow_serialization/borrow_serialization.cbs index 9d9d7e0501706fc078752aa4ff86095109551e46..0ca8227f8903c0690bf930b2f286ef75802acfe2 100644 --- a/clang/test/BSC/Positive/Ownership/borrow_serialization/borrow_serialization.cbs +++ b/clang/test/BSC/Positive/Ownership/borrow_serialization/borrow_serialization.cbs @@ -13,30 +13,30 @@ // RUN: | FileCheck --strict-whitespace %s struct R { - int * borrow p1; - const int * borrow p2; + int *borrow p1; + const int *borrow p2; }; -void int::foo(This * borrow this) {} +void int::foo(This *borrow this) {} -typedef int * borrow myInt; -typedef int* borrow (*PF2)(struct R); +typedef int *borrow myInt; +typedef int *borrow (*PF2)(struct R); void test() { - int x1 = 0; - int x = 0; - float y = 1.0; - int * borrow p = &mut x; - float * borrow p1 = &mut y; - const int * borrow p2 = &const x; - myInt p3 = p; - const int * borrow p4= (const int * borrow)&x; + int x1 = 0; + int x = 0; + float y = 1.0; + const int *borrow p4= (const int *borrow)&x; + const int *borrow p2 = &const x; + int *borrow p7 = &mut *(&x); + int *borrow p = &mut x; + float *borrow p1 = &mut y; + myInt p3 = p; - int * owned p5 = (int * owned)&x1; - const int * borrow b6 = &const *p3; - int * borrow p7 = &mut *(&x); - p7 = &mut *p; - int * p8 = (int *)p5; + int *owned p5 = (int *owned)&x1; + const int *borrow b6 = &const *p3; + p7 = &mut *p; + int *p8 = (int *)p5; } int main() { @@ -45,52 +45,52 @@ int main() { // CHECK: FunctionDecl 0x{{[^ ]*}} line:[[@LINE-21]]:6 test 'void (void)' // CHECK-NEXT: CompoundStmt 0x{{[^ ]*}} -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:9 used x1 'int' cinit -// CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} 'int' 0 -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:9 used x 'int' cinit -// CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} 'int' 0 -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:11 used y 'float' cinit -// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'float' -// CHECK-NEXT: FloatingLiteral 0x{{[^ ]*}} 'double' 1.000000e+00 -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:18 used p 'int *borrow' cinit -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *borrow' prefix '&' cannot overflow -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:20 p1 'float *borrow' cinit -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'float *borrow' prefix '&' cannot overflow -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'float' lvalue Var 0x{{[^ ]*}} 'y' 'float' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:24 p2 'const int *borrow' cinit -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'const int *borrow' prefix '&' cannot overflow -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:11 used p3 'myInt':'int *borrow' cinit -// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'int *borrow' -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int *borrow' lvalue Var 0x{{[^ ]*}} 'p' 'int *borrow' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:24 p4 'const int *borrow' cinit -// CHECK-NEXT: CStyleCastExpr 0x{{[^ ]*}} 'const int *borrow' -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *' prefix '&' cannot overflow -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:17 used p5 'int *owned' cinit -// CHECK-NEXT: CStyleCastExpr 0x{{[^ ]*}} 'int *owned' -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *' prefix '&' cannot overflow -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x1' 'int' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:24 b6 'const int *borrow' cinit -// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'int *borrow' -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'myInt':'int *borrow' lvalue Var 0x{{[^ ]*}} 'p3' 'myInt':'int *borrow' -// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} -// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:18 used p7 'int *borrow' cinit -// CHECK-NEXT: ParenExpr 0x{{[^ ]*}} 'int *' -// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *' prefix '&' cannot overflow -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' -// CHECK-NEXT: BinaryOperator 0x{{[^ ]*}} 'int *borrow' '=' -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int *borrow' lvalue Var 0x{{[^ ]*}} 'p7' 'int *borrow' -// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'int *borrow' -// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int *borrow' lvalue Var 0x{{[^ ]*}} 'p' 'int *borrow' \ No newline at end of file +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:7 used x1 'int' cinit +// CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} 'int' 0 +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:7 used x 'int' cinit +// CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} 'int' 0 +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:9 used y 'float' cinit +// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'float' +// CHECK-NEXT: FloatingLiteral 0x{{[^ ]*}} 'double' 1.000000e+00 +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:21 p4 'const int *borrow' cinit +// CHECK-NEXT: CStyleCastExpr 0x{{[^ ]*}} 'const int *borrow' +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *' prefix '&' cannot overflow +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:21 p2 'const int *borrow' cinit +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'const int *borrow' prefix '&' cannot overflow +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:15 used p7 'int *borrow' cinit +// CHECK-NEXT: ParenExpr 0x{{[^ ]*}} 'int *' +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *' prefix '&' cannot overflow +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:15 used p 'int *borrow' cinit +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *borrow' prefix '&' cannot overflow +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x' 'int' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:17 p1 'float *borrow' cinit +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'float *borrow' prefix '&' cannot overflow +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'float' lvalue Var 0x{{[^ ]*}} 'y' 'float' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:9 used p3 'myInt':'int *borrow' cinit +// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'int *borrow' +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int *borrow' lvalue Var 0x{{[^ ]*}} 'p' 'int *borrow' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:14 used p5 'int *owned' cinit +// CHECK-NEXT: CStyleCastExpr 0x{{[^ ]*}} 'int *owned' +// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'int *' prefix '&' cannot overflow +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int' lvalue Var 0x{{[^ ]*}} 'x1' 'int' +// CHECK-NEXT: DeclStmt 0x{{[^ ]*}} +// CHECK-NEXT: VarDecl 0x{{[^ ]*}} col:21 b6 'const int *borrow' cinit +// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'int *borrow' +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'myInt':'int *borrow' lvalue Var 0x{{[^ ]*}} 'p3' 'myInt':'int *borrow' +// CHECK-NEXT: BinaryOperator 0x{{[^ ]*}} 'int *borrow' '=' +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int *borrow' lvalue Var 0x{{[^ ]*}} 'p7' 'int *borrow' +// CHECK-NEXT: ImplicitCastExpr 0x{{[^ ]*}} 'int *borrow' +// CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} 'int *borrow' lvalue Var 0x{{[^ ]*}} 'p' 'int *borrow' \ No newline at end of file diff --git a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp index d6d2bb601c8fa951e85e67f4f737270f4a12a79c..5be8a679170d6761acec7b68db3ce067e7849b54 100644 --- a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp +++ b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp @@ -1199,12 +1199,14 @@ std::string DiagnosticTextBuilder::buildForDefinition(const Record *R) { //===----------------------------------------------------------------------===// // Warning Tables (.inc file) generation. //===----------------------------------------------------------------------===// - +#if ENABLE_BSC +#else static bool isError(const Record &Diag) { const std::string &ClsName = std::string(Diag.getValueAsDef("Class")->getName()); return ClsName == "CLASS_ERROR"; } +#endif static bool isRemark(const Record &Diag) { const std::string &ClsName = @@ -1248,8 +1250,11 @@ void clang::EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS, for (unsigned i = 0, e = Diags.size(); i != e; ++i) { const Record &R = *Diags[i]; - // Check if this is an error that is accidentally in a warning - // group. +// Check if this is an error that is accidentally in a warning +// group. +#if ENABLE_BSC +// BSC allow adding error groups +#else if (isError(R)) { if (DefInit *Group = dyn_cast(R.getValueInit("Group"))) { const Record *GroupRec = Group->getDef(); @@ -1259,6 +1264,7 @@ void clang::EmitClangDiagsDefs(RecordKeeper &Records, raw_ostream &OS, " cannot be in a warning group [" + GroupName + "]"); } } +#endif // Check that all remarks have an associated diagnostic group. if (isRemark(R)) {