Skip to content

Commit 5ca49bb

Browse files
committed
use global NaN symbol for NaN equality comparisons
1 parent fb44a1d commit 5ca49bb

File tree

6 files changed

+121
-6
lines changed

6 files changed

+121
-6
lines changed

src/compiler/checker.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,7 @@ namespace ts {
996996
let deferredGlobalOmitSymbol: Symbol | undefined;
997997
let deferredGlobalAwaitedSymbol: Symbol | undefined;
998998
let deferredGlobalBigIntType: ObjectType | undefined;
999+
let deferredGlobalNaNSymbol: Symbol | undefined;
9991000

10001001
const allPotentiallyUnusedIdentifiers = new Map<Path, PotentiallyUnusedIdentifier[]>(); // key is file name
10011002

@@ -14287,6 +14288,10 @@ namespace ts {
1428714288
return (deferredGlobalBigIntType ||= getGlobalType("BigInt" as __String, /*arity*/ 0, /*reportErrors*/ false)) || emptyObjectType;
1428814289
}
1428914290

14291+
function getGlobalNaNSymbol(): Symbol | undefined {
14292+
return (deferredGlobalNaNSymbol ||= getGlobalValueSymbol("NaN" as __String, /*reportErrors*/ false));
14293+
}
14294+
1429014295
/**
1429114296
* Instantiates a global type that is generic with some element type, and returns that instantiation.
1429214297
*/
@@ -34386,7 +34391,7 @@ namespace ts {
3438634391
const eqType = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken;
3438734392
error(errorNode, Diagnostics.This_condition_will_always_return_0_since_JavaScript_compares_objects_by_reference_not_value, eqType ? "false" : "true");
3438834393
}
34389-
checkNaNEquality(errorNode, operator, left, right, leftType, rightType);
34394+
checkNaNEquality(errorNode, operator, left, right);
3439034395
reportOperatorErrorUnless((left, right) => isTypeEqualityComparableTo(left, right) || isTypeEqualityComparableTo(right, left));
3439134396
return booleanType;
3439234397

@@ -34620,9 +34625,9 @@ namespace ts {
3462034625
}
3462134626
}
3462234627

34623-
function checkNaNEquality(errorNode: Node | undefined, operator: SyntaxKind, left: Expression, right: Expression, leftType: Type, rightType: Type) {
34624-
const isLeftNaN = (leftType.flags & TypeFlags.Number) && isNaN(skipParentheses(left));
34625-
const isRightNaN = (rightType.flags & TypeFlags.Number) && isNaN(skipParentheses(right));
34628+
function checkNaNEquality(errorNode: Node | undefined, operator: SyntaxKind, left: Expression, right: Expression) {
34629+
const isLeftNaN = isGlobalNaN(skipParentheses(left));
34630+
const isRightNaN = isGlobalNaN(skipParentheses(right));
3462634631
if (isLeftNaN || isRightNaN) {
3462734632
const err = error(errorNode, Diagnostics.This_condition_will_always_return_0,
3462834633
tokenToString(operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.EqualsEqualsToken ? SyntaxKind.FalseKeyword : SyntaxKind.TrueKeyword));
@@ -34635,8 +34640,12 @@ namespace ts {
3463534640
}
3463634641
}
3463734642

34638-
function isNaN(expr: Expression): boolean {
34639-
return isIdentifier(expr) && expr.escapedText === "NaN";
34643+
function isGlobalNaN(expr: Expression): boolean {
34644+
if (isIdentifier(expr) && expr.escapedText === "NaN") {
34645+
const globalNaNSymbol = getGlobalNaNSymbol();
34646+
return !!globalNaNSymbol && globalNaNSymbol === getResolvedSymbol(expr);
34647+
}
34648+
return false;
3464034649
}
3464134650
}
3464234651

tests/baselines/reference/nanEquality.errors.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,16 @@ tests/cases/compiler/nanEquality.ts(29,5): error TS2845: This condition will alw
9494
~~~~~~~~~~~~~~~
9595
!!! error TS2845: This condition will always return 'false'.
9696
!!! related TS1369 tests/cases/compiler/nanEquality.ts:29:13: Did you mean 'Number.isNaN(...)'?
97+
98+
function t1(value: number, NaN: number) {
99+
return value === NaN; // ok
100+
}
101+
102+
function t2(value: number, NaN: number) {
103+
return NaN == value; // ok
104+
}
105+
106+
function t3(NaN: number) {
107+
return NaN === NaN; // ok
108+
}
97109

tests/baselines/reference/nanEquality.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ if (NaN != NaN) {}
2828
// ...
2929
declare let y: any;
3030
if (NaN === y[0][1]) {}
31+
32+
function t1(value: number, NaN: number) {
33+
return value === NaN; // ok
34+
}
35+
36+
function t2(value: number, NaN: number) {
37+
return NaN == value; // ok
38+
}
39+
40+
function t3(NaN: number) {
41+
return NaN === NaN; // ok
42+
}
3143

3244

3345
//// [nanEquality.js]
@@ -48,3 +60,12 @@ if (NaN !== NaN) { }
4860
if (NaN == NaN) { }
4961
if (NaN != NaN) { }
5062
if (NaN === y[0][1]) { }
63+
function t1(value, NaN) {
64+
return value === NaN; // ok
65+
}
66+
function t2(value, NaN) {
67+
return NaN == value; // ok
68+
}
69+
function t3(NaN) {
70+
return NaN === NaN; // ok
71+
}

tests/baselines/reference/nanEquality.symbols

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,32 @@ if (NaN === y[0][1]) {}
7474
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
7575
>y : Symbol(y, Decl(nanEquality.ts, 27, 11))
7676

77+
function t1(value: number, NaN: number) {
78+
>t1 : Symbol(t1, Decl(nanEquality.ts, 28, 23))
79+
>value : Symbol(value, Decl(nanEquality.ts, 30, 12))
80+
>NaN : Symbol(NaN, Decl(nanEquality.ts, 30, 26))
81+
82+
return value === NaN; // ok
83+
>value : Symbol(value, Decl(nanEquality.ts, 30, 12))
84+
>NaN : Symbol(NaN, Decl(nanEquality.ts, 30, 26))
85+
}
86+
87+
function t2(value: number, NaN: number) {
88+
>t2 : Symbol(t2, Decl(nanEquality.ts, 32, 1))
89+
>value : Symbol(value, Decl(nanEquality.ts, 34, 12))
90+
>NaN : Symbol(NaN, Decl(nanEquality.ts, 34, 26))
91+
92+
return NaN == value; // ok
93+
>NaN : Symbol(NaN, Decl(nanEquality.ts, 34, 26))
94+
>value : Symbol(value, Decl(nanEquality.ts, 34, 12))
95+
}
96+
97+
function t3(NaN: number) {
98+
>t3 : Symbol(t3, Decl(nanEquality.ts, 36, 1))
99+
>NaN : Symbol(NaN, Decl(nanEquality.ts, 38, 12))
100+
101+
return NaN === NaN; // ok
102+
>NaN : Symbol(NaN, Decl(nanEquality.ts, 38, 12))
103+
>NaN : Symbol(NaN, Decl(nanEquality.ts, 38, 12))
104+
}
105+

tests/baselines/reference/nanEquality.types

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,35 @@ if (NaN === y[0][1]) {}
103103
>0 : 0
104104
>1 : 1
105105

106+
function t1(value: number, NaN: number) {
107+
>t1 : (value: number, NaN: number) => boolean
108+
>value : number
109+
>NaN : number
110+
111+
return value === NaN; // ok
112+
>value === NaN : boolean
113+
>value : number
114+
>NaN : number
115+
}
116+
117+
function t2(value: number, NaN: number) {
118+
>t2 : (value: number, NaN: number) => boolean
119+
>value : number
120+
>NaN : number
121+
122+
return NaN == value; // ok
123+
>NaN == value : boolean
124+
>NaN : number
125+
>value : number
126+
}
127+
128+
function t3(NaN: number) {
129+
>t3 : (NaN: number) => boolean
130+
>NaN : number
131+
132+
return NaN === NaN; // ok
133+
>NaN === NaN : boolean
134+
>NaN : number
135+
>NaN : number
136+
}
137+

tests/cases/compiler/nanEquality.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,15 @@ if (NaN != NaN) {}
2727
// ...
2828
declare let y: any;
2929
if (NaN === y[0][1]) {}
30+
31+
function t1(value: number, NaN: number) {
32+
return value === NaN; // ok
33+
}
34+
35+
function t2(value: number, NaN: number) {
36+
return NaN == value; // ok
37+
}
38+
39+
function t3(NaN: number) {
40+
return NaN === NaN; // ok
41+
}

0 commit comments

Comments
 (0)