2939. Some type-completeness constraints of traits are overspecified

Section: 21.3.3 [meta.type.synop] Status: Open Submitter: Daniel Krügler Opened: 2017-03-02 Last modified: 2024-08-21

Priority: 2

View other active issues in [meta.type.synop].

View all other issues in [meta.type.synop].

View all issues with Open status.

Discussion:

LWG 2797 (RU 2) suggests that certain type-traits should be required to diagnose violations of their pre-conditions. The basic idea is founded and I see no problems for requiring this for the mentioned traits alignment_of or is_base_of, for example. But if we want to require this diagnostics for some other traits, such as is_convertible, is_constructible (and friends), or is_callable (and possibly some others), we really should be sure that our current requirements are OK.

Unfortunately, there exists some cases, where we currently overspecify imposing complete type requirements where they are not actually required. For example, for the following situation the answer of the trait could be given without ever needing the complete type of X:

struct X; // Never defined

static_assert(std::is_convertible_v<X, const X&>);

Unfortunately we cannot always allow incomplete types, because most type constructions or conversions indeed require a complete type, so generally relaxing the current restrictions is also not an option.

The core language has a solution for this "small" gap of situations, where the response of the compiler might depend on type completeness: Undefined behaviour. So, I believe we need a somewhat more detailled form to express the intend here. Informally, I would suggest that the program should only be ill-formed in the situation described by LWG 2797, if there exists the possibility that the compiler would require complete types for the considered operation. The example shown above, std::is_convertible_v<X, const X&>, would never require the need to complete X, so here no violation should exist.

The presented example might seem a tiny one, but the Standard Library type traits are extreme fundamental tools and we should try to not give the impression that an approximate rule of the current type constraints breaks reasonable code.

It is correct, that above example has currently undefined behaviour due to the breakage of pre-conditions, therefore this issue suggests to fix the current situation before enforcing a diagnostic for such valid situations.

[2017-03-04, Kona]

Set priority to 2. Is related to 2797, but really needs an audit of the type traits.

[2018-08 Batavia Monday issue discussion]

Issues 2797, 2939, 3022, and 3099 are all closely related. Walter to write a paper resolving them.

[2020-02 Prague Thursday issue discussion]

Two of the issues (2797 and 3022) had been resolved by the acceptance of P1285R0.

[2024-05-09; Jonathan provides wording]

We could also relax the type completeness requirements for reference_converts_from_temporary and reference_constructs_from_temporary, as the result is always false if the first type is a non-reference, so we don't need complete types in that case. This doesn't seem important to support, but if we wanted to then we could say:

Either T is not a reference type, or T and U shall be a complete type complete types, cv void, or an arrayarrays of unknown bound.

Previous resolution [SUPERSEDED]:

This wording is relative to N4981.

  1. In 21.3.5.4 [meta.unary.prop] Table 51, change the Preconditions text for is_constructible, is_trivially_constructible, is_nothrow_constructible, is_convertible, and is_nothrow_convertible, as indicated.
    TemplateConditionPreconditions
    template<class T, class... Args>
    struct is_constructible;
    For a function type T or for a cv void type T, is_constructible_v<T, Args...> is false, otherwise see below . Either T is a reference type and Args contains a single type that is similar (7.3.6 [conv.qual]) to remove_reference_t<T>, or T and all types in the template parameter pack Args shall be complete types, cv void, or arrays of unknown bound.
    template<class T, class... Args>
    struct is_trivially_constructible;
    is_constructible_v<T, Args...> is true and the variable definition for is_constructible, as defined below, is known to call no operation that is not trivial (6.8.1 [basic.types.general], 11.4.4 [special]). Either T is a reference type and Args contains a single type that is similar (7.3.6 [conv.qual]) to remove_reference_t<T>, or T and all types in the template parameter pack Args shall be complete types, cv void, or arrays of unknown bound.
    template<class T, class... Args>
    struct is_nothrow_constructible;
    is_constructible_v<T, Args...> is true and the variable definition for is_constructible, as defined below, is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]). Either T is a reference type and Args contains a single type that is similar (7.3.6 [conv.qual]) to remove_reference_t<T>, or T and all types in the template parameter pack Args shall be complete types, cv void, or arrays of unknown bound.
  2. In 21.3.7 [meta.rel] Table 53, change the Comments text for is_convertible and is_nothrow_convertible as indicated.
    TemplateConditionComments
    template<class From, class To>
    struct is_convertible;
    see below Either To is a reference type and From is similar (7.3.6 [conv.qual]) to remove_reference_t<To>, or From and To shall be complete types, cv void, or arrays of unknown bound.
    template<class From, class To>
    struct is_nothrow_convertible;
    is_convertible_v<From, To> is true and the conversion, as defined by is_convertible, is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]). Either To is a reference type and From is similar (7.3.6 [conv.qual]) to remove_reference_t<To>, or From and To shall be complete types, cv void, or arrays of unknown bound.

[2024-08-21; Jonathan provides improved wording]

Following on LWG telecon review, change "Args contains a single type that is similar" to "Args contains a single type and that type is similar".

Proposed resolution:

This wording is relative to N4988.

  1. In 21.3.5.4 [meta.unary.prop] Table 51, change the Preconditions text for is_constructible, is_trivially_constructible, is_nothrow_constructible, is_convertible, and is_nothrow_convertible, as indicated.
    TemplateConditionPreconditions
    template<class T, class... Args>
    struct is_constructible;
    For a function type T or for a cv void type T, is_constructible_v<T, Args...> is false, otherwise see below . Either T is a reference type and Args contains a single type and that type is similar (7.3.6 [conv.qual]) to remove_reference_t<T>, or T and all types in the template parameter pack Args shall be complete types, cv void, or arrays of unknown bound.
    template<class T, class... Args>
    struct is_trivially_constructible;
    is_constructible_v<T, Args...> is true and the variable definition for is_constructible, as defined below, is known to call no operation that is not trivial (6.8.1 [basic.types.general], 11.4.4 [special]). Either T is a reference type and Args contains a single type and that type is similar (7.3.6 [conv.qual]) to remove_reference_t<T>, or T and all types in the template parameter pack Args shall be complete types, cv void, or arrays of unknown bound.
    template<class T, class... Args>
    struct is_nothrow_constructible;
    is_constructible_v<T, Args...> is true and the variable definition for is_constructible, as defined below, is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]). Either T is a reference type and Args contains a single type and that type is similar (7.3.6 [conv.qual]) to remove_reference_t<T>, or T and all types in the template parameter pack Args shall be complete types, cv void, or arrays of unknown bound.
  2. In 21.3.7 [meta.rel] Table 53, change the Comments text for is_convertible and is_nothrow_convertible as indicated.
    TemplateConditionComments
    template<class From, class To>
    struct is_convertible;
    see below Either To is a reference type and From is similar (7.3.6 [conv.qual]) to remove_reference_t<To>, or From and To shall be complete types, cv void, or arrays of unknown bound.
    template<class From, class To>
    struct is_nothrow_convertible;
    is_convertible_v<From, To> is true and the conversion, as defined by is_convertible, is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]). Either To is a reference type and From is similar (7.3.6 [conv.qual]) to remove_reference_t<To>, or From and To shall be complete types, cv void, or arrays of unknown bound.