3588. Strike out purposeless UB involving the deleter in members functions of unique_ptr

Section: 20.3.1.3.1 [unique.ptr.single.general] Status: NAD Submitter: Johel Ernesto Guerrero Peña Opened: 2021-09-07 Last modified: 2021-09-20

Priority: Not Prioritized

View all other issues in [unique.ptr.single.general].

View all issues with NAD status.

Discussion:

This originated from the editorial issues #4871 and #4872.

Several member functions of unique_ptr are noexcept, and yet, they have the precondition that an expression involving the deleter does not exit via an exception. There's nothing an implementation or user can take advantage of in presence of this UB. Since the behavior otherwise would be a call to std::terminate, these preconditions should be striked out.

Note that although ~unique_ptr() is not noexcept, 16.4.6.13 [res.on.exception.handling] p3 specifies it to behave as if it were.

[2021-09-20 Status changed: New → NAD.]

The current specification allows the compiler to omit noexcept-enforcement.

Proposed resolution:

This wording is relative to N4892.

  1. Modify 20.3.1.3.2 [unique.ptr.single.ctor] as indicated:

    constexpr unique_ptr() noexcept;
    constexpr unique_ptr(nullptr_t) noexcept;
    

    -1- […]

    -2- Preconditions: D meets the Cpp17DefaultConstructible requirements (Table 27), and that construction does not throw an exception.

    […]

    explicit unique_ptr(pointer p) noexcept;
    

    -5- Constraints: […]

    -6- Mandates: […]

    -7- Preconditions: D meets the Cpp17DefaultConstructible requirements (Table 27), and that construction does not throw an exception.

    […]

    unique_ptr(pointer p, const D& d) noexcept;
    unique_ptr(pointer p, remove_reference_t<D>&& d) noexcept;
    

    -10- Constraints: […]

    -11- Mandates: […]

    -12- Preconditions: For the first constructor, if D is not a reference type, D meets the Cpp17CopyConstructible requirements and such construction does not exit via an exception. For the second constructor, if D is not a reference type, D meets the Cpp17MoveConstructible requirements and such construction does not exit via an exception.

    […]

    unique_ptr(unique_ptr&& u) noexcept;
    

    -17- Constraints: […]

    -18- Preconditions: If D is not a reference type, D meets the Cpp17MoveConstructible requirements (Table 28). Construction of the deleter from an rvalue of type D does not throw an exception.

    […]

    template<class U, class E> unique_ptr(unique_ptr<U, E>&& u) noexcept;
    

    -21- Constraints: […]

    -22- Preconditions: If E is not a reference type, construction of the deleter from an rvalue of type E is well-formed and does not throw an exception. Otherwise, E is a reference type and construction of the deleter from an lvalue of type E is well-formed and does not throw an exception.

    […]

  2. Modify 20.3.1.3.3 [unique.ptr.single.dtor] as indicated:

    ~unique_ptr();
    

    -1- Preconditions: The expression get_deleter()(get()) is well-formed, and has well-defined behavior, and does not throw exceptions.

    […]

  3. Modify 20.3.1.3.4 [unique.ptr.single.asgn] as indicated:

    unique_ptr& operator=(unique_ptr&& u) noexcept;
    

    -1- Constraints: […]

    -2- Preconditions: If D is not a reference type, D meets the Cpp17MoveAssignable requirements (Table 30) and assignment of the deleter from an rvalue of type D does not throw an exception. Otherwise, D is a reference type; remove_reference_t<D> meets the Cpp17CopyAssignable requirements and assignment of the deleter from an lvalue of type D does not throw an exception.

    […]

    template<class U, class E> unique_ptr& operator=(unique_ptr<U, E>&& u) noexcept;
    

    -6- Constraints: […]

    -7- Preconditions: If E is not a reference type, assignment of the deleter from an rvalue of type E is well-formed and does not throw an exception. Otherwise, E is a reference type and assignment of the deleter from an lvalue of type E is well-formed and does not throw an exception.

    […]

  4. Modify 20.3.1.3.6 [unique.ptr.single.modifiers] as indicated:

    void reset(pointer p = pointer()) noexcept;
    

    -3- Preconditions: The expression get_deleter()(get()) is well-formed, and has well-defined behavior, and does not throw exceptions.

    […]

    void swap(unique_ptr& u) noexcept;
    

    -6- Preconditions: get_deleter() is swappable (16.4.4.3 [swappable.requirements]) and does not throw an exception under swap.

    […]