<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>

<meta http-equiv="Content-Type" content="text/html;charset=US-ASCII">

<style type="text/css">

body { color: #000000; background-color: #FFFFFF; }
del { text-decoration: line-through; color: #8B0040; }
ins { text-decoration: underline; color: #005100; }

p.example { margin-left: 2em; }
pre.example { margin-left: 2em; }
div.example { margin-left: 2em; }

code.extract { background-color: #F5F6A2; }
pre.extract { margin-left: 2em; background-color: #F5F6A2;
  border: 1px solid #E1E28E; }

p.function { }
.attribute { margin-left: 2em; }
.attribute dt { float: left; font-style: italic;
  padding-right: 1ex; }
.attribute dd { margin-left: 0em; }

blockquote.std { color: #000000; background-color: #F1F1F1;
  border: 1px solid #D1D1D1;
  padding-left: 0.5em; padding-right: 0.5em; }
blockquote.stddel { text-decoration: line-through;
  color: #000000; background-color: #FFEBFF;
  border: 1px solid #ECD7EC;
  padding-left: 0.5empadding-right: 0.5em; ; }

blockquote.stdins { text-decoration: underline;
  color: #000000; background-color: #C8FFC8;
  border: 1px solid #B3EBB3; padding: 0.5em; }

table { border: 1px solid black; border-spacing: 0px;
  margin-left: auto; margin-right: auto; }
th { text-align: left; vertical-align: top;
  padding-left: 0.8em; border: none; }
td { text-align: left; vertical-align: top;
  padding-left: 0.8em; border: none; }

</style>

<title>Ambiguity and Insecurities with Three-Way Comparison</title>
</head>
<body>

<table style="margin-right: 0em">
<tr><th>Project:</th><td>ISO JTC1/SC22/WG21: Programming Language C++</td></tr>
<tr><th>Number:</th><td>P1380R0</td></tr>
<tr><th>Date:</th><td>2018-11-26</td></tr>
<tr><th>Audience</th><td>CWG, LWG</td></tr>
<tr><th>Revises:</th><td>None</td></tr>
<tr><th>Author:</th><td>Lawrence Crowl</td></tr>
<tr><th>Contact</th><td>Lawrence@Crowl.org</td></tr>
</table>

<h1>Ambiguity and Insecurities with Three-Way Comparison</h1>

<p>
Lawrence Crowl, Lawrence@Crowl.org
</p>


<h2>Abstract</h2>

<p>
The definition of non-type template parameter equivalence is ambiguous.
We strengthen the definition to ensure that compilation is dependable.
</p>

<p>
Several comparison function definitions
fail to meet the goal of ensuring sound comparison within programs.
Weakening the results of these functions can mitigate much of the problem.
</p>

<p>
We provide for weak ordering on floating-point types.
</p>


<h2>Introduction</h2>

<p>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0100r0.html">P0100R0
Comparison in C++</a>
introduced three-way comparison
with the express purpose of making comparison sound.
Programmers should be sure that their comparison operations
meet the constraints of the algorithms they use.
</p>

<p>
The current definitions of several standard functions
infer a strong ordering from existing
<code>&lt;</code> and <code>==</code> operators.
Unfortunately, doing so has a strong chance of making the program unsound.
A strong ordering on user-defined types
takes special care to ensure substitutability.
As a consequence,
it is easy to write a class that fails to provide strong ordering.
So, we should not infer a strong ordering from non-3way operators.
</p>

<p>
In contrast, user-defined types
are much more likely to provide a weak ordering.
Infering a weak ordering from existing operators is far less problematic.
However, there is still the potential
to strengthen a partial ordering into a weak ordering.
As currently defined, this strengthening causes silent failure.
An exception is preferable to silent failure.
</p>


<p>
The definition of non-type template parameter equivalence
appeals to the &lt;=&gt; operator,
but fails to specify the strength.
Specifying a strong ordering solves the problem.
</p>

<p>
Note that using <code>strong_compare</code> or <code>strong_equal</code>
instead of <code>operator &lt;=&gt;</code>
for non-type template argument equivalence
would enable more types as non-type template parameters,
particularly floating-point types.
We make no proposal here, but merely float the idea.
</p>


<p>
There are reasons to wish for a weak ordering on floating-point types,
particularly when keeping sets of 'normally distinguishable' values.
We provide the means to do so.
</p>


<h2>Wording</h2>

<p>
All wording edits are relative to
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4778.pdf">N4778
Working Draft, Standard for Programming Language C++</a>.
</p>


<h3>10.10.1 Defaulted comparison operator functions[class.compare.default]</h3>

<p>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1185r0.html">P1185R0 &lt;=&gt; != ==</a>
proposes a "structural equality operator"
(section 4.1 of the paper, section 10.10.1 of the standard).
This proposal calls out floating-point types as an exception,
rather than calling out the fact that the comparison is not strong. 
This approach means that the standard has a stealth defect
if it ever introduces a new built-in type without strong equality.
</p>

<p>
Edit the proposal in 
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1185r0.html">P1185R0</a> section 4.1 as follows.
</p>

<blockquote class="std">
<p>
An <code>==</code> (equal to) operator is a structural equality operator if:
</p>
<ul>
<li>it is a built-in candidate ([over.built]) where
<del>neither argument has floating point type</del>
<ins>both arguments have strong equality comparisions</ins>, or</li>
<li>it is an operator for a class type <code>C</code>
that is defined as defaulted in the definition of <code>C</code> and
all <code>==</code> operators it invokes are structural equality operators.</li>
</ul>

<p>
A type <code>T</code> has strong structural equality if,
for a glvalue <code>x</code> of type <code>const T</code>,
<code>x == x</code> is a valid expression of type <code>bool</code> and
invokes a structural equality operator.
</p>
</blockquote>


<h3>10.10.3 Other comparison operators [class.rel.eq]</h3>

<p>
When an explicitly defaulted function is deleted,
users are likely to be confused.
</p>

<blockquote class="std">
<p>
(2) The operator function with parameters <code>x</code> and <code>y</code>
is defined as deleted if ....
</p>
</blockquote>

<p>
I provide no specific remedy to this confusion,
but hope that implementations may diagnose this occurence.
</p>


<h3>12.5 Type equivalence [temp.type]</h3>

<p>
Template non-type argument equality is ambiguous;
it fails to specify the strength of the equality.
Since this ambiguity affects the application binary interface,
we should be cautious and require strong equality.
</p>

<p>
Edit paragraph 1.5 as follows.
</p>

<blockquote class="std">
<p>
(1.5) &mdash; their remaining corresponding
non-type <var>template-arguments</var>
have the same type and value
after conversion to the type of the <var>template-parameter</var>,
where they are considered to have the same value
if they compare <del>equal</del>
<ins><code>std::strong_ordering::equal</code></ins>
with <code>operator&lt;=&gt;</code>, and
</p>
</blockquote>

<p>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1185r0.html">P1185R0
&lt;=&gt; != ==</a>
proposes a "structural equality operator"
(section 4.1 of the paper, section 10.10.1 of the standard).
With adoption of 
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1185r0.html">P1185R0</a> or a successor,
the above paragraph should be rewritten using strong structural equality.
</p>

<p>
Edit paragraph 1.5 as follows.
</p>

<blockquote class="std">
<p>
(1.5) &mdash; their remaining corresponding
non-type <var>template-arguments</var>
have the same type and value
after conversion to the type of the <var>template-parameter</var>,
where they are considered to have the same value
if they compare equal
with <ins>a strong structural equality</ins>
<code>operator&lt;=&gt;</code>, and
</p>
</blockquote>


<h3>16.11.4 Comparison algorithms [cmp.alg]</h3>

<p>
Constructing a strong ordering
from the existence of <code>&lt;</code> and <code>==</code>
has a strong chance of making the program unsound.
Furthermore, most algorithms do not need a strong ordering,
so the risk outweighs the reward.
Programmers that need the strong ordering can write it.
</p>

<p>
Remove paragraph 1.4.
</p>

<blockquote class="stddel">
<p>
(1.4) &mdash; Otherwise,
if the expressions <code>a == b</code> and <code>a &lt; b</code>
are each well-formed and convertible to bool,
then
</p>
<ul>
<li>(1.4.1) &mdash; if a == b is true,
returns strong_ordering::equal;</li>
<li>(1.4.2) &mdash; otherwise, if a < b is true,
returns strong_ordering::less;</li>
<li>(1.4.3) &mdash; otherwise,
returns strong_ordering::greater.</li>
</ul>
</blockquote>


<p>
The <code>strong_order</code> function has a special case for floating point.
</p>

<blockquote class="std">
<p>
(1.1) &mdash; If <code>numeric_limits&lt;T&gt;::is_iec559</code>
is <code>true</code>,
returns a result of type <code>strong_ordering</code>
that is consistent with the <code>totalOrder</code> operation
as specified in ISO/IEC/IEEE 60559.
</p>
</blockquote>

<p>
Because standard floating point provides only a partial ordering,
we have a gap in the comparison strengths at weak ordering.
So, a similar special case is needed for a weak ordering.
This special case should be consistent
(P0100R2, Consistence Between Relations)
with both <code>totalOrder</code>
and the partial order implied by the operators.
</p>

<p>
Add a new subparagraph before (2.1) as follows.
</p>

<blockquote class="stdins">
<p>
(2.05) &mdash; If <code>numeric_limits&lt;T&gt;::is_iec559</code>
is <code>true</code>,
returns a result of type <code>weak_ordering</code>
that has the following equivalence classes ordered from lesser to greater.
</p>
<ul>
<li>Together, all negative NaN values.</li>
<li>Negative infinity.</li>
<li>Separately, each normal and subnormal negative value.</li>
<li>Together, both zero values.</li>
<li>Separately, each normal and subnormal positive value.</li>
<li>Positive infinity.</li>
<li>Together, all positive NaN values.</li>
</ul>
</blockquote>


<p>
Rather than solently promote partial orders to weak orders,
we propose to detect comparisons that are not weak
and throw an exception.
</p>

<p>
Edit paragraph 2.3 as follows.
</p>

<blockquote class="std">
<p>
(2.3) &mdash; Otherwise,
if the expressions <code>a == b</code> and <code>a &lt; b</code>
are each well-formed and convertible to bool,
then
</p>
<ul>
<li>(2.3.1) &mdash; if <code>a == b</code> is <code>true</code>,
returns <code>weak_ordering::equivalent</code>;</li>
<li>(2.3.2) &mdash; otherwise,
if <code>a &lt; b</code> is <code>true</code>,
returns <code>weak_ordering::less</code>;</li>
<li>(2.3.3) &mdash; otherwise,
<ins>if <code>b &lt; a</code> is <code>true</code>,</ins>
returns <code>weak_ordering::greater</code>;</li>
<li><ins>(2.3.4) &mdash; otherwise, 
throws <code>std::domain_error</code>.</ins></li>
</ul>
</blockquote>


<p>
Similarly, the definition for <code>partial_order</code>
incorrectly strengthens a partial ordering.
</p>

<p>
Edit paragraph 3.3 as follows.
</p>

<blockquote class="std">
<p>
(3.3) &mdash; Otherwise,
if the expressions <code>a == b</code> and <code>a &lt; b</code>
are each well-formed and convertible to bool,
then
</p>
<ul>
<li>(3.3.1) &mdash; if <code>a == b</code> is <code>true</code>,
returns <code>partial_ordering::equivalent</code>;</li>
<li>(3.3.2) &mdash; otherwise,
if <code>a &lt; b</code> is <code>true</code>,
returns <code>partial_ordering::less</code>;</li>
<li>(3.3.3) &mdash; otherwise,
<ins>if <code>b &lt; a</code> is <code>true</code>,</ins>
returns <code>partial_ordering::greater</code>;</li>
<li><ins>(3.3.4) &mdash; otherwise, 
returns <code>partial_ordering::unordered</code>.</ins></li>
</ul>
</blockquote>


<p>
Similarly to <code>strong_order</code>,
<code>strong_equal</code> unsafely strengthens the order of the operators.
</p>

<p>
Delete paragraph (4.3).
</p>

<blockquote class="stddel">
<p>
(4.3) &mdash; Otherwise,
if the expression <code>a == b</code>
is well-formed and convertible to <code>bool</code>, then
</p>
<ul>
<li>(4.3.1) &mdash; if <code>a == b</code> is <code>true</code>,
returns <code>strong_equality::equal</code>;</li>
<li>(4.3.2) &mdash; otherwise,
returns <code>strong_equality::nonequal</code>.</li>
</ul>
</blockquote>


<p>
Paragraph 5 defining <code>weak_order</code>
is probably an acceptable default.
Programmers that use <code>operator ==</code> for other purposes
will need to delete the overload.
</p>


<h3>23.7.11 Three-way comparison algorithms [alg.3way]</h3>

<p>
As before, inferring a <code>strong_ordering</code>
or a <code>strong_equality</code>
from <code>&lt;</code> and <code>==</code>
is unsound.
Again, a weak ordering, with protection,
is probably acceptable.
</p>

<p>
Edit paragraph 1.2 for <code>compare_3way</code> as follows.
</p>

<blockquote class="std">
<p>
(1.2) &mdash; Otherwise,
if the expressions <code>a == b</code> and <code>a &lt; b</code>
are each well-formed and convertible to <code>bool</code>,
returns <del><code>strong_ordering::equal</code></del>
<ins><code>weak_ordering::equivalent</code></ins>
when <code>a == b</code> is <code>true</code>,
otherwise returns <del><code>strong_ordering::less</code></del>
<ins><code>weak_ordering::less</code></ins>
when <code>a &lt; b</code> is <code>true</code>, and
otherwise returns <code>strong_ordering::greater</code>
<ins>when <code>b &lt; a</code> is <code>true</code>, and
otherwise throws <code>std::domain_error</code></ins>.
</p>
</blockquote>

<blockquote class="std">
<p>
(1.3) &mdash; Otherwise,
if the expression <code>a == b</code>
is well-formed and convertible to <code>bool</code>,
returns <del><code>strong_equality::equal</code></del>
<ins><code>weak_equality::equivalent</code></ins>
when <code>a == b</code> is <code>true</code>,
and otherwise returns <del><code>strong_equality::nonequal</code></del>
<ins><code>weak_equality::nonequivalent</code></ins>.
</p>
</blockquote>

<p>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1186r0.html">P1186R0
When do you actually use &lt;=&gt;</a>
Proposes to move this function definition
into the definition for <code>operator &lt;=&gt;</code>.
If so done,
the edits here would apply to <code>operator &lt;=&gt;</code>.
</p>

</body>
</html>
