Skip to content

Commit 6252da2

Browse files
committed
New issue from Giuseppe D'Angelo: "std::trivially_relocate needs stronger preconditions on "nested" objects with dynamic lifetime"
1 parent b00277c commit 6252da2

File tree

1 file changed

+151
-0
lines changed

1 file changed

+151
-0
lines changed

xml/issue4283.xml

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<?xml version='1.0' encoding='utf-8' standalone='no'?>
2+
<!DOCTYPE issue SYSTEM "lwg-issue.dtd">
3+
4+
<issue num="4283" status="New">
5+
<title>`std::trivially_relocate` needs stronger preconditions on "nested" objects with dynamic lifetime</title>
6+
<section>
7+
<sref ref="[obj.lifetime]"/>
8+
</section>
9+
<submitter>Giuseppe D'Angelo</submitter>
10+
<date>23 Jun 2025</date>
11+
<priority>99</priority>
12+
13+
<discussion>
14+
<p>
15+
In <sref ref="[obj.lifetime]"/> the `std::trivially_relocate` function
16+
is missing a precondition, that is, that any object alive in the range being
17+
relocated is itself trivially relocatable.
18+
<p/>
19+
We know the objects in the range are trivially relocatable, because
20+
there is a <i>Mandates</i>: element for this. The current draft has precise
21+
rules to determine whether a type is trivially relocatable or not; in
22+
general, subobjects are considered there (cf. <sref ref="[class.prop]"/>,
23+
"eligible for trivial relocation", which discusses base classes and non-static
24+
data members).
25+
<p/>
26+
However these rules do not take into account objects with dynamic
27+
lifetime whose storage is being provided by (sub)objects in the range.
28+
<p/>
29+
For instance, given a `wrapper` type like:
30+
</p>
31+
<blockquote><pre>
32+
// wraps a T
33+
template&lt;typename T&gt;
34+
struct wrapper {
35+
alignas(T) std::byte data[sizeof(T)];
36+
};
37+
</pre></blockquote>
38+
<p>
39+
then one can build a non-trivially relocatable object into `wrapper`
40+
objects:
41+
</p>
42+
<blockquote><pre>
43+
struct NTR { ~NTR() {} };
44+
static_assert(not std::is_trivially_relocatable_v&lt;NTR&gt;);
45+
46+
using WS = wrapper&lt;NTR&gt;;
47+
static_assert(std::is_trivially_relocatable_v&lt;WS&gt;); // OK
48+
</pre></blockquote>
49+
<p>
50+
And now one can do this:
51+
</p>
52+
<blockquote><pre>
53+
WS* ws = /* &hellip; */; // create a wrapper
54+
new (&amp;ws-&gt;data) NTR(); // create a NTR object into it
55+
56+
std::trivially_relocate(ws, ws+1, dest); // should be UB
57+
</pre></blockquote>
58+
<p>
59+
Attempting to trivially relocate `*ws` should result in undefined
60+
behavior because `NTR` isn't trivially relocatable. I don't believe that
61+
this fact is correctly captured by the preconditions of
62+
`std::trivially_relocate`.
63+
<p/>
64+
A similar issue is present for polymorphic types. In <paper num="P2786"/>'s
65+
design polymorphic types can be trivially relocatable (assuming all the other
66+
conditions hold). Given a trivially relocatable polymorphic type `P`,
67+
then this code:
68+
</p>
69+
<blockquote><pre>
70+
struct P { virtual void f(); };
71+
static_assert(std::is_trivially_relocatable_v&lt;P&gt;);
72+
73+
using WP = wrapper&lt;P&gt;;
74+
WP* wp = /* &hellip; */; // create a wrapper
75+
new (&amp;wp-&gt;data) P(); // create a P object into it
76+
77+
std::trivially_relocate(wp, wp+1, dest); // implementation defined
78+
</pre></blockquote>
79+
<p>
80+
is well-defined or UB, depending on the implementation. This is because
81+
on some implementations trivially relocating a polymorphic type requires
82+
patching its virtual table pointer; cf. the discussion in chapter 15.1
83+
of <paper num="P2786R13"/>. However the "type erasure" done by
84+
<tt>wrapper&lt;P&gt;</tt> in the example (ultimately, it is just an array
85+
of bytes) does not allow implementations to do such patching, and the code
86+
is going to fail at runtime. Therefore this case also needs to be discussed by
87+
`std::trivially_relocate`'s specification.
88+
</p>
89+
</discussion>
90+
91+
<resolution>
92+
<p>
93+
This wording is relative to <paper num="N5008"/>.
94+
</p>
95+
96+
<ol>
97+
<li><p>Modify <sref ref="[obj.lifetime]"/> as indicated:</p>
98+
99+
<blockquote class="note">
100+
<p>
101+
[<i>Drafting note</i>: For the general part of the issue (all objects in the range must be of
102+
trivially relocatable type), we append another point at the end of the existing
103+
<i>Preconditions:</i> element of `trivially_relocate`.<br/>
104+
For the specifics of polymorphic types, we amend at the end of the description the existing
105+
<i>Remarks</i>: element]
106+
</p>
107+
</blockquote>
108+
109+
<blockquote>
110+
<pre>
111+
template&lt;class T&gt;
112+
T* trivially_relocate(T* first, T* last, T* result);
113+
</pre>
114+
<blockquote>
115+
<p>
116+
-9- <i>Mandates</i>: [&hellip;]
117+
<p/>
118+
-10- <i>Preconditions</i>:
119+
</p>
120+
<ol style="list-style-type: none">
121+
<li><p>(10.1) &mdash; `[first, last)` is a valid range.</p></li>
122+
<li><p>(10.2) &mdash; `[result, result + (last - first))` denotes a region of storage that is a subset of the region
123+
reachable through `result` (<sref ref="[basic.compound]"/>) and suitably aligned for the type `T`.</p></li>
124+
<li><p>(10.3) &mdash; No element in the range `[first, last)` is a potentially-overlapping subobject.</p></li>
125+
<li><p><ins>(10.?) &mdash; All objects whose storage is being provided for (<sref ref="[intro.object]"/>) by
126+
objects in the `[first, last)` range are of trivially relocatable type.</ins></p></li>
127+
</ol>
128+
<p>
129+
-11- <i>Postconditions</i>: [&hellip;]
130+
<p/>
131+
-12- <i>Returns</i>: `result + (last - first)`.
132+
<p/>
133+
-13- <i>Throws</i>: Nothing.
134+
<p/>
135+
-14- <i>Complexity</i>: Linear in the length of the source range.
136+
<p/>
137+
-15- <i>Remarks</i>: The destination region of storage is considered reused (<sref ref="[basic.life]"/>).
138+
No constructors or destructors are invoked. <ins>If any polymorphic object (<sref ref="[class.virtual]"/>)
139+
exists in storage provided for (<sref ref="[intro.object]"/>) by objects in the `[first, last)` range,
140+
it is implementation-defined whether the behavior is undefined.</ins>
141+
<p/>
142+
[<i>Note 2</i>: Overlapping ranges are supported. &mdash; <i>end note</i>]
143+
</p>
144+
</blockquote>
145+
</blockquote>
146+
</li>
147+
148+
</ol>
149+
</resolution>
150+
151+
</issue>

0 commit comments

Comments
 (0)