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 byb
a.push_back(move(*b)); // UB: rvalue reference aliasesc
: not unique in whole-program scope assert(c == b); // FAILS: if rvalue reference to*b
is unique,b
is unique, andc == 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
.
restrict
. This allows aliasing optimizations, for example:
void std_api(int&& a, int&& b); // allowed to assume thata
andb
do not alias int a, b, c; std_api(move(a), move(b)); // OK: two unique references instd_api
std_api(move(c), move(c)); // UB:a
andb
alias
See llvm Bug 48238 for a bug tracking the implementation of these optimizations in clang.
This also allows optimizingvector::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.
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) — 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.
(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.
(1.3) — If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to
[Example ?:this argumentthe value within the function's scope and may move from it.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
andint&& b
do not alias std_api(move(c), move(c)); // UB:int&& a
andint&& 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 formT&&
and an lvalue of typeA
is bound, the argument binds to an lvalue reference (13.10.3.2 [temp.deduct.call]) and thus is not covered bythe previous sentencethis 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 argumentstd::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]