D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 9668 - Disallow (dis)equality with FP.nan/FP.init literals
Summary: Disallow (dis)equality with FP.nan/FP.init literals
Status: NEW
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P4 enhancement
Assignee: No Owner
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-03-08 11:50 UTC by bearophile_hugs
Modified: 2024-12-13 18:04 UTC (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description bearophile_hugs 2013-03-08 11:50:05 UTC
Since a lot of time D statically refuses the use of "classReference == null":

// Program #1
class Foo {}
void main() {
    Foo f;
    assert(f == null);
    assert(f != null);
}


test.d(4): Error: use 'is' instead of '==' when comparing with null
test.d(5): Error: use '!is' instead of '!=' when comparing with null



Not expert D programmers sometime compare a double with double.nan in a wrong way:

http://forum.dlang.org/thread/mailman.1845.1334694574.4860.digitalmars-d-learn@puremagic.com#post-jmlhfr:2428cv:241:40digitalmars.com

because someDouble == double.nan is always false:


// Program #2
import std.math: isNaN;
void main() {
    double x = double.init;
    assert(x != double.nan);
    assert(x != double.init);
    assert(isNaN(x));
    assert(x is double.init);
    assert(x !is double.nan);

    double y = double.nan;
    assert(y != double.nan);
    assert(y != double.init);
    assert(isNaN(y));
    assert(y !is double.init);
    assert(y is double.nan);
}



I think there are three common wrong usage patterns of NaNs 
testing:
1) Test that x is equal to/different from nan using x==FP.nan/x!=FP.nan
2) Test that x is equal to/different from all NaNs using x==FP.nan/x!=FP.nan
3) Test that x is equal to/different from FP.init using x==FP.init/x!=FP.init

The case 3 is a bit less important because the programmer already knows something about FP init, but it's wrong still.

There are other wrong usages of NaNs but they are by more expert programmers, to I don't want to catch them (example: using "is" to test if x is equal to/different from all NaNs is a bug, because there are many bitwise-different NaNs. But if the programmer uses "is" I assume he/she/shi knows enough about NaNs, so this is not flagged by the compiler).

Currently this program compiles with no errors:


// Program #3
void main() {
    float x1 = float.nan;
    assert(x1 == float.nan);
    float x2 = 0.0;
    assert(x2 != float.nan);
    float x3 = float.init;
    assert(x3 == float.init);
    float x4 = 0.0;
    assert(x4 != float.init);

    double x5 = double.nan;
    assert(x5 == double.nan);
    double x6 = 0.0;
    assert(x6 != double.nan);
    double x7 = double.init;
    assert(x7 == double.init);
    double x8 = 0.0;
    assert(x8 != double.init);

    real x9 = real.nan;
    assert(x9 == real.nan);
    real x10 = 0.0;
    assert(x10 != real.nan);
    real x11 = real.init;
    assert(x11 == real.init);
    real x12 = 0.0;
    assert(x12 != real.init);

    enum double myNaN = double.nan;
    assert(myNaN == double.nan);
}



So I propose to statically disallow comparisons of Program #3, so it generates errors similar to:

test.d(4): Error: comparison is always false. Use 'std.math.isNaN' to test for every kind of NaN or 'is float.nan' for this specific NaN
test.d(6): Error: comparison is always true. Use '!std.math.isNaN' to test for every kind of NaN or '!is float.nan' for this specific NaN
test.d(8): Error: comparison is always false. Use 'std.math.isNaN' to test for every kind of NaN or 'is float.init' for this specicif NaN
...


Opinions, improvements, votes or critics are welcome.


See discussion thread, this idea was generally liked:
http://forum.dlang.org/thread/undjpdewiqmghmhndedw@forum.dlang.org
Comment 1 dlangBugzillaToGithub 2024-12-13 18:04:37 UTC
THIS ISSUE HAS BEEN MOVED TO GITHUB

https://github.com/dlang/dmd/issues/18531

DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB