cplusplus / CWG

Core Working Group
23 stars 7 forks source link

[basic.align] p1 Whether a offset address satisfies the alignment is not clear #383

Closed xmh0511 closed 1 year ago

xmh0511 commented 1 year ago

Full name of submitter (unless configured in github; will be published with the issue): Jim X

Consider this example:

alignas(int) unsigned char buff[10];
auto p = buff;
p+=sizeof(int);
new (p) int{0};

The alignas(int) can provide that the address of the first byte occupied by object char[10] will satisfy the alignment requirement imposed by object type int, however, it is not clear whether the address represented by the pointer value p+sizeof(int) can still satisfy the requirement or not because [basic.align] p1 is not clear in this respect:

Object types have alignment requirements ([basic.fundamental], [basic.compound]) which place restrictions on the addresses at which an object of that type may be allocated. An alignment is an implementation-defined integer value representing the number of bytes between successive addresses at which a given object can be allocated.

jensmaurer commented 1 year ago

I think the desired property follows from the specification of arrays.

An array of int is required to consist of contiguous "int"s (i.e. no padding outside of the array elements). Thus, if a[0] is aligned, a[1] must also be aligned. And the address difference is exactly sizeof(int).

xmh0511 commented 1 year ago

I think the desired property follows from the specification of arrays.

An array of int is required to consist of contiguous "int"s (i.e. no padding outside of the array elements). Thus, if a[0] is aligned, a[1] must also be aligned. And the address difference is exactly sizeof(int).

The object is an array of 10 unsigned char rather than int.

jensmaurer commented 1 year ago

Sure, but there do exist arrays of 10 ints, and those ints are the same ints as the ones you're creating here.

xmh0511 commented 1 year ago

Sure, but there do exist arrays of 10 ints, and those ints are the same ints as the ones you're creating here.

Why do we have an array of 10 ints when the declaration is an array of 10 chars?

int main(){
   alignas(int) unsigned char buff[10];
   int int_buff[10];
   static_assert(sizeof(buff) == sizeof(int_buff)); // ill-formed
}
jensmaurer commented 1 year ago

The implementation has to provide support for an array of ints, in general (and unrelated to your example). The required properties for such arrays indirectly define properties for "int"s in general, in this case: alignment.

xmh0511 commented 1 year ago

The required properties for such arrays indirectly define properties for "int"s in general, in this case: alignment.

The array created by alignas(int) unsigned char buff[10]; at most can provide storage for two int objects, I think. I don't understand what you meant. Do you say, if the address A satisfies the alignment requirement imposed by int, then A + alignof(int) will also satisfy that alignment requirement, as per the definition below:

An alignment is an implementation-defined integer value representing the number of bytes between successive addresses at which a given object can be allocated.

jensmaurer commented 1 year ago

I am not referring to your example at all.

I am saying that, in general, an implementation must support the type "array of int". The constraints on such arrays mean that offsetting the address of a well-aligned int by sizeof(int) gives another well-aligned address for an int.

xmh0511 commented 1 year ago

I am not referring to your example at all.

I am saying that, in general, an implementation must support the type "array of int". The constraints on such arrays mean that offsetting the address of a well-aligned int by sizeof(int) gives another well-aligned address for an int.

Ok, that's the matter of an array of N int. However, in this issue and the fix in https://github.com/cplusplus/draft/pull/6422, the array is an array of N char. alignas(int) just make the address(named A) of the first byte occupied by the array can satisfy the alignment requirement of int, how about the successive bytes? For example A+ 4, A+8, and so forth.

jensmaurer commented 1 year ago

Ok, that's the matter of an array of N int.

No, that's a matter of int, by the fact that an int can be part of an array, and the alignment requirements of all int (regardless of where they are) are the same.

xmh0511 commented 1 year ago

No, that's a matter of int, by the fact that an int can be part of an array, and the alignment requirements of all int (regardless of where they are) are the same.

I still don't understand what it relates to an array of N unsigned char.

alignas(int) unsigned char buff[10];

&a[0] is sure to satisfy the alignment of int. How about &a[1], &a[2] ... &a[10]? Do you mean they all satisfy the alignment of int since &a[0] satisfy the requirement?

jensmaurer commented 1 year ago

No, I'm saying that &a[sizeof(int)] satisfies the alignment of int if &a[0] satisfies it. Because, if you were to put an array of int inside "a", the address of the second int must be equal to the address of a[sizeof(int)].

xmh0511 commented 1 year ago

Because, if you were to put an array of int inside "a", the address of the second int must be equal to the address of a[sizeof(int)].

Since the special of an array of unsigned char as per [basic.types.general] p4

The object representation of a complete object type T is the sequence of N unsigned char objects taken up by a non-bit-field complete object of type T, where N equals sizeof(T).

we can think in this way. If we change the example to:

aliasof(int) short buff[8];
auto p = buff;
p+= 2 * sizeof(short);// assume sizeof(int) equals to 2x sizeof(short)
new (p) int{0};

Now, I think we cannot use the above mental model to interpret whether p satisfy the alignment of int or not.

jensmaurer commented 1 year ago

Now, I think we cannot use the above mental model to interpret whether p satisfy the alignment of int or not.

Sure we can. Alignment is concerned only with addresses, and your new "buff" also has an underlying "array of unsigned char" as the object representation.

xmh0511 commented 1 year ago

Now, I think we cannot use the above mental model to interpret whether p satisfy the alignment of int or not.

Sure we can. Alignment is concerned only with addresses, and your new "buff" also has an underlying "array of unsigned char" as the object representation.

Note [basic.align] p1:

An alignment is an implementation-defined integer value representing the number of bytes between successive addresses at which a given object can be allocated.

IIUC, assume the address A satisfy the alignment of int, then A + alignof(int) will also satisfy the alignment requirement, as well as, A + N * alignof(int). However, A+ sizeof(int) may not because sizeof(int) does not has the value of the alignment of int, and [basic.align] p1 only guarantees A + N * alignof(int), unless sizeof(int) equals to alignof(int), however, they are both implementation-defined value and not be gurannteed to have the same value.

jensmaurer commented 1 year ago

Please read my reasoning above that an array of int (any such array) has an int every sizeof(int) bytes, and therefore sizeof(int) must be a multiple of alignof(int).

xmh0511 commented 1 year ago

Please read my reasoning above that an array of int (any such array) has an int every sizeof(int) bytes, and therefore sizeof(int) must be a multiple of alignof(int).

Ah, I got your idea. You said, for int arr[2], because &arr[0] and &arr[1] are two adjacent addresses and their distance is sizeof(int), &arr[1] is a valid address at which an int object locates, so sizeof(int) must be an integral multiple of alignof(int).