llvm-project/clang/test/Sema/attr-counted-by-or-null-struct-ptrs-completable-incomplete-pointee.c
Dan Liew 59eafd1bdc
[BoundsSafety][Sema] Allow counted_by and counted_by_or_null on pointers where the pointee type is incomplete but potentially completable (#106321)
Previously using the `counted_by` or `counted_by_or_null` attribute on a
pointer with an incomplete pointee type was forbidden. Unfortunately
this prevented a situation like the following from being allowed.

Header file:

```
struct EltTy; // Incomplete type
struct Buffer {
  size_t count;
  struct EltTy* __counted_by(count) buffer; // error before this patch
};
```

Implementation file:

```
struct EltTy {
  // definition
};

void allocBuffer(struct Buffer* b) {
  b->buffer = malloc(sizeof(EltTy)* b->count);
}
```

To allow code like the above but still enforce that the pointee
type size is known in locations where `-fbounds-safety` needs to
emit bounds checks the following scheme is used.

* For incomplete pointee types that can never be completed (e.g. `void`)
  these are treated as error where the attribute is written (just like
  before this patch).
* For incomplete pointee types that might be completable later on
  (struct, union, and enum forward declarations)
  in the translation unit, writing the attribute on the incomplete
  pointee type is allowed on the FieldDecl declaration but "uses" of the
  declared pointer are forbidden if at the point of "use" the pointee
  type is still incomplete.

For this patch a "use" of a FieldDecl covers:

* Explicit and Implicit initialization (note see **Tentative Definition
  Initialization** for an exception to this)
* Assignment
* Conversion to an lvalue (e.g. for use in an expression)

In the swift lang fork of Clang the `counted_by` and
`counted_by_or_null` attribute are allowed in many more contexts. That
isn't the case for upstream Clang so the "use" checks for the attribute
on VarDecl, ParamVarDecl, and function return type have been omitted
from this patch because they can't be tested. However, the
`BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy` and
`BoundsSafetyCheckUseOfCountAttrPtrWithIncompletePointeeTy` functions
retain the ability to emit diagnostics for these other contexts to avoid
unnecessary divergence between upstream Clang and Apple's internal fork.
Support for checking "uses" will be upstreamed when upstream Clang
allows the `counted_by` and `counted_by_or_null` attribute in additional
contexts.

This patch has a few limitations:

** 1. Tentative Defition Initialization **

This patch currently allows something like:

```
struct IncompleteTy;
struct Buffer {
  int count;
  struct IncompleteTy* __counted_by(count) buf;
};

// Tentative definition
struct Buffer GlobalBuf;
```

The Tentative definition in this example becomes an actual definition
whose initialization **should be checked** but it currently isn't.
Addressing this problem will be done in a subseqent patch.

** 2. When the incomplete pointee type is a typedef diagnostics are slightly misleading **

For this situation:

```

struct IncompleteTy;
typedef struct IncompleteTy Incomplete_t;

struct Buffer {
  int count;
  struct IncompleteTy* __counted_by(count) buf;
};

void use(struct Buffer b) {
  b.buf = 0x0;
}
```

This code emits `note: forward declaration of 'Incomplete_t' (aka
'struct IncompleteTy')` but the location is on the `struct
IncompleteTy;` forward declaration.  This is misleading because
`Incomplete_t` isn't actually forward declared there (instead the
underlying type is). This could be resolved by additional diagnostics
that walk the chain of typedefs and explain each step of the walk.
However, that would be very verbose and didn't seem like a direction
worth pursuing.

rdar://133600117
2025-04-18 15:26:04 -07:00

632 lines
43 KiB
C

// RUN: %clang_cc1 -fsyntax-only -verify %s
// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s
#define __counted_by_or_null(f) __attribute__((counted_by_or_null(f)))
// =============================================================================
// # Struct incomplete type with attribute in the decl position
// =============================================================================
// Note: This could be considered misleading. The typedef isn't actually on this
// line. Also note the discrepancy in diagnostic count (27 vs 51) is due to
// the pointer arithmetic on incomplete pointee type diagnostic always using
// diagnostic text that refers to the underlying forward decl, even when the
// typedef is used.
// expected-note@+3 27{{consider providing a complete definition for 'Incomplete_t' (aka 'struct IncompleteTy')}}
// The 'forward declaration' notes come from 'arithmetic on a pointer to an incomplete type' errors
// expected-note@+1 24{{forward declaration of 'struct IncompleteTy'}}
struct IncompleteTy; // expected-note 27{{consider providing a complete definition for 'struct IncompleteTy'}}
typedef struct IncompleteTy Incomplete_t;
struct CBBufDeclPos {
int count;
struct IncompleteTy* buf __counted_by_or_null(count); // OK expected-note 27{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}}
Incomplete_t* buf_typedef __counted_by_or_null(count); // OK expected-note 27{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}}
};
void consume_struct_IncompleteTy(struct IncompleteTy* buf);
int idx(void);
void test_CBBufDeclPos(struct CBBufDeclPos* ptr) {
// ===========================================================================
// ## Local variable initialization
// ===========================================================================
struct CBBufDeclPos explicit_desig_init = {
.count = 0,
// expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
.buf = 0x0,
// expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
.buf_typedef = 0x0
};
// Variable is not currently marked as invalid so uses of the variable allows
// diagnostics to fire.
// expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
explicit_desig_init.buf = 0x0;
// expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
explicit_desig_init.buf_typedef = 0x0;
// expected-error@+1{{cannot use 'explicit_desig_init.buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
void *tmp = explicit_desig_init.buf;
// expected-error@+1{{cannot use 'explicit_desig_init.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
void *tmp2 = explicit_desig_init.buf_typedef;
struct CBBufDeclPos partial_explicit_desig_init = {
.count = 0,
// .buf and .buf_typedef are implicit zero initialized
// expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
// expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
};
struct CBBufDeclPos implicit_full_init = {
0
// expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
// expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
};
// Variable is not currently marked as invalid so uses of the variable allows
// diagnostics to fire.
// expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
implicit_full_init.buf = 0x0;
// expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
implicit_full_init.buf_typedef = 0x0;
// expected-error@+1{{cannot use 'implicit_full_init.buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
void* tmp3 = implicit_full_init.buf;
// expected-error@+1{{cannot use 'implicit_full_init.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
void* tmp4 = implicit_full_init.buf_typedef;
struct CBBufDeclPos explicit_non_desig_init = {
0,
// expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
0x0,
// expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
0x0
};
// ===========================================================================
// ## Assignment to fields
// ===========================================================================
struct CBBufDeclPos uninit;
uninit.count = 0;
// expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
uninit.buf = 0x0;
// expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
uninit.buf_typedef = 0x0;
ptr->count = 0;
// expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
ptr->buf = 0x0;
// expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
ptr->buf_typedef = 0x0;
// ===========================================================================
// ## Make sure modifying the fields through other assignment operators is not
// allowed
// ===========================================================================
uninit.buf++; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
++uninit.buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
uninit.buf += 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
uninit.buf_typedef++; // // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
++uninit.buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
uninit.buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
uninit.buf--; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
--uninit.buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
uninit.buf -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
uninit.buf_typedef--; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
--uninit.buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
uninit.buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
ptr->buf++; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
++ptr->buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
ptr->buf += 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
ptr->buf--; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
--ptr->buf; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
ptr->buf -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'struct IncompleteTy'}}
ptr->buf_typedef++; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
++ptr->buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
ptr->buf_typedef += 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
ptr->buf_typedef--; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
--ptr->buf_typedef; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
ptr->buf_typedef -= 1; // expected-error{{arithmetic on a pointer to an incomplete type 'Incomplete_t' (aka 'struct IncompleteTy')}}
// ===========================================================================
// ## Use of fields in expressions
// ===========================================================================
// expected-error@+2{{cannot use 'uninit.buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
void* addr =
((char*) uninit.buf ) + 1;
// expected-error@+2{{cannot use 'uninit.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
void* addr_typedef =
((char*) uninit.buf_typedef ) + 1;
// expected-error@+2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
void* addr_ptr =
((char*) ptr->buf ) + 1;
// expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
void* addr_ptr_typedef =
((char*) ptr->buf_typedef ) + 1;
// ===========================================================================
// ## Take address of fields
// ===========================================================================
// TODO: This should be forbidden, not because of the incomplete pointee type
// but because in the -fbounds-safety language model the address of a
// `counted_by` pointer cannot be taken to avoid it being possible to modify
// the `counted_by` pointer through another pointer. Whether or not this
// should be forbidden when `-fbounds-safety` is off is TBD.
//
// The incomplete pointee type isn't actually a problem here for
// `-fbounds-safety` because taking the address of a pointer returns a pointer
// that have the bounds of a single `void*`, so bounds checks on the resulting
// pointer don't need to know `sizeof(struct IncompleteTy)` but instead
// `sizeof(struct IncompleteTy* buf __counted_by_or_null(count))` which is just the
// size of a pointer.
void* take_addr = &uninit.buf;
void* take_addr_typedef = &uninit.buf_typedef;
void* take_addr_ptr = &ptr->buf;
void* take_addr_ptr_typedef = &ptr->buf_typedef;
// expected-error@+1{{cannot use 'uninit.buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
struct IncompleteTy* addr_elt_zero = &uninit.buf[0];
// expected-error@+1{{cannot use 'uninit.buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
struct IncompleteTy* addr_elt_idx = &uninit.buf[idx()];
// expected-error@+1{{cannot use 'uninit.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
struct IncompleteTy* addr_elt_zero_typedef = &uninit.buf_typedef[0];
// expected-error@+1{{cannot use 'uninit.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
struct IncompleteTy* addr_elt_idx_typedef = &uninit.buf_typedef[idx()];
// expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
struct IncompleteTy* addr_elt_zero_ptr = &ptr->buf[0];
// expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
struct IncompleteTy* addr_elt_idx_ptr = &ptr->buf[idx()];
// expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
struct IncompleteTy* addr_elt_zero_ptr_typedef = &ptr->buf_typedef[0];
// expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
struct IncompleteTy* addr_elt_idx_ptr_typedef = &ptr->buf_typedef[idx()];
// ===========================================================================
// ## Use fields as call arguments
// ===========================================================================
// expected-error@+1{{cannot use 'uninit.buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
consume_struct_IncompleteTy(uninit.buf);
// expected-error@+1{{cannot use 'uninit.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
consume_struct_IncompleteTy(uninit.buf_typedef);
// expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
consume_struct_IncompleteTy(ptr->buf);
// expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
consume_struct_IncompleteTy(ptr->buf_typedef);
// ===========================================================================
// ## Use [] operator on fields
// ===========================================================================
// expected-error@+1 2{{cannot use 'uninit.buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
uninit.buf[0] = uninit.buf[1];
// expected-error@+1 2{{cannot use 'uninit.buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
uninit.buf_typedef[0] = uninit.buf_typedef[1];
// expected-error@+1 2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
ptr->buf[0] = ptr->buf[1];
// expected-error@+1 2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
ptr->buf_typedef[0] = ptr->buf_typedef[1];
}
// =============================================================================
// ## Global initialization
// =============================================================================
struct CBBufDeclPos global_explicit_desig_init = {
.count = 0,
// expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
.buf = 0x0,
// expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
.buf_typedef = 0x0
};
void use_global_explicit_desig_init(void) {
// Variable isn't marked as invalid so diagnostics still fire
// expected-error@+1{{cannot assign to 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
global_explicit_desig_init.buf = 0x0;
// expected-error@+1{{cannot assign to 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
global_explicit_desig_init.buf_typedef = 0x0;
}
struct CBBufDeclPos global_partial_explicit_desig_init = {
.count = 0,
// .buf and .buf_typedef are implicit zero initialized
// expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
// expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
};
struct CBBufDeclPos global_implicit_full_init = {
0
// expected-error@+2{{cannot implicitly initialize 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
// expected-error@+1{{cannot implicitly initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
};
struct CBBufDeclPos global_explicit_non_desig_init = {
0,
// expected-error@+1{{cannot initialize 'CBBufDeclPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'struct IncompleteTy' is incomplete}}
0x0,
// expected-error@+1{{cannot initialize 'CBBufDeclPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_t * __counted_by_or_null(count)' (aka 'struct IncompleteTy *') because the pointee type 'Incomplete_t' (aka 'struct IncompleteTy') is incomplete}}
0x0
};
extern struct CBBufDeclPos global_declaration; // OK
// TODO: These tentative definitions are implicitly empty initialized to zero.
// This should generate an error diagnostic but currently doesn't. There should
// be a carve out to allow `__counted_by_or_null(0)` which is the only constant count
// version of the attribute where it is valid to assign NULL.
struct CBBufDeclPos global_tentative_defn;
static struct CBBufDeclPos global_tentative_defn2;
// =============================================================================
// ## Completing the definition of the type allows use of CBBufDeclPos fields
// =============================================================================
struct IncompleteTy {
int field;
};
void test_CBBufDeclPos_completed(struct CBBufDeclPos* ptr) {
// Initialization is ok
struct CBBufDeclPos explicit_desig_init = {
.count = 0,
.buf = 0x0,
.buf_typedef = 0x0
};
struct CBBufDeclPos partial_explicit_desig_init = {
.count = 0,
// .buf and .buf_typedef are implicit zero initialized
};
struct CBBufDeclPos implicit_full_init = {0};
struct CBBufDeclPos explicit_non_desig_init = {
0,
0x0,
0x0
};
// Assignment to fields is ok
ptr->buf = 0x0;
ptr->buf_typedef = 0x0;
// Use of fields in expressions is ok
void* tmp = ptr->buf;
void* tmp2 = ptr->buf_typedef;
// Take address of fields is ok
void* take_addr_ptr = &ptr->buf;
void* take_addr_ptr_typedef = &ptr->buf_typedef;
struct IncompleteTy* addr_elt_zero_ptr = &ptr->buf[0];
struct IncompleteTy* addr_elt_idx_ptr = &ptr->buf[idx()];
struct IncompleteTy* addr_elt_zero_ptr_typedef = &ptr->buf_typedef[0];
struct IncompleteTy* addr_elt_idx_ptr_typedef = &ptr->buf_typedef[idx()];
// As call arguments is ok
consume_struct_IncompleteTy(ptr->buf);
consume_struct_IncompleteTy(ptr->buf_typedef);
// In [] operator is ok
ptr->buf[0] = ptr->buf[1];
ptr->buf_typedef[0] = ptr->buf_typedef[1];
}
// Global initialization is ok
struct CBBufDeclPos global_explicit_desig_init_completed = {
.count = 0,
.buf = 0x0,
.buf_typedef = 0x0
};
struct CBBufDeclPos global_partial_explicit_desig_init_completed = {
.count = 0,
// .buf and .buf_typedef are implicit zero initialized
};
struct CBBufDeclPos global_implicit_full_init_completed = {0};
struct CBBufDeclPos global_explicit_non_desig_init_completed = {
0,
0x0,
0x0
};
extern struct CBBufDeclPos global_declaration;
struct CBBufDeclPos global_tentative_defn;
static struct CBBufDeclPos global_tentative_defn2;
// =============================================================================
// # Struct incomplete type with attribute in the pointer position
// =============================================================================
// expected-note@+1 8{{consider providing a complete definition for 'Incomplete_ty2' (aka 'struct IncompleteTy2')}}
struct IncompleteTy2; // expected-note 8{{consider providing a complete definition for 'struct IncompleteTy2'}}
typedef struct IncompleteTy2 Incomplete_ty2;
void consume_struct_IncompleteTy2(struct IncompleteTy2* buf);
struct CBBufTyPos {
int count;
struct IncompleteTy2* __counted_by_or_null(count) buf ; // OK expected-note 8{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}}
Incomplete_ty2 *__counted_by_or_null(count) buf_typedef; // OK expected-note 8{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}}
};
void use_CBBufTyPos(struct CBBufTyPos* ptr) {
struct CBBufTyPos explicit_desig_init = {
.count = 0,
// expected-error@+1{{cannot initialize 'CBBufTyPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}}
.buf = 0x0,
// expected-error@+1{{cannot initialize 'CBBufTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}}
.buf_typedef = 0x0
};
// Assignment
// expected-error@+1{{cannot assign to 'CBBufTyPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}}
explicit_desig_init.buf = 0x0;
// expected-error@+1{{cannot assign to 'CBBufTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}}
explicit_desig_init.buf_typedef = 0x0;
// expected-error@+1{{cannot assign to 'CBBufTyPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}}
ptr->buf = 0x0;
// expected-error@+1{{cannot assign to 'CBBufTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}}
ptr->buf_typedef = 0x0;
// Use
// expected-error@+2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}}
void* addr =
((char*) ptr->buf ) + 1;
// expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}}
void* addr_typedef =
((char*) ptr->buf_typedef ) + 1;
// expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}}
consume_struct_IncompleteTy2(ptr->buf);
// expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}}
consume_struct_IncompleteTy2(ptr->buf_typedef);
// expected-error@+1 2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}}
ptr->buf[0] = ptr->buf[1];
// expected-error@+1 2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}}
ptr->buf_typedef[0] = ptr->buf_typedef[1];
}
struct CBBufTyPos global_explicit_desig_init_struct_type_pos = {
.count = 0,
// expected-error@+1{{cannot initialize 'CBBufTyPos::buf' with '__counted_by_or_null' attributed type 'struct IncompleteTy2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'struct IncompleteTy2' is incomplete}}
.buf = 0x0,
// expected-error@+1{{cannot initialize 'CBBufTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'Incomplete_ty2 * __counted_by_or_null(count)' (aka 'struct IncompleteTy2 *') because the pointee type 'Incomplete_ty2' (aka 'struct IncompleteTy2') is incomplete}}
.buf_typedef = 0x0
};
// Defining the type makes `CBBufTyPos` fields usable
struct IncompleteTy2 {
int field;
};
void use_CBBufTyPos_completed(struct CBBufTyPos* ptr) {
ptr->buf = 0x0;
ptr->buf_typedef = 0x0;
void* addr = ((char*) ptr->buf) + 1;
void* addr_typedef = ((char*) ptr->buf_typedef) + 1;
}
// =============================================================================
// # union incomplete type
// =============================================================================
// expected-note@+1 8{{consider providing a complete definition for 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy')}}
union IncompleteUnionTy; // expected-note 8{{consider providing a complete definition for 'union IncompleteUnionTy'}}
typedef union IncompleteUnionTy IncompleteUnion_ty;
void consume_struct_IncompleteUnionTy(union IncompleteUnionTy* buf);
struct CBBufUnionTyPos {
int count;
union IncompleteUnionTy* __counted_by_or_null(count) buf ; // OK expected-note 8{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}}
IncompleteUnion_ty *__counted_by_or_null(count) buf_typedef; // OK expected-note 8{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}}
};
void use_CBBufUnionTyPos(struct CBBufUnionTyPos* ptr) {
struct CBBufUnionTyPos explicit_desig_init = {
.count = 0,
// expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}}
.buf = 0x0,
// expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}}
.buf_typedef = 0x0
};
// Assignment
// expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}}
explicit_desig_init.buf = 0x0;
// expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}}
explicit_desig_init.buf_typedef = 0x0;
// expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}}
ptr->buf = 0x0;
// expected-error@+1{{cannot assign to 'CBBufUnionTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}}
ptr->buf_typedef = 0x0;
// Use
// expected-error@+2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}}
void* addr =
((char*) ptr->buf ) + 1;
// expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}}
void* addr_typedef =
((char*) ptr->buf_typedef ) + 1;
// expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}}
consume_struct_IncompleteUnionTy(ptr->buf);
// expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}}
consume_struct_IncompleteUnionTy(ptr->buf_typedef);
// expected-error@+1 2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}}
ptr->buf[0] = ptr->buf[1];
// expected-error@+1 2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}}
ptr->buf_typedef[0] = ptr->buf_typedef[1];
}
struct CBBufUnionTyPos global_explicit_desig_init_union_type_pos = {
.count = 0,
// expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf' with '__counted_by_or_null' attributed type 'union IncompleteUnionTy * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'union IncompleteUnionTy' is incomplete}}
.buf = 0x0,
// expected-error@+1{{cannot initialize 'CBBufUnionTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteUnion_ty * __counted_by_or_null(count)' (aka 'union IncompleteUnionTy *') because the pointee type 'IncompleteUnion_ty' (aka 'union IncompleteUnionTy') is incomplete}}
.buf_typedef = 0x0
};
// Defining the type makes `CBBufUnionTyPos` fields usable
union IncompleteUnionTy {
int field;
};
void use_CBBufUnionTyPos_completed(struct CBBufUnionTyPos* ptr) {
ptr->buf = 0x0;
ptr->buf_typedef = 0x0;
void* addr = ((char*) ptr->buf) + 1;
void* addr_typedef = ((char*) ptr->buf_typedef) + 1;
}
// =============================================================================
// # enum incomplete type
// =============================================================================
// expected-note@+1 8{{consider providing a complete definition for 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy')}}
enum IncompleteEnumTy; // expected-note 8{{consider providing a complete definition for 'enum IncompleteEnumTy'}}
typedef enum IncompleteEnumTy IncompleteEnum_ty;
void consume_struct_IncompleteEnumTy(enum IncompleteEnumTy* buf);
struct CBBufEnumTyPos {
int count;
enum IncompleteEnumTy* __counted_by_or_null(count) buf ; // OK expected-note 8{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}}
IncompleteEnum_ty *__counted_by_or_null(count) buf_typedef; // OK expected-note 8{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}}
};
void use_CBBufEnumTyPos(struct CBBufEnumTyPos* ptr) {
struct CBBufEnumTyPos explicit_desig_init = {
.count = 0,
// expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}}
.buf = 0x0,
// expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}}
.buf_typedef = 0x0
};
// Assignment
// expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}}
explicit_desig_init.buf = 0x0;
// expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}}
explicit_desig_init.buf_typedef = 0x0;
// expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}}
ptr->buf = 0x0;
// expected-error@+1{{cannot assign to 'CBBufEnumTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}}
ptr->buf_typedef = 0x0;
// Use
// expected-error@+2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}}
void* addr =
((char*) ptr->buf ) + 1;
// expected-error@+2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}}
void* addr_typedef =
((char*) ptr->buf_typedef ) + 1;
// expected-error@+1{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}}
consume_struct_IncompleteEnumTy(ptr->buf);
// expected-error@+1{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}}
consume_struct_IncompleteEnumTy(ptr->buf_typedef);
// expected-error@+1 2{{cannot use 'ptr->buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}}
ptr->buf[0] = ptr->buf[1];
// expected-error@+1 2{{cannot use 'ptr->buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}}
ptr->buf_typedef[0] = ptr->buf_typedef[1];
}
struct CBBufEnumTyPos global_explicit_desig_init_enum_type_pos = {
.count = 0,
// expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf' with '__counted_by_or_null' attributed type 'enum IncompleteEnumTy * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'enum IncompleteEnumTy' is incomplete}}
.buf = 0x0,
// expected-error@+1{{cannot initialize 'CBBufEnumTyPos::buf_typedef' with '__counted_by_or_null' attributed type 'IncompleteEnum_ty * __counted_by_or_null(count)' (aka 'enum IncompleteEnumTy *') because the pointee type 'IncompleteEnum_ty' (aka 'enum IncompleteEnumTy') is incomplete}}
.buf_typedef = 0x0
};
// Defining the type makes `CBBufEnumTyPos` fields usable
enum IncompleteEnumTy {
ONE,
TWO
};
void use_CBBufEnumTyPos_completed(struct CBBufEnumTyPos* ptr) {
ptr->buf = 0x0;
ptr->buf_typedef = 0x0;
void* addr = ((char*) ptr->buf) + 1;
void* addr_typedef = ((char*) ptr->buf_typedef) + 1;
}
// Make a complete enum by providing an underlying type
enum CompleteEnumTy : unsigned;
typedef enum CompleteEnumTy CompleteEnum_ty;
struct CBBufEnumTyPos2 {
int count;
enum CompleteEnumTy* __counted_by_or_null(count) buf;
CompleteEnum_ty *__counted_by_or_null(count) buf_typedef;
};
void use_CBBufEnumTyPos2(struct CBBufEnumTyPos2* ptr) {
struct CBBufEnumTyPos2 explicit_desig_init = {
.count = 0,
.buf = 0x0, // OK
.buf_typedef = 0x0 // OK
};
}
// Make a complete enum by providing a concrete declaration
enum CompleteEnumTy2 {
VALUE_ONE,
VALUE_TWO
};
typedef enum CompleteEnumTy2 CompleteEnum_ty2;
struct CBBufEnumTyPos3 {
int count;
enum CompleteEnumTy2* __counted_by_or_null(count) buf;
CompleteEnum_ty2 *__counted_by_or_null(count) buf_typedef;
};
void use_CBBufEnumTyPos3(struct CBBufEnumTyPos3* ptr) {
struct CBBufEnumTyPos3 explicit_desig_init = {
.count = 0,
.buf = 0x0, // OK
.buf_typedef = 0x0 // OK
};
}
// =============================================================================
// # Array of __counted_by_or_null pointers
// =============================================================================
struct IncompleteTy3;
struct CBBufFAMofCountedByPtrs {
int size;
// TODO: This is misleading. The attribute is written in the type position
// but clang currently doesn't treat it like that and it gets treated as
// an attribute on the array, rather than on the element type.
// expected-error@+1{{'counted_by_or_null' only applies to pointers; did you mean to use 'counted_by'?}}
struct IncompleteTy3* __counted_by_or_null(size) arr[];
};
void arr_of_counted_by_ptr(struct CBBufFAMofCountedByPtrs* ptr) {
// TODO: Should be disallowed once parsing attributes in the type position
// works.
ptr->arr[0] = 0x0;
void* addr = ((char*) ptr->arr[0]) + 1;
}