This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

3193. Mandates: and Expects: elements are not defined for types

Section: 16.3.2.4 [structure.specifications] Status: New Submitter: Daniel Krügler Opened: 2019-03-04 Last modified: 2020-06-11

Priority: 3

View other active issues in [structure.specifications].

View all other issues in [structure.specifications].

View all issues with New status.

Discussion:

The working paper uses the special elements Mandates:, Expects: as well as Requires: to types, albeit 16.3.2.4 [structure.specifications] defines them only for functions, for example 16.3.2.4 [structure.specifications] sub-bullet (3.4):

Expects: the conditions (sometimes termed preconditions) that the function assumes to hold whenever it is called.

Examples for such usages on types are (from N4800):

Instead of replacing these elements usages for these places by extra wording to reach the same effects I recommend to update instead 16.3.2.4 [structure.specifications] to ensure that requirement-expressing elements are defined in a way that it also allows to express requirements imposed on types by these elements to standardize "existing practice".

Considering details, it seems obvious that Mandates:, Expects: as well as Requires: are "suitable" to be defined for types (With the acceptance of P1463R1 there are now also Mandates: for types such as Table 65 — "Allocator-aware container requirements" for type allocator_type).

For Constraints: the meaning would not be so clear: Should it mean that there is conditionally a type defined or not? According to the submitters knowledge there are currently no known examples for Constraints: to specify constraint on types, therefore I'm suggesting to restrict this extension to Mandates:, Expects:, and Requires: alone.

[2019-03-15 Priority set to 3 after reflector discussion]

[2019-03-15; Daniel comments and provides wording]

During the preparation of the wording for this issue it was found that we should allow Remarks: elements to be used for other things than functions. One example of imposed restrictions can be found in 17.11.3 [cmp.common]:

template<class... Ts>
struct common_comparison_category {
  using type = see below;
};

-2- Remarks: The member typedef-name type denotes the common comparison type (11.10.3 [class.spaceship]) of Ts..., the expanded parameter pack. […]

The discussion of this issue speaks of "type" restrictions (versus the specified restrictions on functions), because even the non-type template argument restrictions of 22.3.4 [pair.astuple] appear in the context of a member type specification, but there are examples where not really a single (member) type is involved, e.g. in the 22.4.7 [tuple.helper] example mentioned above.

Another example is when such elements are used for the specification of template specializations, e.g. in 22.4.7 [tuple.helper]:

template<class T> struct tuple_size;

-1- Remarks: All specializations of tuple_size shall satisfy the Cpp17UnaryTypeTrait requirements (21.3.2 [meta.rqmts]) with a base characteristic of integral_constant<size_t, N> for some N.

Besides class template specializations, a second relevant use-case is the specification of member types (Which are not necessarily part of a template), typically within the requirement tables, e.g. in Table 62 — "Container requirements"'s entry X::value_type:

Requires: T is Cpp17Erasable from X

The suggested wording tries to cover the generalization by means of the term "non-function entities" in addition to the existing functions to prevent being enforced to enumerate all entities to which the extended rules apply.

Previous resolution [SUPERSEDED]:

This wording is relative to N4810.

  1. Change 16.3.2.4 [structure.specifications], as indicated:

    -3- Descriptions of function semantics contain the following elements (as appropriate); some of these elements may also appear in the description of non-function entities as denoted below: (footnote […])

    1. (3.1) — Requires: the preconditions imposed on a non-function entity, or for calling the function.

    2. (3.2) — Constraints: […]

    3. (3.3) — Mandates: the conditions that, if not met, render the program ill-formed. [Example: An implementation might express such a condition via the constant-expression in a static_assert-declaration (Clause 9). If the diagnostic is to be emitted only after the function has been selected by overload resolution, an implementation might express such a condition via a constraint-expression (13.5.3 [temp.constr.decl]) and also define the function as deleted. — end example]

    4. (3.4) — Expects: the conditions (sometimes termed preconditions) imposed on a non-function entity, or that the function assumes to hold whenever it is called. [Example: An implementation might express such conditions via an attribute such as [[expects]] ( [dcl.attr.contract]) on a function declaration. However, some such conditions might not lend themselves to expression via code. — end example]

    5. […]

    6. (3.11) — Remarks: additional semantic constraints on the function.

    7. […]

  2. Change 99 [res.on.required], as indicated:

    -1- Violation of any preconditions specified in a function's Requires: element results in undefined behavior unless the function's Throws: element specifies throwing an exception when thea function's precondition is violated.

    -2- Violation of any preconditions specified in an function's Expects: element results in undefined behavior.

[2020-05-01; Daniel comments and adjusts wording to recent working draft]

It should be pointed out that the originally referred to Expects: element has since then be renamed to Preconditions: and that the Requires: element does now only occur in annex D.

[2020-06-11; Jonathan comments]

This issue also affects some type traits such as alignment_of and make_signed/make_unsigned. In addition to clarifying what Mandates: means on a non-function we need to decide exactly what is being mandated in the type traits. Is instantiating the class template ill-formed, or just odr-using the nested type or value member?

Proposed resolution:

This wording is relative to N4861.

  1. Change 16.3.2.4 [structure.specifications], as indicated:

    -3- Descriptions of function semantics contain the following elements (as appropriate); some of these elements may also appear in the description of non-function entities as denoted below: (footnote […])

    1. (3.1) — Constraints: […]

    2. (3.2) — Mandates: the conditions that, if not met, render the program ill-formed. [Example: An implementation might express such a condition via the constant-expression in a static_assert-declaration (9.1 [dcl.pre]). If the diagnostic is to be emitted only after the function has been selected by overload resolution, an implementation might express such a condition via a constraint-expression (13.5.3 [temp.constr.decl]) and also define the function as deleted. — end example]

    3. (3.3) — Preconditions: the conditions imposed on a non-function entity, or that the function assumes to hold whenever it is called.

    4. […]

    5. (3.10) — Remarks: additional semantic constraints on the function.

    6. […]

  2. Change [res.on.expects], as indicated:

    -1- Violation of any preconditions specified in a function's Preconditions: element results in undefined behavior.

  3. Change D.11 [depr.res.on.required], as indicated:

    [Drafting note: Interestingly, albey the Requires: element has nearly vanished, the issue is still relevant, see D.19 [depr.meta.types]]

    -1- Violation of any preconditions specified in a function's Requires: element results in undefined behavior unless the function's Throws: element specifies throwing an exception when thea function's precondition is violated.