Comment #0 by qs.il.paperinik — 2023-02-07T11:37:49Z
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).
Comment #1 by dkorpel — 2023-02-07T14:39:53Z
This should be a linter warning, it's not worth complicating the language for a niche error like that.
Comment #2 by razvan.nitu1305 — 2023-02-08T14:46:58Z
I agree with Dennis on this one.
Comment #3 by robert.schadek — 2024-12-13T19:27:09Z