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.

### 3511. Clarify global permission to move

Section: 16.4.5.9 [res.on.arguments] Status: New Submitter: Gonzalo Brito Gadeschi Opened: 2020-12-08 Last modified: 2021-01-15

Priority: 3

View all other issues in [res.on.arguments].

View all issues with New status.

Discussion:

The intent of LWG 1204 is to allow standard library APIs accepting rvalue arguments:

- to move from their arguments, e.g., without having to specify that they might do this as part of their Effects clause, and

- to assume that rvalue arguments do not alias any pointer in the scope of the standard library API, e.g., to allow `vector`'s `push_back(T&& t)` to assume that `t` is not an element of the `vector`.

The current wording in 16.4.5.9 [res.on.arguments]/1.3 states:

> If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argument.

This sentence is not clear about the scope in which the reference can be assumed to be unique, and it does not explicitly state that the function can modify the argument, e.g., to move from it.

If the scope of the "unique reference" is "whole program scope", this example:

>     void example(vector<int>& a, int* b)
>     {
>       int* c = b;            // reference to object pointed at by b
>       a.push_back(move(*b)); // UB: rvalue reference aliases c: not unique in whole-program scope
>       assert(c == b);        // FAILS: if rvalue reference to *b is unique, b is unique, and c == b is false
>     }

exhibits UB because the implementation may assume that the reference to `b` is unique, which does not hold since `c` is also a reference to `b`.

If the scope of the "unique reference" is the "function scope" of the standard library API, then the semantics of the rvalue reference argument are very similar to those of C's `restrict`. This allows aliasing optimizations, for example:

>     void std_api(int&& a, int&& b); // allowed to assume that a and b do not alias
>     int a, b, c;
>     std_api(move(a), move(b)); // OK: two unique references in std_api
>     std_api(move(c), move(c)); // UB: a and b alias

See llvm Bug 48238 for a bug tracking the implementation of these optimizations in clang.

This also allows optimizing `vector::push_back(T&& t)` since if `t` does not alias any pointer in `vector::push_back`'s scope, it also does not alias `this`, `this->data()`, `(*this)[0]`, etc.

[2021-01-15; Telecon prioritization]

Set priority to 3 following reflector and telecon discussions.

Proposed resolution:

This wording is relative to N4868.

1. Modify 16.4.5.9 [res.on.arguments] as indicated:

   > -1- Each of the following applies to all arguments to functions defined in the C++ standard library, unless explicitly stated otherwise.

   > 1. (1.1) — If an argument to a function has an invalid value (such as a value outside the domain of the function or a pointer invalid for its intended use), the behavior is undefined.

   > 2. (1.2) — If a function argument is described as being an array, the pointer actually passed to the function shall have a value such that all address computations and accesses to objects (that would be valid if the pointer did point to the first element of such an array) are in fact valid.

   > 3. (1.3) — If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argument the value within the function's scope and may move from it.

   >    [Example ?:

   >    >     void std_api(int&& a);
   >    >     int a;
   >    >     std_api(move(a));
   >    >     // a is in an unspecified but valid state

   >    — end example]

   >    [Example ?:

   >    >     void std_api(int&& a, int&& b);
   >    >     int a, b, c;
   >    >     std_api(move(a), move(b)); // OK: int&& a and int&& b do not alias
   >    >     std_api(move(c), move(c)); // UB: int&& a and int&& b alias

   >    — end example]

   >    [Example ?:

   >    >     std::vector<int> a = {...};
   >    >     a.push_back(move(42)); // OK: unique reference
   >    >     a.push_back(move(a[0])); // UB: (*this)[0] and rvalue argument alias

   >    — end example]

   >    [Note 1: If the parameter is a generic parameter of the form `T&&` and an lvalue of type `A` is bound, the argument binds to an lvalue reference (13.10.3.2 [temp.deduct.call]) and thus is not covered by the previous sentence this item. — end note] [Note 2: If a program casts an lvalue to an xvalue while passing that lvalue to a library function (e.g., by calling the function with the argument `std::move(x))`, the program is effectively asking that function to treat that lvalue as a temporary object. The implementation is free to optimize away aliasing checks which might be needed if the argument was an lvalue. — end note]