<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
    <title>Launder less</title>
    <meta http-equiv="Content-Language" content="en-us">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <style type="text/css">
        .addition { color: green; }
        .right { float:right }
        .changed-deleted { background-color: #CFF0FC ; text-decoration: line-through; display: none; }
        .addition.changed-deleted { color: green; background-color: #CFF0FC ; text-decoration: line-through; text-decoration: black double line-through; display: none; }
        .changed-added { background-color: #CFF0FC ;}
        .notes { background-color: gold ;}
        pre { line-height: 1.2; font-size: 10pt; margin-top: 25px; }
        .desc { margin-left: 35px; margin-top: 10px; padding:0; white-space: normal; font-family:monospace }
        body {max-width: 1024px; margin-left: 25px;}
        del { background-color: red; }
        ins { background-color: lightgreen; text-decoration: none; }
        .sub { vertical-align: sub; }
        .lmargin50{margin-left: 50px;}
        .width_third{width: 33%;}
        .cppkeyword { color: blue; }
        .asmcostly { color: red; }
        .cppcomment { color: green; }
        .cppcomment > .cppkeyword{ color: green; }
        .cpptext { color: #2E8B57; }
        .cppaddition { background-color: #CFC; }
        .cppdeletion {  text-decoration: line-through; background-color: #FCC; }
        .stdquote { background-color: #ECECEC; font-family: Consolas,monospace; }
    </style>
    </head>
    <body bgcolor="#ffffff">
    <address>Document number: P3006R0</address>
    <address>Project: Programming Language C++</address>
    <address>Audience: EWGI, EWG, CWG</address>
    <address>&nbsp;</address>
    <address>Antony Polukhin &lt;<a href="mailto:antoshkka@gmail.com">antoshkka@gmail.com</a>&gt;, &lt;<a href="mailto:antoshkka@yandex-team.ru">antoshkka@yandex-team.ru</a>&gt;</address>
    <address>&nbsp;</address>
    <address>Date: 2023-10-19</address>
    <h1>Launder less</h1>

<p align="right">“The problem is that we attempt to solve the simplest questions cleverly, thereby rendering them unusually complex. One should seek the simple solution.”</p>
<p align="right"><i>― Anton Chekhov</i></p>

    <h2>I. Motivation</h2>
<pre>
    alignas(T) std::byte storage[sizeof(T)];
    ::new (&amp;storage) T();
    // ...
    T *ptr_ = reinterpret_cast&lt;T*&gt;(&amp;storage);  // UB
</pre>

    <p>The object that is nested within the <code>storage</code> array is not
    pointer-interconvertible with it <a href="http://eel.is/c++draft/basic.compound#4">[basic.compound] p4</a>.
    According to the Standard users have to
    call <code>std::launder</code> to avoid UB.</p>

    <p>That behavior is surprising for the language users. Moreover, popular
    compilers do not require <code>std::launder</code> call in that place
    and produce the expected assembly without it.</p>

    <p>This proposal attempts to remove UB in that place, standardize existing
    practice and simplify language usage.</p>


    <h2>I. More motivating examples</h2>
    <ul>
    <li><code>boost::optional</code> uses aligned storage and placement new to store a value. Storing a pointer from placement new may increase the size of an class.</li>
    <li><a href="https://github.com/userver-framework/userver/blob/2ccab5c00cb11eed266ccb00b89e7422fa8861f0/universal/include/userver/utils/fast_pimpl.hpp#L104"><code>utils::FastPimpl</code></a>
        from the userver framework uses byte array and placement new to store a value. Storing a pointer from placement new increases the size of an class.</li>
    <li><a href="https://github.com/ClickHouse/ClickHouse/blob/master/src/Common/HashTable/HashTable.h#L367-L396">HashTable</a> of the ClickHouse project.
        Storing a pointer from placement new increases the size of an class.</li>
    <li><a href="https://github.com/llvm/llvm-project/blob/80fa5a6377c44b3e78cddbe43abb79d209abc7e5/libcxx/include/__functional/function.h#L402-L405">libcxx</a> in function.h.</li>
    <li><a href="https://github.com/v8/v8/blob/1c5df5a1b8ade0f484bae1d6f98158bee348dff2/src/base/lazy-instance.h#L231-L245">V8</a> uses reinterpret casts and placement new.
        Storing the pointer increases object size.</li>
    <li>...</li>
    </ul>

    <p><code>std::launder</code> is required to fix the above cases. However it
    is counterintuitive, decreases the code readability
    and requires in-depth knowledge of the Standard to realize that there is an
    issue from the point of the Standard (but in practice there is no problem!).</p>

    <h2>III. Wording</h2>
    <p>Adjust [basic.compound] p4:</p>

<pre>
Two objects a and b are pointer-interconvertible if:
- they are the same object, or
- one is a union object and the other is a non-static data member of that object ([class.union]), or
- one is a standard-layout class object and the other is the first non-static data member of that object or any base class subobject of that object ([class.mem]), or
<ins>- one is an element of an array of <code>std::byte</code> or <code>unsigned char</code> and the other is an object for which the array provides storage, created at the address of the array element, or</ins>
- there exists an object c such that a and c are pointer-interconvertible, and c and b are pointer-interconvertible.
If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_cast ([expr.reinterpret.cast]).
[Note 4: An array object and its first element are not pointer-interconvertible, even though they have the same address. — end note]
</pre>


    <h2>IV. Acknowledgements</h2>
    <p>Many thanks to Артём Колпаков (Artyom Kolpakov) for asking the question on that topic at <a href="https://lists.isocpp.org/std-discussion/2023/07/2304.php">STD discussion list</a>
    and for drawing my attention to the problem. Thanks to Brian Bi for answering the question at
    <a href="https://lists.isocpp.org/std-discussion/2023/07/2305.php">STD discussion list</a>.</p>
    <p>Thanks to Andrey Erokhin, Timur Doumler, Roman Rusyaev, Mathias Stearn and all the mailing lists members for feedback and help!</p>
    <p>Thanks to Andrey Erokhin and Jason Merrill for the wording help!</p>

    <script type="text/javascript">
        function colorize_texts(texts) {
        for (var i = 0; i < texts.length; ++i) {
            var text = texts[i].innerHTML;
            text = text.replace(/namespace|using|async|do\n|while|resumable|co_await|co_yield|co_return|await|yield|sizeof|long|enum|void|concept |constexpr|extern|noexcept|bool|template|class |struct |auto|const |typename|explicit|public|private|operator|#include|inline| char |static_assert|int |return|union|static_cast|static/g,"<span class='cppkeyword'>$&<\/span>");
            text = text.replace(/\/\/[\s\S]+?\n/g,"<span class='cppcomment'>$&<\/span>");
            //text = text.replace(/\"[\s\S]+?\"/g,"<span class='cpptext'>$&<\/span>");
            texts[i].innerHTML = text;
        }
        }

        colorize_texts(document.getElementsByTagName("pre"));
        colorize_texts(document.getElementsByTagName("code"));

        function show_hide_deleted() {
        var to_change = document.getElementsByClassName('changed-deleted');
        for (var i = 0; i < to_change.length; ++i) {
            to_change[i].style.display = (document.getElementById("show_deletions").checked ? 'block' : 'none');
        }
        }
        show_hide_deleted()

        initial_text = document.getElementById('diff').innerHTML
        function on_input_change(self) {
            document.getElementById('diff').innerHTML = initial_text.replace(/async/g, self.value);
        }
    </script>
</body></html>
