Pointers to objects with new-extended alignment

72 views
Skip to first unread message

Timur Doumler

unread,
Dec 2, 2017, 5:45:36 PM12/2/17
to ISO C++ Standard - Discussion
Given a type with new-extended alignment, such as

struct alignas (8*alignof(double)) D { double d[8]; };

is it legal to construct an object of type D by placement-new’ing it into memory that is not over-aligned to alignof(D)?

char* buffer = new char[sizeof(D)];
D* d = new(buffer) D;

Further, is it correct that the compiler *cannot* in general assume that a pointer to an object of type D will point to a memory location over-aligned to alignof(D)?

Timur

Ville Voutilainen

unread,
Dec 2, 2017, 6:04:20 PM12/2/17
to std-dis...@isocpp.org
On 3 December 2017 at 00:45, Timur Doumler <goo...@timur.audio> wrote:
> Given a type with new-extended alignment, such as
>
> struct alignas (8*alignof(double)) D { double d[8]; };
>
> is it legal to construct an object of type D by placement-new’ing it into
> memory that is not over-aligned to alignof(D)?
>
> char* buffer = new char[sizeof(D)];
> D* d = new(buffer) D;

In the most general sense, the answer is "maybe". If there are no
observable side-effects,
and nothing ever cares whether a live object is there, the
initialization itself is fine. However, note
that you never began the object's lifetime. See [basic.life]/1:

"The lifetime of an object of type T begins when:
— storage with the proper alignment and size for type T is obtained, and
— if the object has non-vacuous initialization, its initialization is complete,
"

If the storage doesn't have the proper alignment, the lifetime of the
object never began.

> Further, is it correct that the compiler *cannot* in general assume that a
> pointer to an object of type D will point to a memory location over-aligned
> to alignof(D)?

In the sense that the pointer can be invalid or null, yes.

Richard Smith

unread,
Dec 2, 2017, 6:42:09 PM12/2/17
to std-dis...@isocpp.org
The new-expression *should* have undefined behavior if the pointer is not suitably aligned. Implementations are going to generate instructions that assume the specified alignment on targets that have them, sanitizers will fire, the bottom few bits of your pointer may not be retained or even part of the representation, and so on. But it's not clear to me that any wording normatively captures this. [expr.new]p17 has a note that describes the intent:

"When the allocation function returns a value other than null, it must be a pointer to a block of storage in which space for the object has been reserved. The block of storage is assumed to be appropriately aligned and of the requested size. The address of the created object will not necessarily be the same as that of the block if the object is an array."

... but nothing says what happens if this assumption is violated, and this wording is not normative.

You can /sort of/ piece together some kind of rule violation from this:

[basic.stc.dynamic] (6.6.4.4)/3: "Any allocation and/or deallocation functions defined in a C++ program, including the default versions in the library, shall conform to the semantics specified in 6.6.4.4.1 and 6.6.4.4.2."

[basic.stc.dynamic.allocation] (6.6.4.4.1)/2: "The pointer returned shall be suitably aligned so that it can be converted to a pointer to any suitable complete object type (21.6.2.1) and then used to access the object or array in the storage allocated"

[new.delete.single] (21.6.2.1)/1: "The [form with an align_val_t parameter] is called for a type with new-extended alignment, and allocates storage with the specified alignment."

... and you could probably try to argue from this that any 'operator new' that returns a pointer misaligned for the requested allocation is UB, but it's far from clear that this is the right interpretation. And also it's far from clear that the "must provide storage with the specified alignment" rule would even apply if an align_val_t overload is not selected. (This wording predates the strong distinction between "shall" (meaning "ill-formed with a diagnostic if not") and phrases that result in undefined behavior, so presumably UB is the result if these rules are violated, but that's also not clear.)


--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+unsubscribe@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.

Timur Doumler

unread,
Dec 3, 2017, 9:08:46 AM12/3/17
to std-dis...@isocpp.org
Thanks, that’s what I have suspected as well. There’s another (non-normative) note that suggests an object with new-extended alignment cannot live in memory not such over-aligned, and a pointer to an object of that type in such a memory location would automatically have an invalid value:

[basic.compound]/3.4: [ Note: Pointers to over-aligned types (6.6.5) have no special representation, but their range of valid values is restricted by the extended alignment requirement. — end note ]

I think this is remarkable, because it means that before the introduction of dynamic allocation of over-aligned types in C++17, any attempt to dynamically allocate an over-aligned object with standard operator new and then using that object would instantly drop you in undefined behaviour land! It is surprising to me that the paper that introduced operator new with align_val_t (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0035r4.html) does not state this fact anywhere in its motivation, and seems to suggest that the old behaviour was, in fact, defined (just not ideal) behaviour. Maybe I am still missing something?

Timur

--

---
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-discussion/9sUeEHqPZ00/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-discussio...@isocpp.org.

Nicol Bolas

unread,
Dec 3, 2017, 10:01:02 AM12/3/17
to ISO C++ Standard - Discussion, ma...@timur.audio
On Sunday, December 3, 2017 at 9:08:46 AM UTC-5, Timur Doumler wrote:
Thanks, that’s what I have suspected as well. There’s another (non-normative) note that suggests an object with new-extended alignment cannot live in memory not such over-aligned, and a pointer to an object of that type in such a memory location would automatically have an invalid value:

[basic.compound]/3.4: [ Note: Pointers to over-aligned types (6.6.5) have no special representation, but their range of valid values is restricted by the extended alignment requirement. — end note ]

I think this is remarkable, because it means that before the introduction of dynamic allocation of over-aligned types in C++17, any attempt to dynamically allocate an over-aligned object with standard operator new and then using that object would instantly drop you in undefined behaviour land! It is surprising to me that the paper that introduced operator new with align_val_t (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0035r4.html) does not state this fact anywhere in its motivation, and seems to suggest that the old behaviour was, in fact, defined (just not ideal) behaviour. Maybe I am still missing something?

I'm not sure how you're reading that from the paper. It's right there in the "Problem" section:

> In this example, not only is an implementation of C++ not required to allocate properly-aligned memory for the array, for practical purposes it is very nearly required to do the allocation incorrectly. In any event, it is certainly required to perform the allocation by a process that does not take the specified alignment value into account.

The fact that the last part leads to UB is probably not something the writer felt the need to explicitly spell out. If it didn't, why would the alignment matter?

Timur Doumler

unread,
Dec 3, 2017, 10:18:29 AM12/3/17
to Nicol Bolas, ISO C++ Standard - Discussion
Thanks, that makes sense!

In any case, my original question has been answered.

When dereferencing a pointer to an object of type T declared with new-extended alignment, the compiler can always assume that its value will be a memory address over-aligned with alignof(T), as otherwise dereferencing such a pointer would constitute UB.
Reply all
Reply to author
Forward
0 new messages