Anon
Anon

Reputation: 351

Incorrect `cast-align` warnings on GCC and Clang even with `__attribute__((aligned))`

Background

I have a struct with a flexible array member, which I am using to store arbitrarily-sized data inline (for a generic linked list for any element type), and I can simply cast data to T* to access the data (it will only store one struct of T). Obviously, I need to ensure that the field is aligned for any type T. I do this via use of __attribute__((aligned)) and static_assert, which shows me the field is clearly aligned.

I am having an issue where my compilers (GCC and Clang) do not seem to be correctly detecting this alignment, and are giving me cast alignment warnings with -Wcast-align=strict.

Sample

#include <assert.h>
#include <stddef.h>
#include <stdalign.h>

// `data` is aligned to 16 bytes
struct S { int data_len; __attribute__((aligned(16))) char data[]; };
// validate the alignment is correct
static_assert((offsetof(struct S, data) % 16) == 0);
// struct is aligned to alignment of largest member, in this case `data`
static_assert(alignof(struct S) == 16);

// any type with a large required alignment
typedef long double big;
static_assert(alignof(big) > alignof(char));

big *f_s(struct S *s) {
    // the `data` field is aligned to a multiple of the required alignment
    // for a struct and a pointer of `big`
    static_assert((offsetof(struct S, data) % alignof(big)) == 0);
    static_assert((offsetof(struct S, data) % alignof(big*)) == 0);
    return (big*)s->data;
}

Issue

Compilation with gcc input.c -Wcast-align=strict -c:

input.c: In function ‘f_s’:
input.c:21:12: warning: cast increases required alignment of target type [-Wcast-align]
   21 |     return (big*)s->data;
      |            ^

Compilation with clang input.c -Wcast-align -Wno-c23-extensions -c:

input.c:21:12: warning: cast from 'char *' to 'big *' (aka 'long double *') increases required alignment from 1 to 16 [-Wcast-align]
   21 |     return (big*)s->data;
      |            ^~~~~~~~~~~~~

Obviously this is wrong, as s->data must be aligned more than big, else the static_assert would be throwing errors. Is there something that I am missing here, or is this a bug in GCC and Clang?

Upvotes: 2

Views: 124

Answers (1)

chux
chux

Reputation: 154127

... as s->data must be aligned more than big ...

Is not quite so. Code (big*)s->data is like (big*)(&(s->data[0])) and that is converting a pointer to an object requiring 1 byte alignment (char *) to a pointer to an object requiring more than 1 byte alignment (long double *).


Instead, consider using max_align_t for the FMA .data type.

#include <assert.h>
#include <stddef.h>
#include <stdalign.h>

typedef long double big;

struct S_alt {
  int char_data_size;  // Consider `size_t char_data_size;`
  max_align_t data[];
};

big* f_s_alt(struct S_alt *s) {
  return (big*) s->data;
}

This may affect how .char_data_size is derived and used in OP's unposted code, yet would need to see it to best advise how to handle.

Upvotes: 0

Related Questions