[Issue 23675] New: Error on direct double.NaN comparison

d-bugmail at puremagic.com d-bugmail at puremagic.com
Tue Feb 7 11:37:49 UTC 2023


https://issues.dlang.org/show_bug.cgi?id=23675

          Issue ID: 23675
           Summary: Error on direct double.NaN comparison
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: enhancement
          Priority: P1
         Component: dmd
          Assignee: nobody at puremagic.com
          Reporter: qs.il.paperinik at gmail.com

The following code compiles without error or warning:
```d
void main()
{
    double x;
    alias D = double;
    if (x == double.nan){} // 1 should be an error
    if (x != double()){}   // 2 should be an error
    if (x is double.nan){} // 3 no error
    if (x < (D.nan)){}     // 4 should be an error
    if (x == D()){}        // 5 no error
    if (x == double.init){}// 6 should be an error
    if (x == D.init){}     // 7 no error

    auto vnan      = double.nan;
    immutable inan = double.nan;
    enum enan      = double.nan;
    alias anan     = double.nan;
    if (x == vnan){} // no error
    if (x == inan){} // no error
    if (x == enan){} // no error
    if (x == anan){} // no error
}
```

Suggestion: The comparison of a runtime floating-point expression with one of
the compile-time constant `nan` properties of the floating-point types should
be an error.

The assumption is that the programmer wanted to test if `x` holds a NaN value,
but the check is an always-false condition (in the case of `!=` an always-true
condition).

As you can see, relevant is the direct mentioning of the property, the
mentioning of the type is irrelevant, except in the default-construction/init
case. In the default-construction/init case, the type must be syntactically a
floating-point `FundamentalType` (optionally qualified). An alias to a
floating-point is syntactically not a FundamentalType, but an identifier. When
the type is syntactically an identifier that only happens to refer to
floating-point type semantically, for the default-construction/init case, there
could still be a warning, but an error would be a problem for meta-programming.

The error message should suggest to use `std.math.traits.isNaN` (first and
second example) or `std.math.operations.cmp` (fourth example).
Maybe the error message could suggest using `is` if an exact bit pattern match
is desired.

Suggested error message for the first condition in the example:

> Error: A direct comparison with `double.nan` is always false.
>        Use `std.math.traits.isNaN` to check if the value is any NaN value.
>        Use `x is double.nan` if an exact exact bit pattern match is desired.

Suggested error message for the second and sixth condition in the example:

> Error: A direct comparison with `double.init` is always false.
>        Use `std.math.traits.isNaN` to check if the value is any NaN value.
>        Use `x is double.init` if an exact exact bit pattern match is desired.

Suggested error message for the fourth condition in the example:

> Error: A direct comparison with `double.nan` is always false.
>        Use `std.math.operations.cmp` for a total ordering that includes NaN.

This reasoning does of course not apply to an IdentityExpression (e.g. `x is
double.nan`) with `nan`, e.g. the third condition in the introductory example.
Here, we must assume the programmer knows about the issue.

The checks don’t extend to other compile-time known values that are
`double.nan`. The fifth and further conditions in the example do not produce
the error.

---

In particular, for every `EqualExpression` and `RelExpression` if the left-hand
or right-hand side ShiftExpression (or both) is a `PrimaryExpression`
designating a floating-point type’s `nan` property, that is an error; if the
left-hand or right-hand side ShiftExpression (or both) is a `PrimaryExpression`
that accesses an `init` property of or default constructs with syntactically
empty parentheses (i.e. excluding a zero-length expansion of a pack) an
optionally qualified FundamentalType that is a floating-point type, that is an
error.

The following forms of `PrimaryExpression` can result in an expression that
produces that error:

    FundamentalType . Identifier

is the obvious contender when `Identifier` is `nan`. Also when it’s 

    Identifier
    . Identifier

I don’t know if they can represent e.g. `double.nan` because `with` does not
work with built-in types:

    MixinExpression
    TraitsExpression

Not sure if those should count if they happen to become a compile-time nan
value. My guess is no.

    ( Type ) . Identifier
    FundamentalType ( )
    TypeCtor ( Type ) . Identifier
    TypeCtor ( Type ) ( )

Those are just a little more complicated versions of obvious contender when
`Identifier` is `nan`. However, in the last case, `Type` must **syntactically**
be `FundamentalType`, and not e.g. an alias to `double`. This is so that when a
template type parameter is `double`, it won’t trigger that error.

    ( Expression )

If `Expression` is a `PrimaryExpression` that produces the error (recursive).

--


More information about the Digitalmars-d-bugs mailing list