https://github.com/rapidsna created https://github.com/llvm/llvm-project/pull/224561
<sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub> >From 58357e47b000de946cbb4494ecb052619f9fbb17 Mon Sep 17 00:00:00 2001 From: Yeoul Na <[email protected]> Date: Wed, 9 Sep 2026 13:02:47 -0700 Subject: [PATCH] [BoundsSafety][test] Add late-parsed counted_by type-attribute coverage New tests exercising the late-parse fill-in mechanism: - Sema/attr-counted-by-weird-type-positions{,-late-parsed}.c: counted_by in assorted type positions, nested pointers, and rejection cases. - Sema/attr-bounds-safety-function-ptr-param.c: attributes on function-pointer-typed members. - Modules/ and PCH/ bounds-safety-attributed-type-late-parsed: the resolved type round-trips through serialization. - Sema/attr-counted-by-late-parsed-regressions.c: guards against the double-free on a nested-record decl-spec attribute and the null-count escape on a free-function parameter. --- .../AST/attr-counted-by-eager-invalid-strip.c | 27 ++ ...-counted-by-late-parsed-invalid-recovery.c | 46 ++ .../attr-counted-by-late-parsed-struct-ptrs.c | 19 + ...ounds-safety-attributed-type-late-parsed.c | 111 +++++ ...ounds-safety-attributed-type-late-parsed.h | 58 +++ ...ounds-safety-attributed-type-late-parsed.c | 69 +++ .../attr-bounds-safety-function-ptr-param.c | 173 +++++++ .../attr-counted-by-late-parsed-regressions.c | 92 ++++ ...nted-by-weird-type-positions-late-parsed.c | 456 ++++++++++++++++++ .../attr-counted-by-weird-type-positions.c | 454 +++++++++++++++++ 10 files changed, 1505 insertions(+) create mode 100644 clang/test/AST/attr-counted-by-eager-invalid-strip.c create mode 100644 clang/test/AST/attr-counted-by-late-parsed-invalid-recovery.c create mode 100644 clang/test/Modules/bounds-safety-attributed-type-late-parsed.c create mode 100644 clang/test/PCH/Inputs/bounds-safety-attributed-type-late-parsed.h create mode 100644 clang/test/PCH/bounds-safety-attributed-type-late-parsed.c create mode 100644 clang/test/Sema/attr-bounds-safety-function-ptr-param.c create mode 100644 clang/test/Sema/attr-counted-by-late-parsed-regressions.c create mode 100644 clang/test/Sema/attr-counted-by-weird-type-positions-late-parsed.c create mode 100644 clang/test/Sema/attr-counted-by-weird-type-positions.c diff --git a/clang/test/AST/attr-counted-by-eager-invalid-strip.c b/clang/test/AST/attr-counted-by-eager-invalid-strip.c new file mode 100644 index 0000000000000..51cd306aa1970 --- /dev/null +++ b/clang/test/AST/attr-counted-by-eager-invalid-strip.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -verify %s -ast-dump | FileCheck %s + +// On the eager (non -fexperimental-late-parse-attributes) path a +// counted_by-family CountAttributedType is built during type processing, before +// the FieldDecl-dependent checks run in ActOnFields. When those checks reject +// the attribute, the CountAttributedType must be stripped so the field keeps +// its plain wrapped type -- matching the pre-refactor behavior, which built the +// type only after the check passed. A surviving bogus CountAttributedType would +// otherwise flow downstream. This test pins the stripped type; the diagnostics +// themselves are covered elsewhere. + +#define __counted_by(f) __attribute__((counted_by(f))) + +union invalid_union_member { + int n; + int *__counted_by(n) p; // expected-error {{'counted_by' cannot be applied to a union member}} +}; +// CHECK-LABEL: union invalid_union_member definition +// CHECK: FieldDecl {{.*}} p 'int *'{{$}} + +struct invalid_non_fam_array { + int n; + int arr[10] __counted_by(n); // expected-error {{'counted_by' on arrays only applies to C99 flexible array members}} + int last; +}; +// CHECK-LABEL: struct invalid_non_fam_array definition +// CHECK: FieldDecl {{.*}} arr 'int[10]'{{$}} diff --git a/clang/test/AST/attr-counted-by-late-parsed-invalid-recovery.c b/clang/test/AST/attr-counted-by-late-parsed-invalid-recovery.c new file mode 100644 index 0000000000000..776f8422ad0fc --- /dev/null +++ b/clang/test/AST/attr-counted-by-late-parsed-invalid-recovery.c @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -verify %s -ast-dump | FileCheck %s + +// On the late-parsed path the CountAttributedType is built before its count +// argument is parsed, so a rejected argument is only discovered at completion, +// when the node is already embedded in the field's type. Rather than strip a +// (possibly nested) node -- which would force the enclosing types to be rebuilt +// -- the node is kept and completed in place with the raw argument, and the +// field is marked invalid. Consumers bail on such a count (see +// FieldDecl::findCountedByField). This applies whether the argument is unusable +// (a parse failure, or a non-declaration-reference such as `sizeof(...)`) or is +// a valid reference in an invalid position (a union member). +// +// A nested counted_by is the exception: it is diagnosed and dropped while the +// declarator is built -- before the enclosing pointer/array wraps the node, so +// the drop needs no rebuild -- leaving the field its plain wrapped type, exactly +// as on the eager path. + +#define __counted_by(f) __attribute__((counted_by(f))) + +// Non-declaration-reference argument: the node is kept with the raw argument as +// its count and the field is marked invalid. +struct bad_count_expr { + int n; + int *__counted_by(sizeof(int)) p; // expected-error {{'counted_by' argument must be a simple declaration reference}} +}; +// CHECK-LABEL: struct bad_count_expr definition +// CHECK: FieldDecl {{.*}} invalid p 'int * __counted_by(sizeof(int))':'int *' + +// Valid reference in an invalid position: also kept with the raw argument and +// the field marked invalid. +union valid_ref_bad_position { + int n; + int *__counted_by(n) p; // expected-error {{'counted_by' cannot be applied to a union member}} +}; +// CHECK-LABEL: union valid_ref_bad_position definition +// CHECK: FieldDecl {{.*}} invalid p 'int * __counted_by(n)':'int *' + +// Nested under another pointer: diagnosed and dropped while the declarator is +// built, so the field keeps its plain wrapped type (no CountAttributedType) and +// stays valid -- exactly as on the eager path. +struct nested_under_pointer { + int n; + int *__counted_by(n) *pp; // expected-error {{'counted_by' attribute on nested pointer type is not allowed}} +}; +// CHECK-LABEL: struct nested_under_pointer definition +// CHECK: FieldDecl {{.*}} pp 'int **'{{$}} diff --git a/clang/test/AST/attr-counted-by-late-parsed-struct-ptrs.c b/clang/test/AST/attr-counted-by-late-parsed-struct-ptrs.c index f9772db8b6554..9d43523bf3180 100644 --- a/clang/test/AST/attr-counted-by-late-parsed-struct-ptrs.c +++ b/clang/test/AST/attr-counted-by-late-parsed-struct-ptrs.c @@ -43,3 +43,22 @@ struct on_pointer_anon_count { // // See `clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c` for test // cases. + +//============================================================================== +// A declaration-specifier-position attribute shared by several declarators +//============================================================================== +// All declarators share one CountAttributedType, so every field must print a +// resolved count. Previously only the last one did: each declarator built its +// own (un-uniqued) node and only the last was ever completed, leaving the +// earlier fields with an empty '__counted_by()'. + +typedef int *ptr_ty; + +struct shared_declspec_attr { + int count; + ptr_ty __counted_by(count) a, b; +}; +// CHECK-LABEL: struct shared_declspec_attr definition +// CHECK-NEXT: |-FieldDecl {{.*}} referenced count 'int' +// CHECK-NEXT: |-FieldDecl {{.*}} a 'ptr_ty __counted_by(count)':'int *' +// CHECK-NEXT: `-FieldDecl {{.*}} b 'ptr_ty __counted_by(count)':'int *' diff --git a/clang/test/Modules/bounds-safety-attributed-type-late-parsed.c b/clang/test/Modules/bounds-safety-attributed-type-late-parsed.c new file mode 100644 index 0000000000000..58df0761f2022 --- /dev/null +++ b/clang/test/Modules/bounds-safety-attributed-type-late-parsed.c @@ -0,0 +1,111 @@ +// Test serialization of late-parsed bounds-safety attributes via Modules +// This verifies that LateParsedAttrType is transformed to CountAttributedType +// before serialization and remains as CountAttributedType after deserialization. + +// RUN: rm -rf %t +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fmodules -fmodules-cache-path=%t -verify %s +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fmodules -fmodules-cache-path=%t -ast-dump-all %s | FileCheck %s +// expected-no-diagnostics + +#pragma clang module build bounds_safety_late_parsed +module bounds_safety_late_parsed {} +#pragma clang module contents +#pragma clang module begin bounds_safety_late_parsed + +// Test where counted_by references a field declared later +struct LateRefPointer { + int *__attribute__((counted_by(count))) buf; + int count; +}; + +// Test with sized_by referencing later field +struct LateRefSized { + int *__attribute__((sized_by(size))) data; + int size; +}; + +// Test with counted_by_or_null referencing later field +struct LateRefCountedByOrNull { + int *__attribute__((counted_by_or_null(count))) buf; + int count; +}; + +// Test with sized_by_or_null referencing later field +struct LateRefSizedByOrNull { + int *__attribute__((sized_by_or_null(size))) data; + int size; +}; + +// Test with nested struct +struct LateRefNested { + struct Inner { + int value; + } *__attribute__((counted_by(n))) items; + int n; +}; + +// Test with multiple late-parsed attributes +struct MultipleLateRefs { + int *__attribute__((counted_by(count1))) buf1; + int *__attribute__((sized_by(count2))) buf2; + int *__attribute__((counted_by_or_null(count3))) buf3; + int *__attribute__((sized_by_or_null(count4))) buf4; + int count1; + int count2; + int count3; + int count4; +}; + +#pragma clang module end +#pragma clang module endbuild + +#pragma clang module import bounds_safety_late_parsed + +struct LateRefPointer *p1; +struct LateRefSized *p2; +struct LateRefCountedByOrNull *p3; +struct LateRefSizedByOrNull *p4; +struct LateRefNested *p5; +struct MultipleLateRefs *p6; + +// CHECK: RecordDecl {{.*}} imported in bounds_safety_late_parsed <undeserialized declarations> struct LateRefPointer definition +// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed buf 'int * __counted_by(count)':'int *' +// CHECK-NEXT: `-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced count 'int' + +// CHECK: RecordDecl {{.*}} imported in bounds_safety_late_parsed <undeserialized declarations> struct LateRefSized definition +// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed data 'int * __sized_by(size)':'int *' +// CHECK-NEXT: `-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced size 'int' + +// CHECK: RecordDecl {{.*}} imported in bounds_safety_late_parsed <undeserialized declarations> struct LateRefCountedByOrNull definition +// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed buf 'int * __counted_by_or_null(count)':'int *' +// CHECK-NEXT: `-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced count 'int' + +// CHECK: RecordDecl {{.*}} imported in bounds_safety_late_parsed <undeserialized declarations> struct LateRefSizedByOrNull definition +// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed data 'int * __sized_by_or_null(size)':'int *' +// CHECK-NEXT: `-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced size 'int' + +// CHECK: RecordDecl {{.*}} imported in bounds_safety_late_parsed <undeserialized declarations> struct LateRefNested definition +// CHECK: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed items 'struct Inner * __counted_by(n)':'struct Inner *' +// CHECK: `-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced n 'int' + +// CHECK: RecordDecl {{.*}} imported in bounds_safety_late_parsed <undeserialized declarations> struct MultipleLateRefs definition +// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed buf1 'int * __counted_by(count1)':'int *' +// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed buf2 'int * __sized_by(count2)':'int *' +// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed buf3 'int * __counted_by_or_null(count3)':'int *' +// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed buf4 'int * __sized_by_or_null(count4)':'int *' +// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced count1 'int' +// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced count2 'int' +// CHECK-NEXT: |-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced count3 'int' +// CHECK-NEXT: `-FieldDecl {{.*}} imported in bounds_safety_late_parsed referenced count4 'int' + +// Verify that LateParsedAttrType does not appear in the AST dump +// CHECK-NOT: LateParsedAttr + +// Verify the import and variable declarations +// CHECK: ImportDecl {{.*}} implicit bounds_safety_late_parsed +// CHECK: VarDecl {{.*}} p1 'struct LateRefPointer *' +// CHECK: VarDecl {{.*}} p2 'struct LateRefSized *' +// CHECK: VarDecl {{.*}} p3 'struct LateRefCountedByOrNull *' +// CHECK: VarDecl {{.*}} p4 'struct LateRefSizedByOrNull *' +// CHECK: VarDecl {{.*}} p5 'struct LateRefNested *' +// CHECK: VarDecl {{.*}} p6 'struct MultipleLateRefs *' diff --git a/clang/test/PCH/Inputs/bounds-safety-attributed-type-late-parsed.h b/clang/test/PCH/Inputs/bounds-safety-attributed-type-late-parsed.h new file mode 100644 index 0000000000000..c0751408045c7 --- /dev/null +++ b/clang/test/PCH/Inputs/bounds-safety-attributed-type-late-parsed.h @@ -0,0 +1,58 @@ +// Header for testing late-parsed bounds-safety attributes serialization + +#define __counted_by(f) __attribute__((counted_by(f))) +#define __sized_by(f) __attribute__((sized_by(f))) +#define __counted_by_or_null(f) __attribute__((counted_by_or_null(f))) +#define __sized_by_or_null(f) __attribute__((sized_by_or_null(f))) + +// Test where counted_by references a field declared later +struct LateRefPointer { + int *__counted_by(count) buf; + int count; +}; + +// Test with sized_by referencing later field +struct LateRefSized { + int *__sized_by(size) data; + int size; +}; + +// Test with counted_by_or_null referencing later field +struct LateRefCountedByOrNull { + int *__counted_by_or_null(count) buf; + int count; +}; + +// Test with sized_by_or_null referencing later field +struct LateRefSizedByOrNull { + int *__sized_by_or_null(size) data; + int size; +}; + +// Test with nested struct +struct LateRefNested { + struct Inner { + int value; + } *__counted_by(n) items; + int n; +}; + +// Test with multiple late-parsed attributes +struct MultipleLateRefs { + int *__counted_by(count1) buf1; + int *__sized_by(count2) buf2; + int *__counted_by_or_null(count3) buf3; + int *__sized_by_or_null(count4) buf4; + int count1; + int count2; + int count3; + int count4; +}; + +// Test with anonymous struct/union +struct LateRefAnon { + int *__counted_by(count) buf; + struct { + int count; + }; +}; diff --git a/clang/test/PCH/bounds-safety-attributed-type-late-parsed.c b/clang/test/PCH/bounds-safety-attributed-type-late-parsed.c new file mode 100644 index 0000000000000..1d77bbe13927c --- /dev/null +++ b/clang/test/PCH/bounds-safety-attributed-type-late-parsed.c @@ -0,0 +1,69 @@ +// Test serialization of late-parsed bounds-safety attributes via PCH +// This verifies that LateParsedAttrType is transformed to CountAttributedType +// before serialization and remains as CountAttributedType after deserialization. + +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -include %S/Inputs/bounds-safety-attributed-type-late-parsed.h -fsyntax-only -verify %s + +// Test with pch. +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -emit-pch -o %t %S/Inputs/bounds-safety-attributed-type-late-parsed.h +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -include-pch %t -fsyntax-only -verify %s +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -include-pch %t -ast-print %s | FileCheck %s --check-prefix PRINT +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -include-pch %t -ast-dump-all %s | FileCheck %s --check-prefix DUMP +// expected-no-diagnostics + +// PRINT: struct LateRefPointer { +// PRINT-NEXT: int * __counted_by(count)buf; +// PRINT-NEXT: int count; +// PRINT-NEXT: }; + +// PRINT: struct LateRefSized { +// PRINT-NEXT: int * __sized_by(size)data; +// PRINT-NEXT: int size; +// PRINT-NEXT: }; + +// PRINT: struct LateRefCountedByOrNull { +// PRINT-NEXT: int * __counted_by_or_null(count)buf; +// PRINT-NEXT: int count; +// PRINT-NEXT: }; + +// PRINT: struct LateRefSizedByOrNull { +// PRINT-NEXT: int * __sized_by_or_null(size)data; +// PRINT-NEXT: int size; +// PRINT-NEXT: }; + +// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct LateRefPointer definition +// DUMP-NEXT: |-FieldDecl {{.*}} imported buf 'int * __counted_by(count)':'int *' +// DUMP-NEXT: `-FieldDecl {{.*}} imported referenced count 'int' + +// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct LateRefSized definition +// DUMP-NEXT: |-FieldDecl {{.*}} imported data 'int * __sized_by(size)':'int *' +// DUMP-NEXT: `-FieldDecl {{.*}} imported referenced size 'int' + +// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct LateRefCountedByOrNull definition +// DUMP-NEXT: |-FieldDecl {{.*}} imported buf 'int * __counted_by_or_null(count)':'int *' +// DUMP-NEXT: `-FieldDecl {{.*}} imported referenced count 'int' + +// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct LateRefSizedByOrNull definition +// DUMP-NEXT: |-FieldDecl {{.*}} imported data 'int * __sized_by_or_null(size)':'int *' +// DUMP-NEXT: `-FieldDecl {{.*}} imported referenced size 'int' + +// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct LateRefNested definition +// DUMP: |-FieldDecl {{.*}} imported items 'struct Inner * __counted_by(n)':'struct Inner *' +// DUMP: `-FieldDecl {{.*}} imported referenced n 'int' + +// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct MultipleLateRefs definition +// DUMP-NEXT: |-FieldDecl {{.*}} imported buf1 'int * __counted_by(count1)':'int *' +// DUMP-NEXT: |-FieldDecl {{.*}} imported buf2 'int * __sized_by(count2)':'int *' +// DUMP-NEXT: |-FieldDecl {{.*}} imported buf3 'int * __counted_by_or_null(count3)':'int *' +// DUMP-NEXT: |-FieldDecl {{.*}} imported buf4 'int * __sized_by_or_null(count4)':'int *' +// DUMP-NEXT: |-FieldDecl {{.*}} imported referenced count1 'int' +// DUMP-NEXT: |-FieldDecl {{.*}} imported referenced count2 'int' +// DUMP-NEXT: |-FieldDecl {{.*}} imported referenced count3 'int' +// DUMP-NEXT: `-FieldDecl {{.*}} imported referenced count4 'int' + +// DUMP: RecordDecl {{.*}} imported <undeserialized declarations> struct LateRefAnon definition +// DUMP-NEXT: |-FieldDecl {{.*}} imported buf 'int * __counted_by(count)':'int *' +// DUMP: `-IndirectFieldDecl {{.*}} imported implicit referenced count 'int' + +// Verify that LateParsedAttrType does not appear in the AST dump +// DUMP-NOT: LateParsedAttr diff --git a/clang/test/Sema/attr-bounds-safety-function-ptr-param.c b/clang/test/Sema/attr-bounds-safety-function-ptr-param.c new file mode 100644 index 0000000000000..091220e313958 --- /dev/null +++ b/clang/test/Sema/attr-bounds-safety-function-ptr-param.c @@ -0,0 +1,173 @@ +// XFAIL: * +// FIXME: https://github.com/llvm/llvm-project/issues/166454 + +// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s + +#define __counted_by(N) __attribute__((counted_by(N))) +#define __counted_by_or_null(N) __attribute__((counted_by_or_null(N))) +#define __sized_by(N) __attribute__((sized_by(N))) +#define __sized_by_or_null(N) __attribute__((sized_by_or_null(N))) + +//============================================================================== +// Test bounds safety attributes on function pointer parameters +//============================================================================== + +struct counted_by_function_pointer_param { + // expected-error@+1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(int *__counted_by(len)); + int len; +}; + +struct counted_by_or_null_function_pointer_param { + // expected-error@+1{{'counted_by_or_null' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(int *__counted_by_or_null(len)); + int len; +}; + +struct sized_by_function_pointer_param { + // expected-error@+1{{'sized_by' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(char *__sized_by(len)); + int len; +}; + +struct sized_by_or_null_function_pointer_param { + // expected-error@+1{{'sized_by_or_null' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(char *__sized_by_or_null(len)); + int len; +}; + +//============================================================================== +// Test multiple parameters with bounds safety attributes +//============================================================================== + +struct multiple_params_with_bounds_safety { + // expected-error@+1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}} + int (*multi_callback)(int *__counted_by(len1), char *data, int len1); + int len1; +}; + +struct mixed_bounds_safety_params { + // expected-error@+2{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}} + // expected-error@+1{{'sized_by_or_null' attribute cannot be applied to a parameter in a function pointer type}} + int (*mixed_callback)(int *__counted_by(count), char *__sized_by_or_null(size), int count, int size); + int count; + int size; +}; + +//============================================================================== +// Test cases that do not require late parsing (count field defined before use) +//============================================================================== + +struct counted_by_no_late_parse { + int len; + // expected-error@+1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(int *__counted_by(len)); +}; + +struct counted_by_or_null_no_late_parse { + int len; + // expected-error@+1{{'counted_by_or_null' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(int *__counted_by_or_null(len)); +}; + +struct sized_by_no_late_parse { + int len; + // expected-error@+1{{'sized_by' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(char *__sized_by(len)); +}; + +struct sized_by_or_null_no_late_parse { + int len; + // expected-error@+1{{'sized_by_or_null' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(char *__sized_by_or_null(len)); +}; + +//============================================================================== +// Test nested function pointer types +//============================================================================== + +struct nested_function_pointer_with_bounds_safety { + // expected-error@+1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}} + int (*outer_callback)(int (*inner)(int *__counted_by(len)), int len); + int len; +}; + +//============================================================================== +// Test struct members with anonymous structs/unions (no late parsing needed) +//============================================================================== + +struct with_anonymous_struct_no_late_parse { + int len; + // expected-error@+1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(int *__counted_by(len)); +}; + +struct with_anonymous_union_no_late_parse { + union { + int len; + float f_len; + }; + // expected-error@+1{{'counted_by_or_null' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(int *__counted_by_or_null(len)); +}; + +//============================================================================== +// Test with different parameter positions +//============================================================================== + +struct first_param_bounds_safety_no_late_parse { + int count; + // expected-error@+1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(int *__counted_by(count), void *data, int extra); +}; + +struct middle_param_bounds_safety_no_late_parse { + int size; + // expected-error@+1{{'sized_by' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(void *prefix, char *__sized_by(size), int suffix); +}; + +struct last_param_bounds_safety_no_late_parse { + int len; + // expected-error@+1{{'counted_by_or_null' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(int a, float b, int *__counted_by_or_null(len)); +}; + +//============================================================================== +// Test with const and volatile qualifiers +//============================================================================== + +struct const_param_bounds_safety_no_late_parse { + int count; + // expected-error@+1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(const int *__counted_by(count)); +}; + +struct volatile_param_bounds_safety_no_late_parse { + int size; + // expected-error@+1{{'sized_by_or_null' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(volatile char *__sized_by_or_null(size)); +}; + +struct const_volatile_param_bounds_safety_no_late_parse { + int len; + // expected-error@+1{{'counted_by_or_null' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback)(const volatile int *__counted_by_or_null(len)); +}; + +//============================================================================== +// Test with multiple function pointers in same struct +//============================================================================== + +struct multiple_function_pointers_no_late_parse { + int len1, len2, size1, size2; + // expected-error@+1{{'counted_by' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback1)(int *__counted_by(len1)); + // expected-error@+1{{'counted_by_or_null' attribute cannot be applied to a parameter in a function pointer type}} + int (*callback2)(int *__counted_by_or_null(len2)); + // expected-error@+1{{'sized_by' attribute cannot be applied to a parameter in a function pointer type}} + void (*callback3)(char *__sized_by(size1)); + // expected-error@+1{{'sized_by_or_null' attribute cannot be applied to a parameter in a function pointer type}} + void (*callback4)(char *__sized_by_or_null(size2)); +}; diff --git a/clang/test/Sema/attr-counted-by-late-parsed-regressions.c b/clang/test/Sema/attr-counted-by-late-parsed-regressions.c new file mode 100644 index 0000000000000..cb1cca4d95d48 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-late-parsed-regressions.c @@ -0,0 +1,92 @@ +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s + +#define __counted_by(N) __attribute__((counted_by(N))) + +// A late-parsed type attribute in declarator-specifier position after a nested +// record definition used to be registered both in the enclosing record's +// field-attribute list and in its late-parsed-type-attribute list, so its +// cached tokens were parsed and freed twice -- an assertion / use-after-free. +// The point of this test is that it no longer crashes. +// +// FIXME: 'counted_by' on a non-pointer (here struct-typed) field should be +// diagnosed as "only applies to pointers or C99 flexible array members"; the +// late path currently accepts it silently. That missing diagnostic is a +// separate issue from the double-free guarded here. +struct nested_record_declspec_attr { + struct inner1 { + int x; + int *p; + } __counted_by(x) f; + int y; +}; + +// A 'counted_by' type attribute on a free-function parameter has no enclosing +// record to complete it. Late-parsing it left a CountAttributedType with a +// null count expression in the AST, which crashed on serialization / PCH +// round-trip. Parameters now fall back to eager handling, so the attribute is +// resolved (or rejected) immediately instead of escaping unfinished. + +// Forward reference: eager handling can't see 'n' yet, so it is diagnosed +// rather than silently building a null-count type. +void fwd_ref_param(int *__counted_by(n) p, // expected-error {{use of undeclared identifier 'n'}} + int n); + +// FIXME: counted_by on a function parameter isn't supported yet; the eager +// decl-attribute path rejects it. What matters for this regression is that it +// is diagnosed here, not left as an unfinished type for a later crash. +void bwd_ref_param(int n, + int *__counted_by(n) p); // expected-error {{'counted_by' attribute only applies to non-static data members}} + +// A declaration-specifier-position attribute is shared by every declarator in +// the declaration, and ConvertDeclSpecToType walks the DeclSpec's late-attribute +// list once per declarator. Building a fresh (deliberately un-uniqued) +// CountAttributedType on each walk left every field but the last holding a node +// whose count expression was never filled in -- silently, with no diagnostic -- +// and any use of such a field then tripped FieldDecl::findCountedByField's +// unconditional cast<DeclRefExpr>. The attribute now reuses one node for all +// declarators, matching the eager path where uniquing has the same effect. +typedef int *shared_ptr_ty; + +struct shared_declspec_attr { + int n; + shared_ptr_ty __counted_by(n) a, b, c; +}; + +// Exercising the *earlier* declarators is the point: 'c' was always fine. +void use_shared_declspec_attr(struct shared_declspec_attr *s) { + (void)__builtin_counted_by_ref(s->a); + (void)__builtin_counted_by_ref(s->b); + (void)__builtin_counted_by_ref(s->c); +} + +// The same, with the count declared after the fields, so the attribute really is +// late parsed rather than resolved eagerly. +struct shared_declspec_attr_fwd { + shared_ptr_ty __counted_by(n) a, b; + int n; +}; + +void use_shared_declspec_attr_fwd(struct shared_declspec_attr_fwd *s) { + (void)__builtin_counted_by_ref(s->a); +} + +// Each field is still checked in its own declaration context, so a shared +// attribute reports once per field rather than once per attribute. +union shared_declspec_attr_in_union { + int n; + // expected-error@+2 {{'counted_by' cannot be applied to a union member}} + // expected-error@+1 {{'counted_by' cannot be applied to a union member}} + shared_ptr_ty __counted_by(n) a, b; +}; + +// A grouping-paren declarator whose base type is already a pointer/array (via a +// typedef) used to late-parse the attribute even at file scope, where there is +// no enclosing record to complete it -- leaving a CountAttributedType with a +// null count expression in the AST (and skipping the "non-static data members" +// diagnostic entirely). ParseParenDeclarator now late-parses only inside a +// record, so at file scope the attribute is handled eagerly and rejected. +typedef int *ptr_ty; +typedef int arr_ty[4]; +int global_count; +ptr_ty (__counted_by(global_count) file_ptr); // expected-error {{'counted_by' attribute only applies to non-static data members}} +arr_ty (__counted_by(global_count) file_arr); // expected-error {{'counted_by' attribute only applies to non-static data members}} diff --git a/clang/test/Sema/attr-counted-by-weird-type-positions-late-parsed.c b/clang/test/Sema/attr-counted-by-weird-type-positions-late-parsed.c new file mode 100644 index 0000000000000..0728c8623c202 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-weird-type-positions-late-parsed.c @@ -0,0 +1,456 @@ +// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s + +#define __counted_by(f) __attribute__((counted_by(f))) + +// ============================================================================ +// SIMPLE POINTER: int *buf +// ============================================================================ + +// Position: after *, before identifier +// Applies to `int *`. +struct ptr_after_star { + int *__counted_by(count) buf; + int count; +}; + +// Position: before type specifier +// Applies to the top-level type. +struct ptr_before_type { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + __counted_by(count) int *buf; + int count; +}; + +// Position: after type, before * +// Applies to `int`. +struct ptr_after_type { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) *buf; + int count; +}; + +// Position: after identifier +// Applies to the top-level type. +struct ptr_after_ident { + int *buf __counted_by(count); + int count; +}; + +// ============================================================================ +// TYPEDEF POINTER: ptr_to_int_t buf +// ============================================================================ + +typedef int * ptr_to_int_t; + +// Position: after typedef name, before identifier +// Applies to `ptr_to_int_t`. +struct typedef_after_type { + ptr_to_int_t __counted_by(count) buf; + int count; +}; + +// Position: before typedef name +// Applies to the top-level type. +struct typedef_before_type { + __counted_by(count) ptr_to_int_t buf; + int count; +}; + +// Position: after identifier +// Applies to the top-level type. +struct typedef_after_ident { + ptr_to_int_t buf __counted_by(count); + int count; +}; + +// ============================================================================ +// POINTER TO ARRAY: int (*buf)[4] +// ============================================================================ + +// Position: after type, before (*...) +// Applies to `int`. +struct ptr_to_arr_after_type { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) (* buf)[4]; + int count; +}; + +// Position: before type +// Applies to the top-level type. +struct ptr_to_arr_before_type { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + __counted_by(count) int (* buf)[4]; + int count; +}; + +// Position: after *, before identifier (inside parens) +// Applies to `int (*)[4]`. +struct ptr_to_arr_after_star { + int (* __counted_by(count) buf)[4]; + int count; +}; + +// Position: after identifier, before ) (inside parens) +// Invalid position - causes parse error +struct ptr_to_arr_after_ident { + int (*buf __counted_by(count))[4]; // Invalid position + // expected-error@-1{{expected ')'}} + // expected-note@-2{{to match this '('}} + int count; +}; + +// Position: after [4] +// Applies to the top-level type. +struct ptr_to_arr_after_brackets { + int (* buf)[4] __counted_by(count); + int count; +}; + +// Position: after (, before * +struct ptr_to_arr_after_lparen { + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + int (__counted_by(count) *buf)[4]; + int count; +}; + +// Position: inside [4] +struct ptr_to_arr_inside_brackets { + int (* buf)[4 __counted_by(count)]; // Invalid syntax + // expected-error@-1{{expected ']'}} + // expected-note@-2{{to match this '['}} + int count; +}; + +// Position: before [4] +struct ptr_to_arr_before_brackets { + // expected-error@+1{{expected ';' at end of declaration list}} + int (* buf) __counted_by(count) [4]; // Invalid syntax + int count; +}; + +// Position: double parens, after ((, before * +struct ptr_to_arr_double_paren1 { + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + int ((__counted_by(count) * buf))[4]; + int count; +}; + +// Position: double parens, after *, before identifier +struct ptr_to_arr_double_paren2 { + int ((* __counted_by(count) buf))[4]; + int count; +}; + +// ============================================================================ +// POINTER TO ARRAY WITH QUALIFIERS +// ============================================================================ + +// const pointer +struct ptr_to_arr_const_ptr1 { + int (* const __counted_by(count) buf)[4]; + int count; +}; + +struct ptr_to_arr_const_ptr2 { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) (* const buf)[4]; + int count; +}; + +// pointer to const +struct ptr_to_arr_ptr_to_const { + const int (* __counted_by(count) buf)[4]; + int count; +}; + +struct ptr_to_arr_ptr_to_const2 { + int const (* __counted_by(count) buf)[4]; + int count; +}; + +// restrict pointer +struct ptr_to_arr_restrict1 { + int (* __restrict __counted_by(count) buf)[4]; + int count; +}; + +struct ptr_to_arr_restrict2 { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) (* __restrict buf)[4]; + int count; +}; + +// ============================================================================ +// POINTER TO MULTI-DIMENSIONAL ARRAY: int (*buf)[4][8] +// ============================================================================ + +struct ptr_to_multidim_arr_after_type { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) (* buf)[4][8]; + int count; +}; + +struct ptr_to_multidim_arr_after_star { + int (* __counted_by(count) buf)[4][8]; + int count; +}; + +struct ptr_to_multidim_arr_middle { + // expected-error@+1{{expected ';' at end of declaration list}} + int (* buf)[4] __counted_by(count) [8]; // Invalid position + int count; +}; + +struct ptr_to_multidim_arr_after_all { + int (* buf)[4][8] __counted_by(count); + // This doesn't trigger an error - the attribute applies to the pointer + int count; +}; + +// ============================================================================ +// ARRAY OF POINTERS TO ARRAY: int (*buf[10])[4] +// ============================================================================ + +struct arr_of_ptr_to_arr_after_type { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) (* buf[10])[4]; + int count; +}; + +struct arr_of_ptr_to_arr_after_star { + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + int (* __counted_by(count) buf[10])[4]; + int count; +}; + +struct arr_of_ptr_to_arr_middle { + // expected-error@+2{{'counted_by' on arrays only applies to C99 flexible array members}} + // expected-error@+1{{expected ';' at end of declaration list}} + int (* buf[10]) __counted_by(count) [4]; // Invalid position + int count; +}; + +struct arr_of_ptr_to_arr_inside_first_brackets { + int (* buf __counted_by(count) [10])[4]; + // expected-error@-1{{expected ')'}} + // expected-note@-2{{to match this '('}} + int count; +}; + +// ============================================================================ +// TYPEDEF ARRAY: arr4_t *buf where arr4_t is int[4] +// ============================================================================ + +typedef int arr4_t[4]; + +struct typedef_arr_before_type { + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + __counted_by(count) arr4_t * buf; + int count; +}; + +struct typedef_arr_after_type { + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + arr4_t __counted_by(count) * buf; + int count; +}; + +struct typedef_arr_after_star { + arr4_t * __counted_by(count) buf; + int count; +}; + +// ============================================================================ +// FUNCTION POINTER: int (*buf)(void) +// ============================================================================ + +// Position: after *, before identifier +struct fptr_after_star { + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'int (void)' is a function type}} + int (* __counted_by(count) buf)(void); + int count; +}; + +// Position: after (, before * +struct fptr_after_lparen { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int (__counted_by(count) *buf)(void); + int count; +}; + +// ============================================================================ +// _ATOMIC POINTER VARIATIONS +// ============================================================================ + +// _Atomic(int *) - atomic pointer type +struct atomic_ptr_type { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + _Atomic(int *) __counted_by(count) buf; + int count; +}; + +// Attribute inside _Atomic (likely invalid) +struct atomic_ptr_attr_inside { + // expected-error@+1{{use of undeclared identifier 'count'}} + _Atomic(int *__counted_by(count)) buf; + int count; +}; + +struct atomic_ptr_attr_inside_no_forward_ref { + int count; + // FIXME: should not be allowed + _Atomic(int *__counted_by(count)) buf; +}; + +struct atomic_ptr_attr_after { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + __counted_by(count) _Atomic(int *) buf; + int count; +}; + +struct atomic_ptr_attr_after_no_forward_ref { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + __counted_by(count) _Atomic(int *) buf; +}; + +// _Atomic int * - could be atomic int or atomic pointer +struct atomic_ambiguous { + _Atomic int * __counted_by(count) buf; + int count; +}; + +// int *_Atomic - atomic pointer (unambiguous) +struct atomic_ptr_unambiguous1 { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int *_Atomic __counted_by(count) buf; + int count; +}; + +// __counted_by before _Atomic +struct atomic_ptr_attr_before_atomic1 { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int *__counted_by(count) _Atomic buf; + int count; +}; + +// __counted_by before * _Atomic +struct atomic_ptr_attr_before_atomic2 { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) * _Atomic buf; + int count; +}; + +// _Atomic before type +struct atomic_ptr_atomic_first1 { + _Atomic int *__counted_by(count) buf; + int count; +}; + +// _Atomic before type, attribute after * +struct atomic_ptr_atomic_first2 { + _Atomic int * __counted_by(count) buf; + int count; +}; + +// __counted_by at the end +struct atomic_ptr_attr_at_end1 { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int *_Atomic buf __counted_by(count); + int count; +}; + +// __counted_by at the end with space +struct atomic_ptr_attr_at_end2 { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * _Atomic buf __counted_by(count); + int count; +}; + +// ============================================================================ +// _ATOMIC POINTER TO ARRAY +// ============================================================================ + +struct atomic_ptr_to_arr1 { + _Atomic int (* __counted_by(count) buf)[4]; + int count; +}; + +struct atomic_ptr_to_arr2 { + // expected-error@+3{{expected a type}} + // expected-error@+2{{use of undeclared identifier 'count'}} + // expected-error@+1{{expected member name or ';' after declaration specifiers}} + int _Atomic (* __counted_by(count) buf)[4]; + int count; +}; + +struct atomic_ptr_to_arr3 { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int (* _Atomic __counted_by(count) buf)[4]; + int count; +}; + +// ============================================================================ +// ATOMIC WITH CONST/VOLATILE/RESTRICT QUALIFIERS +// ============================================================================ + +// const _Atomic pointer +struct atomic_const_ptr1 { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * const _Atomic __counted_by(count) buf; + int count; +}; + +struct atomic_const_ptr2 { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * _Atomic const __counted_by(count) buf; + int count; +}; + +struct atomic_const_ptr3 { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + const int * _Atomic __counted_by(count) buf; + int count; +}; + +// volatile _Atomic pointer +struct atomic_volatile_ptr1 { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * volatile _Atomic __counted_by(count) buf; + int count; +}; + +struct atomic_volatile_ptr2 { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * _Atomic volatile __counted_by(count) buf; + int count; +}; + +// restrict _Atomic pointer +struct atomic_restrict_ptr1 { + // expected-error@+2{{restrict requires a pointer or reference ('_Atomic(int *)' is invalid)}} + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * __restrict _Atomic __counted_by(count) buf; + int count; +}; + +struct atomic_restrict_ptr2 { + // expected-error@+2{{restrict requires a pointer or reference ('_Atomic(int *)' is invalid)}} + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * _Atomic __restrict __counted_by(count) buf; + int count; +}; + +// Combined qualifiers +struct atomic_const_volatile_ptr { + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * const volatile _Atomic __counted_by(count) buf; + int count; +}; + +struct atomic_all_qualifiers { + // expected-error@+2{{restrict requires a pointer or reference ('_Atomic(int *)' is invalid)}} + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * const volatile __restrict _Atomic __counted_by(count) buf; + int count; +}; diff --git a/clang/test/Sema/attr-counted-by-weird-type-positions.c b/clang/test/Sema/attr-counted-by-weird-type-positions.c new file mode 100644 index 0000000000000..2668ab1e18346 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-weird-type-positions.c @@ -0,0 +1,454 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define __counted_by(f) __attribute__((counted_by(f))) + +// ============================================================================ +// SIMPLE POINTER: int *buf +// ============================================================================ + +// Position: after *, before identifier +// Applies to `int *`. +struct ptr_after_star { + int count; + int *__counted_by(count) buf; +}; + +// Position: before type specifier +// Applies to the top-level type. +struct ptr_before_type { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + __counted_by(count) int *buf; +}; + +// Position: after type, before * +// Applies to `int`. +struct ptr_after_type { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) *buf; +}; + +// Position: after identifier +// Applies to the top-level type. +struct ptr_after_ident { + int count; + int *buf __counted_by(count); +}; + +// ============================================================================ +// TYPEDEF POINTER: ptr_to_int_t buf +// ============================================================================ + +typedef int * ptr_to_int_t; + +// Position: after typedef name, before identifier +// Applies to `ptr_to_int_t`. +struct typedef_after_type { + int count; + ptr_to_int_t __counted_by(count) buf; +}; + +// Position: before typedef name +// Applies to the top-level type. +struct typedef_before_type { + int count; + __counted_by(count) ptr_to_int_t buf; +}; + +// Position: after identifier +// Applies to the top-level type. +struct typedef_after_ident { + int count; + ptr_to_int_t buf __counted_by(count); +}; + +// ============================================================================ +// POINTER TO ARRAY: int (*buf)[4] +// ============================================================================ + +// Position: after type, before (*...) +// Applies to `int`. +struct ptr_to_arr_after_type { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) (* buf)[4]; +}; + +// Position: before type +// Applies to the top-level type. +struct ptr_to_arr_before_type { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + __counted_by(count) int (* buf)[4]; +}; + +// Position: after *, before identifier (inside parens) +// Applies to `int (*)[4]`. +struct ptr_to_arr_after_star { + int count; + int (* __counted_by(count) buf)[4]; +}; + +// Position: after identifier, before ) (inside parens) +// Invalid position - causes parse error +struct ptr_to_arr_after_ident { + int count; + int (*buf __counted_by(count))[4]; // Invalid position + // expected-error@-1{{expected ')'}} + // expected-note@-2{{to match this '('}} +}; + +// Position: after [4] +// Applies to the top-level type. +struct ptr_to_arr_after_brackets { + int count; + int (* buf)[4] __counted_by(count); +}; + +// Position: after (, before * +struct ptr_to_arr_after_lparen { + int count; + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + int (__counted_by(count) *buf)[4]; +}; + +// Position: inside [4] +struct ptr_to_arr_inside_brackets { + int count; + int (* buf)[4 __counted_by(count)]; // Invalid syntax + // expected-error@-1{{expected ']'}} + // expected-note@-2{{to match this '['}} +}; + +// Position: before [4] +struct ptr_to_arr_before_brackets { + int count; + // expected-error@+1{{expected ';' at end of declaration list}} + int (* buf) __counted_by(count) [4]; // Invalid syntax +}; + +// Position: double parens, after ((, before * +struct ptr_to_arr_double_paren1 { + int count; + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + int ((__counted_by(count) * buf))[4]; +}; + +// Position: double parens, after *, before identifier +struct ptr_to_arr_double_paren2 { + int count; + int ((* __counted_by(count) buf))[4]; +}; + +// ============================================================================ +// POINTER TO ARRAY WITH QUALIFIERS +// ============================================================================ + +// const pointer +struct ptr_to_arr_const_ptr1 { + int count; + int (* const __counted_by(count) buf)[4]; +}; + +struct ptr_to_arr_const_ptr2 { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) (* const buf)[4]; +}; + +// pointer to const +struct ptr_to_arr_ptr_to_const { + int count; + const int (* __counted_by(count) buf)[4]; +}; + +struct ptr_to_arr_ptr_to_const2 { + int count; + int const (* __counted_by(count) buf)[4]; +}; + +// restrict pointer +struct ptr_to_arr_restrict1 { + int count; + int (* __restrict __counted_by(count) buf)[4]; +}; + +struct ptr_to_arr_restrict2 { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) (* __restrict buf)[4]; +}; + +// ============================================================================ +// POINTER TO MULTI-DIMENSIONAL ARRAY: int (*buf)[4][8] +// ============================================================================ + +struct ptr_to_multidim_arr_after_type { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) (* buf)[4][8]; +}; + +struct ptr_to_multidim_arr_after_star { + int count; + int (* __counted_by(count) buf)[4][8]; +}; + +struct ptr_to_multidim_arr_middle { + int count; + // expected-error@+1{{expected ';' at end of declaration list}} + int (* buf)[4] __counted_by(count) [8]; // Invalid position +}; + +struct ptr_to_multidim_arr_after_all { + int count; + int (* buf)[4][8] __counted_by(count); + // This doesn't trigger an error - the attribute applies to the pointer +}; + +// ============================================================================ +// ARRAY OF POINTERS TO ARRAY: int (*buf[10])[4] +// ============================================================================ + +struct arr_of_ptr_to_arr_after_type { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) (* buf[10])[4]; +}; + +struct arr_of_ptr_to_arr_after_star { + int count; + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + int (* __counted_by(count) buf[10])[4]; +}; + +struct arr_of_ptr_to_arr_middle { + int count; + // expected-error@+2{{'counted_by' on arrays only applies to C99 flexible array members}} + // expected-error@+1{{expected ';' at end of declaration list}} + int (* buf[10]) __counted_by(count) [4]; // Invalid position +}; + +struct arr_of_ptr_to_arr_inside_first_brackets { + int count; + int (* buf __counted_by(count) [10])[4]; + // expected-error@-1{{expected ')'}} + // expected-note@-2{{to match this '('}} +}; + +// ============================================================================ +// TYPEDEF ARRAY: arr4_t *buf where arr4_t is int[4] +// ============================================================================ + +typedef int arr4_t[4]; + +struct typedef_arr_before_type { + int count; + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + __counted_by(count) arr4_t * buf; +}; + +struct typedef_arr_after_type { + int count; + // expected-error@+1{{'counted_by' attribute on nested pointer type is not allowed}} + arr4_t __counted_by(count) * buf; +}; + +struct typedef_arr_after_star { + int count; + arr4_t * __counted_by(count) buf; +}; + +// ============================================================================ +// FUNCTION POINTER: int (*buf)(void) +// ============================================================================ + +// Position: after *, before identifier +struct fptr_after_star { + int count; + // expected-error@+1{{'counted_by' cannot be applied to a pointer with pointee of unknown size because 'int (void)' is a function type}} + int (* __counted_by(count) buf)(void); +}; + +// Position: after (, before * +struct fptr_after_lparen { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int (__counted_by(count) *buf)(void); +}; + +// ============================================================================ +// _ATOMIC POINTER VARIATIONS +// ============================================================================ + +// _Atomic(int *) - atomic pointer type +struct atomic_ptr_type { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + _Atomic(int *) __counted_by(count) buf; +}; + +// Attribute inside _Atomic (likely invalid) +struct atomic_ptr_attr_inside { + int count; + _Atomic(int *__counted_by(count)) buf; +}; + +struct atomic_ptr_attr_inside_no_forward_ref { + int count; + // FIXME: should not be allowed + _Atomic(int *__counted_by(count)) buf; +}; + +struct atomic_ptr_attr_after { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + __counted_by(count) _Atomic(int *) buf; +}; + +struct atomic_ptr_attr_after_no_forward_ref { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + __counted_by(count) _Atomic(int *) buf; +}; + +// _Atomic int * - could be atomic int or atomic pointer +struct atomic_ambiguous { + int count; + _Atomic int * __counted_by(count) buf; +}; + +// int *_Atomic - atomic pointer (unambiguous) +struct atomic_ptr_unambiguous1 { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int *_Atomic __counted_by(count) buf; +}; + +// __counted_by before _Atomic +struct atomic_ptr_attr_before_atomic1 { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int *__counted_by(count) _Atomic buf; +}; + +// __counted_by before * _Atomic +struct atomic_ptr_attr_before_atomic2 { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int __counted_by(count) * _Atomic buf; +}; + +// _Atomic before type +struct atomic_ptr_atomic_first1 { + int count; + _Atomic int *__counted_by(count) buf; +}; + +// _Atomic before type, attribute after * +struct atomic_ptr_atomic_first2 { + int count; + _Atomic int * __counted_by(count) buf; +}; + +// __counted_by at the end +struct atomic_ptr_attr_at_end1 { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int *_Atomic buf __counted_by(count); +}; + +// __counted_by at the end with space +struct atomic_ptr_attr_at_end2 { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * _Atomic buf __counted_by(count); +}; + +// ============================================================================ +// _ATOMIC POINTER TO ARRAY +// ============================================================================ + +struct atomic_ptr_to_arr1 { + int count; + _Atomic int (* __counted_by(count) buf)[4]; +}; + +struct atomic_ptr_to_arr2 { + int count; + // expected-error@+2{{expected a type}} + // expected-error@+1{{expected member name or ';' after declaration specifiers}} + int _Atomic (* __counted_by(count) buf)[4]; +}; + +struct atomic_ptr_to_arr3 { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int (* _Atomic __counted_by(count) buf)[4]; +}; + +// ============================================================================ +// ATOMIC WITH CONST/VOLATILE/RESTRICT QUALIFIERS +// ============================================================================ + +// const _Atomic pointer +struct atomic_const_ptr1 { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * const _Atomic __counted_by(count) buf; +}; + +struct atomic_const_ptr2 { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * _Atomic const __counted_by(count) buf; +}; + +struct atomic_const_ptr3 { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + const int * _Atomic __counted_by(count) buf; +}; + +// volatile _Atomic pointer +struct atomic_volatile_ptr1 { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * volatile _Atomic __counted_by(count) buf; +}; + +struct atomic_volatile_ptr2 { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * _Atomic volatile __counted_by(count) buf; +}; + +// restrict _Atomic pointer +struct atomic_restrict_ptr1 { + int count; + // expected-error@+2{{restrict requires a pointer or reference ('_Atomic(int *)' is invalid)}} + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * __restrict _Atomic __counted_by(count) buf; +}; + +struct atomic_restrict_ptr2 { + int count; + // expected-error@+2{{restrict requires a pointer or reference ('_Atomic(int *)' is invalid)}} + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * _Atomic __restrict __counted_by(count) buf; +}; + +// Combined qualifiers +struct atomic_const_volatile_ptr { + int count; + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * const volatile _Atomic __counted_by(count) buf; +}; + +struct atomic_all_qualifiers { + int count; + // expected-error@+2{{restrict requires a pointer or reference ('_Atomic(int *)' is invalid)}} + // expected-error@+1{{'counted_by' only applies to pointers or C99 flexible array members}} + int * const volatile __restrict _Atomic __counted_by(count) buf; +}; _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
