diff --git a/clang/docs/BSC/BiShengCLanguageUserManual.md b/clang/docs/BSC/BiShengCLanguageUserManual.md
index d6bf89f8aec7beefa73c056ed4a674953966b860..d081aac2ab10568a40be41e747d95693ebd1eced 100644
--- a/clang/docs/BSC/BiShengCLanguageUserManual.md
+++ b/clang/docs/BSC/BiShengCLanguageUserManual.md
@@ -5814,6 +5814,7 @@ int main() {
| 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\`" |
+| init-nonnull | "`%0` is a _Nonnull pointer and must be properly initialized." |
| 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\`" |
diff --git a/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h b/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h
index 7e862348f0b47536b7395b7c28e7fbe1ac77c7c9..ab4f4f7baa14dd3495b676fe78a6b6f001732f78 100644
--- a/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h
+++ b/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h
@@ -28,6 +28,7 @@ enum NullabilityCheckDiagKind {
NullableCastNonnull,
NullablePointerDereference,
NullablePointerAccessMember,
+ NonnullInitByDefault,
NullabilityMaxDiagKind
};
@@ -37,17 +38,21 @@ const unsigned NullabilityDiagIdList[] = {
diag::err_return_nullable,
diag::err_nullable_cast_nonnull,
diag::err_nullable_pointer_dereference,
- diag::err_nullable_pointer_access_member};
+ diag::err_nullable_pointer_access_member,
+ diag::err_nonnull_init_by_default};
struct NullabilityCheckDiagInfo {
SourceLocation Loc;
NullabilityCheckDiagKind Kind;
-
+ std::string Name;
NullabilityCheckDiagInfo(SourceLocation Loc, NullabilityCheckDiagKind Kind)
: Loc(Loc), Kind(Kind) {}
+ NullabilityCheckDiagInfo(SourceLocation Loc, NullabilityCheckDiagKind Kind,
+ std::string Name)
+ : Loc(Loc), Kind(Kind), Name(Name) {}
bool operator==(const NullabilityCheckDiagInfo &other) const {
- return Loc == other.Loc && Kind == other.Kind;
+ return Loc == other.Loc && Kind == other.Kind && Name == other.Name;
}
};
@@ -97,7 +102,23 @@ public:
});
for (const NullabilityCheckDiagInfo &DI : DIV) {
- S.Diag(DI.Loc, getNullabilityDiagID(DI.Kind));
+ switch (DI.Kind) {
+ case NonnullAssignedByNullable:
+ case PassNullableArgument:
+ case ReturnNullable:
+ case NullableCastNonnull:
+ case NullablePointerDereference:
+ case NullablePointerAccessMember:
+ S.Diag(DI.Loc, getNullabilityDiagID(DI.Kind));
+ break;
+ case NonnullInitByDefault:
+ S.Diag(DI.Loc, getNullabilityDiagID(DI.Kind)) << DI.Name;
+ break;
+ default:
+ llvm_unreachable("Unknown error type");
+ break;
+ }
+
S.getDiagnostics().increaseNullabilityCheckErrors();
}
}
diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td b/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td
index 7a45626a10edc5842f8b1fec007f7095e8cf5cd3..e20ae4c594f5dd9f1b782bcd01358856929f79ed 100644
--- a/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td
+++ b/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td
@@ -7,13 +7,15 @@ def ReturnNullable : DiagGroup<"return-nullable">;
def CastNullable : DiagGroup<"cast-nullable">;
def AssignNullable : DiagGroup<"assign-nullable">;
def AssignNonnull : DiagGroup<"assign-nonnull">;
+def InitNonnull : DiagGroup<"init-nonnull">;
def BSCNullability : DiagGroup<"bsc-nullability",
[DerefNullable,
PassNullable,
ReturnNullable,
CastNullable,
AssignNullable,
- AssignNonnull]>;
+ AssignNonnull,
+ InitNonnull]>;
def UseMovedOwned : DiagGroup<"use-moved-owned">;
def UseUninitOwned : DiagGroup<"use-uninit-owned">;
diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td
index cc7cc3d1c97a141d351b6a5ca1e65e66d01322cc..ca7d1bfe28330ba7a13097d2f53ba36b7d9edc0d 100644
--- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td
+++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td
@@ -278,4 +278,7 @@ def err_nullable_pointer_access_member : Error<
def err_nonnull_assigned_by_nullable : Error<
"nonnull pointer cannot be assigned by nullable pointer">,
InGroup;
+def err_nonnull_init_by_default : Error<
+ "`%0` is a _Nonnull pointer and must be properly initialized.">,
+ InGroup;
diff --git a/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp b/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp
index 111d821cafb2976bf522b0f44a52aadcf4f53824..30393cf5e1c0b8b8a926068287304c6d3ef19c57 100644
--- a/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp
+++ b/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp
@@ -116,8 +116,8 @@ public:
void HandleVarDeclInit(DeclStmt *DS, VarDecl *VD);
void HandlePointerInit(DeclStmt *DS, VarDecl *VD);
void HandleStructInit(DeclStmt *DS, VarDecl *VD);
- void HandleFieldInit(DeclStmt *DS, VarDecl *VD, FieldDecl *FD, Expr *FieldInit);
- void HandleFieldStructInit(DeclStmt *DS, VarDecl *VD, RecordDecl *RD,InitListExpr *ILE);
+ void HandleFieldInit(DeclStmt *DS, VarDecl *VD, FieldDecl *FD,
+ Expr *FieldInit, std::string FullFieldPath);
void VisitBinaryOperator(BinaryOperator *BO);
void VisitUnaryOperator(UnaryOperator *UO);
void VisitMemberExpr(MemberExpr *ME);
@@ -126,6 +126,8 @@ public:
void VisitCStyleCastExpr(CStyleCastExpr *CSCE);
NullabilityKind getExprPathNullability(Expr *E);
void PassConditionStatusToSuccBlocks(Stmt *Cond);
+ void HandleNestedStructInit(DeclStmt *DS, VarDecl *TopVD, RecordDecl *RD,
+ InitListExpr *InitList, std::string FieldPrefix);
};
} // namespace
@@ -328,99 +330,90 @@ void TransferFunctions::HandlePointerInit(DeclStmt *DS, VarDecl *VD) {
// Init a struct variable with pointer fields.
void TransferFunctions::HandleStructInit(DeclStmt *DS, VarDecl *VD) {
- Expr *Init = VD->getInit();
- if (!Init)
- return;
-
- // Skip all ParenExpr/ImplicitCastExpr
- Init = Init->IgnoreParenImpCasts();
- // Strip all CStyleCastExpr (casts) and CompoundLiteralExpr (compound
- // literals)
- while (true) {
- if (auto *CSE = dyn_cast(Init)) {
- Init = CSE->getSubExpr()->IgnoreParenImpCasts();
- continue;
- }
- if (auto *CLE = dyn_cast(Init)) {
- if (Expr *Sub = CLE->getInitializer()) {
- Init = Sub->IgnoreParenImpCasts();
- continue;
+ if (auto RecTy = dyn_cast(VD->getType().getCanonicalType())) {
+ if (RecordDecl *RD = RecTy->getDecl()) {
+ if (Expr *Init = VD->getInit()) {
+
+ Init = Init->IgnoreParenImpCasts();
+ while (true) {
+ if (auto *CSE = dyn_cast(Init)) {
+ Init = CSE->getSubExpr()->IgnoreParenImpCasts();
+ continue;
+ }
+ if (auto *CLE = dyn_cast(Init)) {
+ if (Expr *Sub = CLE->getInitializer()) {
+ Init = Sub->IgnoreParenImpCasts();
+ continue;
+ }
+ }
+ break;
+ }
+ if (auto InitListE = dyn_cast(Init)) {
+ HandleNestedStructInit(DS, VD, RD, InitListE, "");
+ }
}
}
- break;
- }
-
- // Finally attempt InitListExpr
- auto *InitListE = dyn_cast(Init);
- if (!InitListE)
- return;
-
- // Get the RecordDecl of variable VD
- QualType QT = VD->getType().getCanonicalType();
- if (auto *RT = dyn_cast(QT)) {
- RecordDecl *RD = RT->getDecl();
- // Use ParentVD = VD as the recursive context; all subfields belong to the
- // same top-level variable
- HandleFieldStructInit(DS, VD, RD, InitListE);
}
}
-// Modified by Huang Bowen
-void TransferFunctions::HandleFieldStructInit(DeclStmt *DS, VarDecl *ParentVD,
- RecordDecl *RD,
- InitListExpr *ILE) {
- Expr **Inits = ILE->getInits();
- // Iterate over all fields in the record
+void TransferFunctions::HandleNestedStructInit(DeclStmt *DS, VarDecl *TopVD,
+ RecordDecl *RD,
+ InitListExpr *InitList,
+ std::string FieldPrefix) {
+ Expr **Inits = InitList->getInits();
for (FieldDecl *FD : RD->fields()) {
- Expr *FieldInit = Inits[FD->getFieldIndex()]->IgnoreParenImpCasts();
+ unsigned idx = FD->getFieldIndex();
+ std::string fullFieldPath = FieldPrefix + "." + FD->getNameAsString();
+
+ Expr *fieldInit = Inits[idx];
// Skip all ParenExpr/ImplicitCastExpr
- FieldInit = FieldInit->IgnoreParenImpCasts();
- // Process C99 compound literals (CompoundLiteralExpr) and strip all
- // CStyleCastExpr (casts) and CompoundLiteralExpr (compound literals)
while (true) {
- if (auto *CSE = dyn_cast(FieldInit)) {
- FieldInit = CSE->getSubExpr()->IgnoreParenImpCasts();
+ if (auto *CSE = dyn_cast(fieldInit)) {
+ fieldInit = CSE->getSubExpr()->IgnoreParenImpCasts();
continue;
}
- if (auto *CLE = dyn_cast(FieldInit)) {
+ if (auto *CLE = dyn_cast(fieldInit)) {
if (Expr *Sub = CLE->getInitializer()) {
- FieldInit = Sub->IgnoreParenImpCasts();
+ fieldInit = Sub->IgnoreParenImpCasts();
continue;
}
}
break;
}
- // (A) If the field is a pointer, perform nullability check
if (FD->getType()->isPointerType()) {
- HandleFieldInit(DS, ParentVD, FD, Inits[FD->getFieldIndex()]);
- }
- // (B) If the field is also a struct/class, recurse into it
- else if (auto *NestedRT =
- dyn_cast(FD->getType().getCanonicalType())) {
- if (RecordDecl *NestedRD = NestedRT->getDecl()) {
- // The inner FieldInit should be an InitListExpr
- if (auto *NestedILE = dyn_cast(FieldInit)) {
- HandleFieldStructInit(DS, ParentVD, NestedRD, NestedILE);
+ HandleFieldInit(DS, TopVD, FD, fieldInit, fullFieldPath);
+ } else if (auto nestedRecTy =
+ dyn_cast(FD->getType().getCanonicalType())) {
+ if (RecordDecl *nestedRD = nestedRecTy->getDecl()) {
+ if (auto nestedInitList = dyn_cast(fieldInit)) {
+ HandleNestedStructInit(DS, TopVD, nestedRD, nestedInitList,
+ fullFieldPath);
}
}
}
}
}
-void TransferFunctions::HandleFieldInit(DeclStmt *DS, VarDecl *VD, FieldDecl *FD, Expr *FieldInit) {
+void TransferFunctions::HandleFieldInit(DeclStmt *DS, VarDecl *VD,
+ FieldDecl *FD, Expr *FieldInit,
+ std::string FullFieldPath) {
+ FieldPath FP(VD, FullFieldPath);
+
NullabilityKind LHSKind = getDefNullability(FD->getType(), Ctx);
NullabilityKind RHSKind = getExprPathNullability(FieldInit);
+
if (LHSKind == NullabilityKind::NonNull) {
- if (RHSKind == NullabilityKind::Nullable && ShouldReportNullPtrError(DS)) {
- NullabilityCheckDiagInfo DI(FieldInit->getBeginLoc(), NonnullAssignedByNullable);
+ if (RHSKind != NullabilityKind::NonNull && ShouldReportNullPtrError(DS)) {
+ NullabilityCheckDiagInfo DI(VD->getBeginLoc(), NonnullInitByDefault,
+ VD->getNameAsString() + FullFieldPath);
Reporter.addDiagInfo(DI);
}
} else {
- FieldPath FP(VD, "." + FD->getNameAsString());
- if (CurrStatusFP.count(FP))
+ if (CurrStatusFP.count(FP)) {
CurrStatusFP[FP] = RHSKind;
+ }
}
}
diff --git a/clang/test/BSC/Negative/NullabilityCheck/nested_owned_struct/nested_owned_struct.cbs b/clang/test/BSC/Negative/NullabilityCheck/nested_owned_struct/nested_owned_struct.cbs
new file mode 100644
index 0000000000000000000000000000000000000000..1af9d928484362d8ec68a8e1d841ae4c1d3d1a70
--- /dev/null
+++ b/clang/test/BSC/Negative/NullabilityCheck/nested_owned_struct/nested_owned_struct.cbs
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -nullability-check=all -verify %s
+
+safe T *owned safe_malloc(T t);
+safe void safe_free(void *owned p);
+
+owned struct B{
+ public:
+ int * owned p;
+ ~B(B this){
+ safe_free((void*owned)this.p);
+ }
+};
+
+owned struct A{
+ public:
+ B b;
+};
+
+int main(){
+ B b1={};// expected-error {{`b1.p` is a _Nonnull pointer and must be properly initialized.}}
+ return 0;
+}
+
+
+
diff --git a/clang/test/BSC/Negative/NullabilityCheck/nested_owned_struct1/nested_owned_struct1.cbs b/clang/test/BSC/Negative/NullabilityCheck/nested_owned_struct1/nested_owned_struct1.cbs
new file mode 100644
index 0000000000000000000000000000000000000000..f0776d7fd20951cbdadcbe4c68f98e9a4175e534
--- /dev/null
+++ b/clang/test/BSC/Negative/NullabilityCheck/nested_owned_struct1/nested_owned_struct1.cbs
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -nullability-check=all -verify %s
+
+safe T *owned safe_malloc(T t);
+safe void safe_free(void *owned p);
+
+owned struct B{
+ public:
+ int * owned p;
+ int pp;
+ ~B(B this){
+ safe_free((void*owned)this.p);
+ }
+};
+
+owned struct A{
+ public:
+ B b;
+ int * owned p;
+ int * q;
+ int * _Nullable nq;
+ ~A(A this){
+ safe_free((void* owned)this.p);
+ }
+};
+
+void test1(){
+ A a ={};// #1
+ B b = {};// expected-error {{`b.p` is a _Nonnull pointer and must be properly initialized.}}
+}
+// expected-error@#1 {{`a.b.p` is a _Nonnull pointer and must be properly initialized.}}
+// expected-error@#1 {{`a.p` is a _Nonnull pointer and must be properly initialized.}}
+
+void test2(){
+ B b ={};// expected-error {{`b.p` is a _Nonnull pointer and must be properly initialized.}}
+ A a = {.b=b};// expected-error {{`a.p` is a _Nonnull pointer and must be properly initialized.}}
+}
+
+void test3(){
+ B b = {.p=nullptr};// expected-error {{`b.p` is a _Nonnull pointer and must be properly initialized.}}
+ A a = {.b=b};// expected-error {{`a.p` is a _Nonnull pointer and must be properly initialized.}}
+}
+
+void test4(){
+ B b = {.p=safe_malloc(42)};
+ A a = {.b=b};// expected-error {{`a.p` is a _Nonnull pointer and must be properly initialized.}}
+}
+
+void test5(){
+ A a ={.b={safe_malloc(42)},.p=safe_malloc(420)};
+}
\ No newline at end of file
diff --git a/clang/test/BSC/Negative/NullabilityCheck/nullable_nonnull_conflict/component_struct_asign.cbs b/clang/test/BSC/Negative/NullabilityCheck/nullable_nonnull_conflict/component_struct_asign.cbs
index 83370dcc9f9d79b7aa83ce663eaacf3aba4f5a2b..033d9659b23b835324a314f2ce6d97628129dee2 100644
--- a/clang/test/BSC/Negative/NullabilityCheck/nullable_nonnull_conflict/component_struct_asign.cbs
+++ b/clang/test/BSC/Negative/NullabilityCheck/nullable_nonnull_conflict/component_struct_asign.cbs
@@ -1,43 +1,59 @@
// RUN: %clang_cc1 -nullability-check=all -verify %s
+
int *_Nullable foo() {
return nullptr;
}
+
struct inside{
int* _Nonnull p;
};
+
struct mid{
struct inside c1;
};
+
struct out{
struct inside a;
struct mid b;
int* _Nonnull pt;
};
+
+struct simOut{
+ struct inside a;
+};
+
void test1(void){
- struct inside a = (struct inside){ nullptr };// expected-error {{nonnull pointer cannot be assigned by nullable pointer}}
+ struct inside a = (struct inside){ nullptr };// expected-error {{`a.p` is a _Nonnull pointer and must be properly initialized.}}
}
void test2(void){
- struct mid m = { { nullptr } };// expected-error {{nonnull pointer cannot be assigned by nullable pointer}}
+ struct mid m = { { nullptr } };// expected-error {{`m.c1.p` is a _Nonnull pointer and must be properly initialized.}}
}
void test3(void){
- struct out o = { {nullptr},// expected-error {{nonnull pointer cannot be assigned by nullable pointer}}
+ struct out o = { {nullptr},// #1
- {{nullptr}},// expected-error {{nonnull pointer cannot be assigned by nullable pointer}}
+ {{nullptr}},
- nullptr };// expected-error {{nonnull pointer cannot be assigned by nullable pointer}}
+ nullptr };
}
+// expected-error@#1 {{`o.a.p` is a _Nonnull pointer and must be properly initialized.}}
+// expected-error@#1 {{`o.b.c1.p` is a _Nonnull pointer and must be properly initialized.}}
+// expected-error@#1 {{`o.pt` is a _Nonnull pointer and must be properly initialized.}}
+void test4(){
+ struct simOut o1={(struct inside)(struct inside){nullptr}};// expected-error {{`o1.a.p` is a _Nonnull pointer and must be properly initialized.}}
+ struct simOut o2={(struct inside)(struct inside){}};// expected-error {{`o2.a.p` is a _Nonnull pointer and must be properly initialized.}}
+}
-void test4(void){
+void test5(void){
int * _Nullable p0 = nullptr;
- struct inside a = (struct inside){ p0 };// expected-error {{nonnull pointer cannot be assigned by nullable pointer}}
- struct inside b = (struct inside){ foo() };// expected-error {{nonnull pointer cannot be assigned by nullable pointer}}
+ struct inside a = (struct inside){ p0 };// expected-error {{`a.p` is a _Nonnull pointer and must be properly initialized.}}
+ struct inside b = (struct inside){ foo() };// expected-error {{`b.p` is a _Nonnull pointer and must be properly initialized.}}
}
\ No newline at end of file
diff --git a/libcbs/src/bishengc_safety/bishengc_safety.cbs b/libcbs/src/bishengc_safety/bishengc_safety.cbs
index d9465fd53bc830550f099fdf485379e810ed5dce..a02b49d54204b209adffa7b4967e681942a777cd 100644
--- a/libcbs/src/bishengc_safety/bishengc_safety.cbs
+++ b/libcbs/src/bishengc_safety/bishengc_safety.cbs
@@ -63,7 +63,7 @@ safe void bsc_refcell_immut_borrow_failed(void) {
}
}
-safe void safe_free(void * owned p) {
+safe void safe_free(void * _Nullable owned p) {
unsafe {
free((void *)p);
}