Title
[[no_unique_address] and common initial sequence
Status
drwp
Section
11.4.1 [class.mem.general]
Submitter
Richard Smith

Created on 2020-11-10.00:00:00 last changed 3 weeks ago

Messages

Date: 2023-08-26.21:49:54

Proposed resolution (approved by CWG 2023-08-25):

Change in 11.4.1 [class.mem.general] paragraph 23 as follows:

The common initial sequence of two standard-layout struct (11.2 [class.prop]) types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that
  • corresponding entities have layout-compatible types (6.8 [basic.types]),
  • corresponding entities have the same alignment requirements (6.7.6 [basic.align]),
  • either both entities are declared with the no_unique_address attribute (9.12.11 [dcl.attr.nouniqueaddr]) or neither is, if a has-attribute-expression (15.2 [cpp.cond]) is not 0 for the no_unique_address attribute, then neither entity is declared with the no_unique_address attribute (9.12.11 [dcl.attr.nouniqueaddr]), and
  • either both entities are bit-fields with the same width or neither is a bit-field.
Date: 2023-11-15.00:00:00

[Accepted as a DR at the November, 2023 meeting.]

The interaction of [[no_unique_address]] and the definition of common initial sequence is still problematic. Subclause 11.4.1 [class.mem.general] bullet 23.3 specifies that corresponding members in a common initial sequence are not allowed to differ with respect to the presence or absence of a [[no_unique_address]] attribute. However, the Itanium ABI will not allocate two successive data members of the same empty class type at the same address, causing non-conforming behavior for the following example:

  struct A {};
  struct B {};

  struct C {
   [[no_unique_address]] A a;
   [[no_unique_address]] B b;
  };

  struct D {
   [[no_unique_address]] A a1;
   [[no_unique_address]] A a2;
  };

  static_assert(offsetof(C, b) == offsetof(D, a2));

See Itanium ABI issue 108.

Since "common initial sequence" and "layout compatible" are concepts mostly used for C compatibility, but [[no_unique_address]] does not exist in C, it seems reasonable to terminate a common initial sequence at the first data member that is declared [[no_unique_address]].

Another concern is the behavior of std::is_layout_compatible on implementations that ignore [[no_unique_address]]. On such an implementation, the following example would be considered layout-compatible, although it actually is not:

  struct E {};

  struct A {
    E e;
    int i;
  };

  struct B {
    [[no_unique_address]] E e;
    int i;
  };

  static_assert(
    std::is_layout_compatible_v<A, B>
  );

Alternative possible resolution [SUPERSEDED]:

Change in 11.4.1 [class.mem.general] paragraph 23 as follows:

The common initial sequence of two standard-layout struct (11.2 [class.prop]) types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that
  • corresponding entities have layout-compatible types (6.8 [basic.types]),
  • corresponding entities have the same alignment requirements (6.7.6 [basic.align]),
  • either both entities are declared with the no_unique_address attribute (9.12.11 [dcl.attr.nouniqueaddr]) or neither is, neither entity is declared with the no_unique_address attribute (9.12.11 [dcl.attr.nouniqueaddr]), and
  • either both entities are bit-fields with the same width or neither is a bit-field.
History
Date User Action Args
2024-04-05 21:43:46adminsetstatus: dr -> drwp
2023-12-19 10:15:28adminsetstatus: ready -> dr
2023-11-10 14:27:11adminsetstatus: tentatively ready -> ready
2023-08-26 21:49:54adminsetstatus: open -> tentatively ready
2023-07-10 21:53:51adminsetmessages: + msg7355
2020-11-10 00:00:00admincreate