<!DOCTYPE html>
    <html>
    <head>
        <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
        <title>Fixing inconsistencies between `constexpr` and `consteval` functions</title>
        
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Microsoft/vscode/extensions/markdown-language-features/media/markdown.css">
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Microsoft/vscode/extensions/markdown-language-features/media/highlight.css">
        
        <style>
.task-list-item { list-style-type: none; } .task-list-item-checkbox { margin-left: -20px; vertical-align: middle; }
</style>
        <style>
            body {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'Ubuntu', 'Droid Sans', sans-serif;
                font-size: 14px;
                line-height: 1.6;
            }
        </style>
        
        
    </head>
    <body>
        <h1 id="fixing-inconsistencies-between-constexpr-and-consteval-functions">Fixing inconsistencies between <code>constexpr</code> and <code>consteval</code> functions</h1>
<pre>Document Number: P1937R0
Date: 2019-10-06
Author: David Stone (david.stone@uber.com, david@doublewise.net)
Audience: Evolution Working Group (EWG)</pre>
<p>A <code>consteval</code> function is the same as a <code>constexpr</code> function except that:</p>
<ol>
<li>A <code>consteval</code> function is guaranteed to be evaluated at compile time.</li>
<li>You cannot have a pointer to a <code>consteval</code> function except in the body of another <code>consteval</code> function.</li>
<li>All overrides of a virtual function must be <code>consteval</code> if the base function is <code>consteval</code>, otherwise they must not be <code>consteval</code>.</li>
<li>A <code>consteval</code> function is evaluated even in contexts that would otherwise be unevaluated.</li>
</ol>
<p>The first bullet is the intended difference between the two functions. The second bullet is a consequence of the first. The third bullet (<code>virtual</code> functions) ends up making sense as a difference between them because of what is fundamentally possible. The final bullet seems like an unfortunate inconsistency in the language. This paper proposes changing the rules surrounding bullet 4 to unify the behavior of <code>consteval</code> and <code>constexpr</code> functions for C++20. There is also a discussion of <code>virtual</code> functions at the end with a proposal for a minor addition targeted at C++23.</p>
<h2 id="unevaluated-contexts">Unevaluated contexts</h2>
<p>Requiring evaluation of <code>consteval</code> functions in unevaluated contexts does not seem necessary. When I <a href="https://lists.isocpp.org/core/2019/09/7249.php">asked about this discrepancy on the core reflector</a>, the answer that I got was essentially that evaluation of a <code>consteval</code> function may have side-effects on the abstract machine, and we would like to preserve these side-effects. However, <code>consteval</code> functions are not special in this ability, so it seems strange to have a rule that makes them special. It seems to me that we should either</p>
<ol>
<li>not have a special case for <code>consteval</code>, which would mean that it is not evaluated in an unevaluated operand, or</li>
<li>Rethink the concept of &quot;unevaluated operands&quot;.</li>
</ol>
<p>This paper argues in favor of option 1, as option 2 is a breaking change that doesn't seem to bring much benefit.</p>
<p>My understanding is that the status quo means the following is valid:</p>
<pre><code><div>constexpr auto add1(auto lhs, auto rhs) {
    return lhs + rhs;
}
using T = decltype(add1(std::declval&lt;int&gt;(), std::declval&lt;int&gt;()));
</div></code></pre>
<p>but this very similar code is ill-formed</p>
<pre><code><div>consteval auto add2(auto lhs, auto rhs) {
    return lhs + rhs;
}
using T = decltype(add2(std::declval&lt;int&gt;(), std::declval&lt;int&gt;()));
</div></code></pre>
<h2 id="virtual-functions">virtual functions</h2>
<p>In C++20, we added support for <code>constexpr virtual</code> functions and we also added <code>consteval</code> functions. This means that we do not have any backward compatibility concerns binding us. This paper argues that the <code>consteval</code> rules are right for <code>consteval</code>. It is possible that the <code>consteval</code> rules highlight a problem in the rules for <code>constexpr</code>, but this paper argues that the current rules are correct.</p>
<p>A <code>virtual</code> override is allowed to weaken pre-conditions relative to the function it overrides. With <code>consteval</code> vs. an unannotated function, there is in fact no overlap in the pre-conditions. An unannotated function can only be called at run time, but a <code>consteval</code> function can only be called at compile time. A <code>constexpr</code> function, on the other hand, can be called at either run time or compile time. In other words, a <code>constexpr</code> function has weaker pre-conditions than either a <code>consteval</code> function or an unannotated function. This suggests that a <code>constexpr virtual</code> function can be overridden only by another <code>constexpr</code> function (if you have a pointer to a base class with a <code>constexpr virtual</code> member function, you should be able to call that at run time or compile time and get polymorphic behavior). However, a <code>constexpr</code> function should be allowed to override a non-<code>constexpr</code> function (as it would meet all of the pre-conditions of which phase of C++ it can run during).</p>
<p>The alternative approach here is the status quo. If a non-<code>constexpr</code> function overrides a <code>constexpr virtual</code> function, the compiler still knows that the function that would be called is non-<code>constexpr</code> and can thus still gives an error at compile time. This means that there isn't actually a hole opened up in the <code>virtual</code> &quot;type system&quot;. Overriding a <code>constexpr</code> with a non-<code>constexpr</code> does not actually cause any problems and has use cases laid out in <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html">the paper that originally proposed <code>constexpr virtual</code> functions</a>.</p>
<p>This suggests one further change to the rules. A <code>constexpr</code> function should also be allowed to override a <code>consteval virtual</code> function. However, there is no need for this to be changed for C++20: this is a feature addition, not a bug fix.</p>

    </body>
    </html>