From 659364dcf20f745269741cbce8fb2cb5938c2d5c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 May 2026 19:42:27 +0000 Subject: [PATCH 1/3] Initial plan From d6775a061436b3267d08026303f239f7eccfd80b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 May 2026 20:19:15 +0000 Subject: [PATCH 2/3] Add test case for NaN-valued enum members Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/4d642ea9-d310-4a99-b4f0-21b5bbc1eb13 Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- .../compiler/enumNaNValues.errors.txt | 26 ++++++++++ .../reference/compiler/enumNaNValues.js | 32 +++++++++++++ .../reference/compiler/enumNaNValues.symbols | 47 +++++++++++++++++++ .../reference/compiler/enumNaNValues.types | 44 +++++++++++++++++ .../tests/cases/compiler/enumNaNValues.ts | 15 ++++++ 5 files changed, 164 insertions(+) create mode 100644 testdata/baselines/reference/compiler/enumNaNValues.errors.txt create mode 100644 testdata/baselines/reference/compiler/enumNaNValues.js create mode 100644 testdata/baselines/reference/compiler/enumNaNValues.symbols create mode 100644 testdata/baselines/reference/compiler/enumNaNValues.types create mode 100644 testdata/tests/cases/compiler/enumNaNValues.ts diff --git a/testdata/baselines/reference/compiler/enumNaNValues.errors.txt b/testdata/baselines/reference/compiler/enumNaNValues.errors.txt new file mode 100644 index 0000000000..a1394099cd --- /dev/null +++ b/testdata/baselines/reference/compiler/enumNaNValues.errors.txt @@ -0,0 +1,26 @@ +enumNaNValues.ts(6,7): error TS2322: Type 'E.B' is not assignable to type 'E.A'. +enumNaNValues.ts(7,7): error TS2322: Type 'E.A' is not assignable to type 'E.B'. +enumNaNValues.ts(13,7): error TS2322: Type 'F' is not assignable to type 'E.A'. + + +==== enumNaNValues.ts (3 errors) ==== + enum E { + A = NaN, + B = NaN, + } + + const a: E.A = E.B; + ~ +!!! error TS2322: Type 'E.B' is not assignable to type 'E.A'. + const b: E.B = E.A; + ~ +!!! error TS2322: Type 'E.A' is not assignable to type 'E.B'. + + enum F { + X = NaN, + } + + const c: E.A = F.X; // Error expected - different enums + ~ +!!! error TS2322: Type 'F' is not assignable to type 'E.A'. + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/enumNaNValues.js b/testdata/baselines/reference/compiler/enumNaNValues.js new file mode 100644 index 0000000000..88ba034c4d --- /dev/null +++ b/testdata/baselines/reference/compiler/enumNaNValues.js @@ -0,0 +1,32 @@ +//// [tests/cases/compiler/enumNaNValues.ts] //// + +//// [enumNaNValues.ts] +enum E { + A = NaN, + B = NaN, +} + +const a: E.A = E.B; +const b: E.B = E.A; + +enum F { + X = NaN, +} + +const c: E.A = F.X; // Error expected - different enums + + +//// [enumNaNValues.js] +"use strict"; +var E; +(function (E) { + E[E["A"] = NaN] = "A"; + E[E["B"] = NaN] = "B"; +})(E || (E = {})); +const a = E.B; +const b = E.A; +var F; +(function (F) { + F[F["X"] = NaN] = "X"; +})(F || (F = {})); +const c = F.X; // Error expected - different enums diff --git a/testdata/baselines/reference/compiler/enumNaNValues.symbols b/testdata/baselines/reference/compiler/enumNaNValues.symbols new file mode 100644 index 0000000000..0d3125bbb8 --- /dev/null +++ b/testdata/baselines/reference/compiler/enumNaNValues.symbols @@ -0,0 +1,47 @@ +//// [tests/cases/compiler/enumNaNValues.ts] //// + +=== enumNaNValues.ts === +enum E { +>E : Symbol(E, Decl(enumNaNValues.ts, 0, 0)) + + A = NaN, +>A : Symbol(E.A, Decl(enumNaNValues.ts, 0, 8)) +>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --)) + + B = NaN, +>B : Symbol(E.B, Decl(enumNaNValues.ts, 1, 10)) +>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --)) +} + +const a: E.A = E.B; +>a : Symbol(a, Decl(enumNaNValues.ts, 5, 5)) +>E : Symbol(E, Decl(enumNaNValues.ts, 0, 0)) +>A : Symbol(E.A, Decl(enumNaNValues.ts, 0, 8)) +>E.B : Symbol(E.B, Decl(enumNaNValues.ts, 1, 10)) +>E : Symbol(E, Decl(enumNaNValues.ts, 0, 0)) +>B : Symbol(E.B, Decl(enumNaNValues.ts, 1, 10)) + +const b: E.B = E.A; +>b : Symbol(b, Decl(enumNaNValues.ts, 6, 5)) +>E : Symbol(E, Decl(enumNaNValues.ts, 0, 0)) +>B : Symbol(E.B, Decl(enumNaNValues.ts, 1, 10)) +>E.A : Symbol(E.A, Decl(enumNaNValues.ts, 0, 8)) +>E : Symbol(E, Decl(enumNaNValues.ts, 0, 0)) +>A : Symbol(E.A, Decl(enumNaNValues.ts, 0, 8)) + +enum F { +>F : Symbol(F, Decl(enumNaNValues.ts, 6, 19)) + + X = NaN, +>X : Symbol(F.X, Decl(enumNaNValues.ts, 8, 8)) +>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --)) +} + +const c: E.A = F.X; // Error expected - different enums +>c : Symbol(c, Decl(enumNaNValues.ts, 12, 5)) +>E : Symbol(E, Decl(enumNaNValues.ts, 0, 0)) +>A : Symbol(E.A, Decl(enumNaNValues.ts, 0, 8)) +>F.X : Symbol(F.X, Decl(enumNaNValues.ts, 8, 8)) +>F : Symbol(F, Decl(enumNaNValues.ts, 6, 19)) +>X : Symbol(F.X, Decl(enumNaNValues.ts, 8, 8)) + diff --git a/testdata/baselines/reference/compiler/enumNaNValues.types b/testdata/baselines/reference/compiler/enumNaNValues.types new file mode 100644 index 0000000000..e9684c7f81 --- /dev/null +++ b/testdata/baselines/reference/compiler/enumNaNValues.types @@ -0,0 +1,44 @@ +//// [tests/cases/compiler/enumNaNValues.ts] //// + +=== enumNaNValues.ts === +enum E { +>E : E + + A = NaN, +>A : E.A +>NaN : number + + B = NaN, +>B : E.B +>NaN : number +} + +const a: E.A = E.B; +>a : E.A +>E : any +>E.B : E.B +>E : typeof E +>B : E.B + +const b: E.B = E.A; +>b : E.B +>E : any +>E.A : E.A +>E : typeof E +>A : E.A + +enum F { +>F : F + + X = NaN, +>X : F.X +>NaN : number +} + +const c: E.A = F.X; // Error expected - different enums +>c : E.A +>E : any +>F.X : F +>F : typeof F +>X : F + diff --git a/testdata/tests/cases/compiler/enumNaNValues.ts b/testdata/tests/cases/compiler/enumNaNValues.ts new file mode 100644 index 0000000000..33c0d03741 --- /dev/null +++ b/testdata/tests/cases/compiler/enumNaNValues.ts @@ -0,0 +1,15 @@ +// @strict: true + +enum E { + A = NaN, + B = NaN, +} + +const a: E.A = E.B; +const b: E.B = E.A; + +enum F { + X = NaN, +} + +const c: E.A = F.X; // Error expected - different enums From 3c31b07322c41d61c60270561c04201889ac6271 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 May 2026 20:32:44 +0000 Subject: [PATCH 3/3] Fix NaN-valued enum members creating distinct literal types In Go, NaN != NaN for map key comparisons, causing each NaN-valued enum member to get a distinct type. Fix by caching NaN types separately from the map-based caches in both getNumberLiteralType and getEnumLiteralType. Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/4d642ea9-d310-4a99-b4f0-21b5bbc1eb13 Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/checker/checker.go | 24 ++++++++++++++++++- .../compiler/enumAutoIncrementValue.types | 2 +- .../compiler/enumNaNValues.errors.txt | 12 +++------- .../reference/compiler/enumNaNValues.types | 16 ++++++------- .../conformance/enumConstantMembers.types | 4 ++-- .../enumConstantMembers.types.diff | 20 ---------------- 6 files changed, 37 insertions(+), 41 deletions(-) delete mode 100644 testdata/baselines/reference/submodule/conformance/enumConstantMembers.types.diff diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 020fe61193..5e23758e72 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -614,8 +614,10 @@ type Checker struct { evaluate evaluator.Evaluator stringLiteralTypes map[string]*Type numberLiteralTypes map[jsnum.Number]*Type + nanType *Type bigintLiteralTypes map[jsnum.PseudoBigInt]*Type enumLiteralTypes map[EnumLiteralKey]*Type + enumNaNLiteralTypes map[*ast.Symbol]*Type indexedAccessTypes map[CacheHashKey]*Type templateLiteralTypes map[CacheHashKey]*Type stringMappingTypes map[StringMappingKey]*Type @@ -924,6 +926,7 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { c.numberLiteralTypes = make(map[jsnum.Number]*Type) c.bigintLiteralTypes = make(map[jsnum.PseudoBigInt]*Type) c.enumLiteralTypes = make(map[EnumLiteralKey]*Type) + c.enumNaNLiteralTypes = make(map[*ast.Symbol]*Type) c.indexedAccessTypes = make(map[CacheHashKey]*Type) c.templateLiteralTypes = make(map[CacheHashKey]*Type) c.stringMappingTypes = make(map[StringMappingKey]*Type) @@ -25152,6 +25155,14 @@ func (c *Checker) getStringLiteralType(value string) *Type { } func (c *Checker) getNumberLiteralType(value jsnum.Number) *Type { + // NaN cannot be used as a Go map key because NaN != NaN in IEEE 754, + // so Go map lookups for NaN always miss. Cache NaN type separately. + if value.IsNaN() { + if c.nanType == nil { + c.nanType = c.newLiteralType(TypeFlagsNumberLiteral, value, nil) + } + return c.nanType + } t := c.numberLiteralTypes[value] if t == nil { t = c.newLiteralType(TypeFlagsNumberLiteral, value, nil) @@ -25193,11 +25204,22 @@ func getBooleanLiteralValue(t *Type) bool { func (c *Checker) getEnumLiteralType(value any, enumSymbol *ast.Symbol, symbol *ast.Symbol) *Type { var flags TypeFlags - switch value.(type) { + switch v := value.(type) { case string: flags = TypeFlagsEnumLiteral | TypeFlagsStringLiteral case jsnum.Number: flags = TypeFlagsEnumLiteral | TypeFlagsNumberLiteral + // NaN cannot be used as a Go map key because NaN != NaN in IEEE 754, + // so Go map lookups for NaN always miss. Cache NaN enum types separately by enum symbol. + if v.IsNaN() { + t := c.enumNaNLiteralTypes[enumSymbol] + if t == nil { + t = c.newLiteralType(flags, value, nil) + t.symbol = symbol + c.enumNaNLiteralTypes[enumSymbol] = t + } + return t + } default: panic("Unhandled case in getEnumLiteralType") } diff --git a/testdata/baselines/reference/compiler/enumAutoIncrementValue.types b/testdata/baselines/reference/compiler/enumAutoIncrementValue.types index 900cfc8b84..dd23ea4453 100644 --- a/testdata/baselines/reference/compiler/enumAutoIncrementValue.types +++ b/testdata/baselines/reference/compiler/enumAutoIncrementValue.types @@ -11,6 +11,6 @@ enum E { >0 : 0 B, ->B : E.B +>B : E.A } diff --git a/testdata/baselines/reference/compiler/enumNaNValues.errors.txt b/testdata/baselines/reference/compiler/enumNaNValues.errors.txt index a1394099cd..10d8b638b2 100644 --- a/testdata/baselines/reference/compiler/enumNaNValues.errors.txt +++ b/testdata/baselines/reference/compiler/enumNaNValues.errors.txt @@ -1,20 +1,14 @@ -enumNaNValues.ts(6,7): error TS2322: Type 'E.B' is not assignable to type 'E.A'. -enumNaNValues.ts(7,7): error TS2322: Type 'E.A' is not assignable to type 'E.B'. -enumNaNValues.ts(13,7): error TS2322: Type 'F' is not assignable to type 'E.A'. +enumNaNValues.ts(13,7): error TS2322: Type 'F' is not assignable to type 'E'. -==== enumNaNValues.ts (3 errors) ==== +==== enumNaNValues.ts (1 errors) ==== enum E { A = NaN, B = NaN, } const a: E.A = E.B; - ~ -!!! error TS2322: Type 'E.B' is not assignable to type 'E.A'. const b: E.B = E.A; - ~ -!!! error TS2322: Type 'E.A' is not assignable to type 'E.B'. enum F { X = NaN, @@ -22,5 +16,5 @@ enumNaNValues.ts(13,7): error TS2322: Type 'F' is not assignable to type 'E.A'. const c: E.A = F.X; // Error expected - different enums ~ -!!! error TS2322: Type 'F' is not assignable to type 'E.A'. +!!! error TS2322: Type 'F' is not assignable to type 'E'. \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/enumNaNValues.types b/testdata/baselines/reference/compiler/enumNaNValues.types index e9684c7f81..5417c190e0 100644 --- a/testdata/baselines/reference/compiler/enumNaNValues.types +++ b/testdata/baselines/reference/compiler/enumNaNValues.types @@ -9,23 +9,23 @@ enum E { >NaN : number B = NaN, ->B : E.B +>B : E.A >NaN : number } const a: E.A = E.B; ->a : E.A +>a : E >E : any ->E.B : E.B +>E.B : E >E : typeof E ->B : E.B +>B : E const b: E.B = E.A; ->b : E.B +>b : E >E : any ->E.A : E.A +>E.A : E >E : typeof E ->A : E.A +>A : E enum F { >F : F @@ -36,7 +36,7 @@ enum F { } const c: E.A = F.X; // Error expected - different enums ->c : E.A +>c : E >E : any >F.X : F >F : typeof F diff --git a/testdata/baselines/reference/submodule/conformance/enumConstantMembers.types b/testdata/baselines/reference/submodule/conformance/enumConstantMembers.types index da805aefa1..5bddfd9872 100644 --- a/testdata/baselines/reference/submodule/conformance/enumConstantMembers.types +++ b/testdata/baselines/reference/submodule/conformance/enumConstantMembers.types @@ -79,7 +79,7 @@ enum E5 { >0.0 : 0 e = NaN, ->e : E5.e +>e : E5.d >NaN : number f = Infinity, @@ -120,7 +120,7 @@ const enum E6 { >0.0 : 0 e = NaN, ->e : E6.e +>e : E6.d >NaN : number f = Infinity, diff --git a/testdata/baselines/reference/submodule/conformance/enumConstantMembers.types.diff b/testdata/baselines/reference/submodule/conformance/enumConstantMembers.types.diff deleted file mode 100644 index d7977f8424..0000000000 --- a/testdata/baselines/reference/submodule/conformance/enumConstantMembers.types.diff +++ /dev/null @@ -1,20 +0,0 @@ ---- old.enumConstantMembers.types -+++ new.enumConstantMembers.types -@@= skipped -78, +78 lines =@@ - >0.0 : 0 - - e = NaN, -->e : E5.d -+>e : E5.e - >NaN : number - - f = Infinity, -@@= skipped -41, +41 lines =@@ - >0.0 : 0 - - e = NaN, -->e : E6.d -+>e : E6.e - >NaN : number - - f = Infinity, \ No newline at end of file