Skip to content

Commit 60a0c88

Browse files
github-actions[bot]Copilotdbrattliclaude
authored
[Repo Assist] [JS/TS/Python/Beam] Fix ResizeArray equality to use reference equality (fixes #3718) (#4524)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Dag Brattli <dag@brattli.net> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 267054e commit 60a0c88

9 files changed

Lines changed: 54 additions & 5 deletions

File tree

src/Fable.Cli/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
* [JS/TS/Python/Beam] Fix `ResizeArray` (`System.Collections.Generic.List`) equality to use reference equality instead of structural equality (fixes #3718)
1213
* [Beam] Fix `List.Cons` call replacement and test (by @ncave)
1314
* [Beam/Dart/Python/TypeScript] Fix `Array.Equals` to use reference equality instead of structural equality (by @ncave)
1415
* [Dart/Python/TypeScript/Rust] Fix `Seq.foldBack2` for sequences with different lengths (by @ncave)

src/Fable.Compiler/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
* [JS/TS/Python/Beam] Fix `ResizeArray` (`System.Collections.Generic.List`) equality to use reference equality instead of structural equality (fixes #3718)
1213
* [Beam] Fix `List.Cons` call replacement and test (by @ncave)
1314
* [Beam/Dart/Python/TypeScript] Fix `Array.Equals` to use reference equality instead of structural equality (by @ncave)
1415
* [Dart/Python/TypeScript/Rust] Fix `Seq.foldBack2` for sequences with different lengths (by @ncave)

src/Fable.Transforms/Beam/Replacements.fs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,19 @@ type Context = FSharp2Fable.Context
1111
type ICompiler = FSharp2Fable.IFableCompiler
1212
type CallInfo = ReplaceCallInfo
1313

14+
let private physicalEquals r (left: Expr) (right: Expr) =
15+
emitExpr r Boolean [ left; right ] "($0 =:= $1)"
16+
1417
// Use fable_comparison:equals/2 for structural equality.
1518
// This handles ref-wrapped arrays (process dict refs) by dereferencing
1619
// before comparison, so structural equality works correctly.
1720
let private equals (com: ICompiler) r equal (left: Expr) (right: Expr) =
1821
let eqCall =
19-
Helper.LibCall(com, "fable_comparison", "equals", Boolean, [ left; right ], ?loc = r)
22+
match left.Type with
23+
// ResizeArray (System.Collections.Generic.List) uses reference equality in .NET, see #3718.
24+
// Two distinct refs are never =:=, so this matches .NET semantics directly.
25+
| Array(_, ResizeArray) -> physicalEquals r left right
26+
| _ -> Helper.LibCall(com, "fable_comparison", "equals", Boolean, [ left; right ], ?loc = r)
2027

2128
if equal then
2229
eqCall
@@ -26,9 +33,6 @@ let private equals (com: ICompiler) r equal (left: Expr) (right: Expr) =
2633
let private compare (com: ICompiler) r (left: Expr) (right: Expr) =
2734
Helper.LibCall(com, "fable_comparison", "compare", Number(Int32, NumberInfo.Empty), [ left; right ], ?loc = r)
2835

29-
let private physicalEquals r (left: Expr) (right: Expr) =
30-
emitExpr r Boolean [ left; right ] "($0 =:= $1)"
31-
3236
/// Deref an array ref to its underlying list (for passing to list BIFs).
3337
/// Byte arrays (UInt8) are atomics — convert to list via fable_utils:byte_array_to_list.
3438
let private derefArr r (expr: Expr) =

src/Fable.Transforms/Python/Replacements.fs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,16 @@ let rec equals (com: ICompiler) ctx r equal (left: Expr) (right: Expr) =
620620
| DeclaredType _ ->
621621
Helper.LibCall(com, "util", "equals", Boolean, [ left; right ], ?loc = r)
622622
|> is equal
623+
| Array(_, ResizeArray) ->
624+
// ResizeArray (System.Collections.Generic.List) uses reference equality in .NET, see #3718
625+
makeEqOpStrict
626+
r
627+
left
628+
right
629+
(if equal then
630+
BinaryEqual
631+
else
632+
BinaryUnequal)
623633
| Array(t, _) ->
624634
let f = makeEqualityFunction com ctx t
625635

src/Fable.Transforms/Replacements.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,12 @@ let rec equals (com: ICompiler) ctx r equal (left: Expr) (right: Expr) =
566566
| DeclaredType _ ->
567567
Helper.LibCall(com, "Util", "equals", Boolean, [ left; right ], ?loc = r)
568568
|> is equal
569+
| Array(_, ResizeArray) ->
570+
// ResizeArray (System.Collections.Generic.List) uses reference equality in .NET, see #3718
571+
if equal then
572+
makeBinOp r Boolean left right BinaryEqual
573+
else
574+
makeBinOp r Boolean left right BinaryUnequal
569575
| Array(t, _) ->
570576
let f = makeEqualityFunction com ctx t
571577

src/fable-library-ts/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
* [JS/TS] Fix `ResizeArray` (`System.Collections.Generic.List`) equality to use reference equality instead of structural equality (fixes #3718)
1213
* [JS/TS] Fix `String.Contains` ignoring `StringComparison` argument (second argument was silently discarded)
1314

1415
## 2.0.0-rc.6 - 2026-04-07

tests/Beam/ResizeArrayTests.fs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,3 +348,12 @@ let ``test ResizeArray works with Seq.item`` () =
348348
equal 2 secondItem
349349
let thirdItem = Seq.item 2 li
350350
equal 3 thirdItem
351+
352+
[<Fact>]
353+
let ``test ResizeArray uses reference equality not structural equality`` () = // See #3718
354+
let xs = ResizeArray [0; 1; 2]
355+
let ys = ResizeArray [0; 1; 2]
356+
equal false (xs = ys)
357+
equal true (xs = xs)
358+
equal true (xs <> ys)
359+
equal false (xs <> xs)

tests/Js/Main/ResizeArrayTests.fs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,4 +376,12 @@ let tests =
376376
let xs = ResizeArray [1; 2; 3]
377377
throwsAnyError (fun () -> xs.[-1] <- 42)
378378
throwsAnyError (fun () -> xs.[10] <- 42)
379+
380+
testCase "ResizeArray uses reference equality not structural equality" <| fun _ -> // See #3718
381+
let xs = ResizeArray [0; 1; 2]
382+
let ys = ResizeArray [0; 1; 2]
383+
equal false (xs = ys)
384+
equal true (xs = xs)
385+
equal true (xs <> ys)
386+
equal false (xs <> xs)
379387
]

tests/Python/TestResizeArray.fs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,4 +347,13 @@ let ``test ResizeArray works with Seq.item`` () =
347347
let secondItem = Seq.item 1 li
348348
equal 2 secondItem
349349
let thirdItem = Seq.item 2 li
350-
equal 3 thirdItem
350+
equal 3 thirdItem
351+
352+
[<Fact>]
353+
let ``test ResizeArray uses reference equality not structural equality`` () = // See #3718
354+
let xs = ResizeArray [0; 1; 2]
355+
let ys = ResizeArray [0; 1; 2]
356+
equal false (xs = ys)
357+
equal true (xs = xs)
358+
equal true (xs <> ys)
359+
equal false (xs <> xs)

0 commit comments

Comments
 (0)