https://github.com/rapidsna created
https://github.com/llvm/llvm-project/pull/224553
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.
>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