1195. "Diagnostic required" wording is insufficient to prevent UB

Section: 16 [library] Status: C++11 Submitter: Daniel Krügler Opened: 2009-08-18 Last modified: 2016-01-28

Priority: Not Prioritized

View other active issues in [library].

View all other issues in [library].

View all issues with C++11 status.

Discussion:

Several parts of the library use the notion of "Diagnostic required" to indicate that in the corresponding situation an error diagnostic should occur, e.g. 20.3.1.2.2 [unique.ptr.dltr.dflt]/2

void operator()(T *ptr) const;

Effects: calls delete on ptr. A diagnostic is required if T is an incomplete type.

The problem with this approach is that such a requirement is insufficient to prevent undefined behavior, if this situation occurs. According to 3.17 [defns.diagnostic] a diagnostic message is defined as

a message belonging to an implementation-defined subset of the implementation's output messages.

which doesn't indicate any relation to an ill-formed program. In fact, "compiler warnings" are a typical expression of such diagnostics. This means that above wording can be interpreted by compiler writers that they satisfy the requirements of the standard if they just produce such a "warning", if the compiler happens to compile code like this:

#include <memory>

struct Ukn; // defined somewhere else
Ukn* create_ukn(); // defined somewhere else

int main() {
 std::default_delete<Ukn>()(create_ukn());
}

In this and other examples discussed here it was the authors intent to guarantee that the program is ill-formed with a required diagnostic, therefore such wording should be used instead. According to the general rules outlined in 4.1 [intro.compliance] it should be sufficient to require that these situations produce an ill-formed program and the "diagnostic required" part should be implied. The proposed resolution also suggests to remove several redundant wording of "Diagnostics required" to ensure that the absence of such saying does not cause a misleading interpretation.

[ 2009 Santa Cruz: ]

Move to NAD.

It's not clear that there's any important difference between "ill-formed" and "diagnostic required". From 4.1 [intro.compliance], 3.25 [defns.ill.formed], and 3.66 [defns.well.formed] it appears that an ill-formed program is one that is not correctly constructed according to the syntax rules and diagnosable semantic rules, which means that... "a conforming implementation shall issue at least one diagnostic message." The author's intent seems to be that we should be requiring a fatal error instead of a mere warning, but the standard just doesn't have language to express that distinction. The strongest thing we can ever require is a "diagnostic".

The proposed rewording may be a clearer way of expressing the same thing that the WP already says, but such a rewording is editorial.

[ 2009 Santa Cruz: ]

Considered again. Group disagrees that the change is technical, but likes it editorially. Moved to NAD Editorial.

[ 2009-11-19: Moved from NAD Editorial to Open. Please see the thread starting with Message c++std-lib-25916. ]

[ 2009-11-20 Daniel updated wording. ]

The following resolution differs from the previous one by avoiding the unusual and misleading term "shall be ill-formed", which does also not follow the core language style. This resolution has the advantage of a minimum impact on the current wording, but I would like to mention that a more intrusive solution might be preferrable - at least as a long-term solution: Jens Maurer suggested the following approach to get rid of the usage of the term "ill-formed" from the library by introducing a new category to existing elements to the list of 16.3.2.4 [structure.specifications]/3, e.g. "type requirements" or "static constraints" that define conditions that can be checked during compile-time and any violation would make the program ill-formed. As an example, the currently existing phrase 22.4.7 [tuple.helper]/1

Requires: I < sizeof...(Types). The program is ill-formed if I is out of bounds.

could then be written as

Static constraints: I < sizeof...(Types).

[ 2009-11-21 Daniel updated wording. ]

[ 2009-11-22 Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]

Proposed resolution:

  1. Change 21.4 [ratio]/2 as indicated:

    Throughout this subclause, if the template argument types R1 and R2 shall be are not specializations of the ratio template, the program is ill-formed. Diagnostic required.

  2. Change 21.4.3 [ratio.ratio]/1 as indicated:

    If tThe template argument D shall not be is zero, and or the absolute values of the template arguments N and D shall be are not representable by type intmax_t, the program is ill-formed. Diagnostic required. [..]

  3. Change 21.4.4 [ratio.arithmetic]/1 as indicated:

    Implementations may use other algorithms to compute these values. If overflow occurs, the program is ill-formed a diagnostic shall be issued.

  4. Change 21.4.5 [ratio.comparison]/2 as indicated:

    [...] Implementations may use other algorithms to compute this relationship to avoid overflow. If overflow occurs, the program is ill-formed a diagnostic is required.

  5. Change 20.3.1.2.2 [unique.ptr.dltr.dflt]/2 as indicated:

    Effects: calls delete on ptr. A diagnostic is required if T is an incomplete type.

    Remarks: If T is an incomplete type, the program is ill-formed.

  6. Change 20.3.1.2.3 [unique.ptr.dltr.dflt1]/1 as indicated:

    void operator()(T* ptr) const;
    

    Effects: operator() calls delete[] on ptr. A diagnostic is required if T is an incomplete type.

    Remarks: If T is an incomplete type, the program is ill-formed.

  7. Change 20.3.1.3.2 [unique.ptr.single.ctor] as indicated: [Note: This editorially improves the currently suggested wording of 932 by replacing

    "shall be ill-formed" by "is ill-formed"]

    [If N3025 is accepted this bullet is applied identically in that paper as well.]

    -1- Requires: D shall be default constructible, and that construction shall not throw an exception. D shall not be a reference type or pointer type (diagnostic required).

    ...

    Remarks: If this constructor is instantiated with a pointer type or reference type for the template argument D, the program is ill-formed.

  8. Change 20.3.1.3.2 [unique.ptr.single.ctor]/8 as indicated: [Note: This editorially improves the currently suggested wording of 932 by replacing

    "shall be ill-formed" by "is ill-formed"]

    [If N3025 is accepted this bullet is applied identically in that paper as well.]

    unique_ptr(pointer p);
    

    ...

    Remarks: If this constructor is instantiated with a pointer type or reference type for the template argument D, the program is ill-formed.

  9. Change 20.3.1.3.2 [unique.ptr.single.ctor]/13 as indicated:

    [..] If d is an rvalue, it will bind to the second constructor of this pair and the program is ill-formed. That constructor shall emit a diagnostic. [Note: The diagnostic could be implemented using a static_assert which assures that D is not a reference type. — end note] Else d is an lvalue and will bind to the first constructor of this pair. [..]

  10. 20.3.1.3.2 [unique.ptr.single.ctor]/20: Solved by 950.
  11. Change 20.3.1.4 [unique.ptr.runtime]/1 as indicated:

    A specialization for array types is provided with a slightly altered interface.

    • Conversions among different types of unique_ptr<T[], D> or to or from the non-array forms of unique_ptr are disallowed (diagnostic required) produce an ill-formed program.
    • ...
  12. Change 30.5 [time.duration]/2-4 as indicated:

    2 Requires: Rep shall be an arithmetic type or a class emulating an arithmetic type. If a program instantiates duration with a duration type for the template argument Rep a diagnostic is required.

    3 Remarks: If duration is instantiated with a duration type for the template argument Rep, the program is ill-formed.

    3 4 Requires Remarks: If Period shall be is not a specialization of ratio, diagnostic required the program is ill-formed.

    4 5 Requires Remarks: If Period::num shall be is not positive, diagnostic required the program is ill-formed.

  13. 30.5.2 [time.duration.cons]/1+4: Apply 1177
  14. 30.5.6 [time.duration.nonmember]/4+6+8+11: Apply 1177
  15. 30.5.8 [time.duration.cast]/1: Apply 1177
  16. Change 30.6 [time.point]/2 as indicated:

    If Duration shall be is not an instance of duration, the program is ill-formed. Diagnostic required.

  17. 30.6.2 [time.point.cons]/3: Apply 1177
  18. 30.6.8 [time.point.cast]/1: Apply 1177