Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Rust] Added support for generic comparers #3703

Merged
merged 1 commit into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
#### Javascript

* Fixed 'System.Collections.Generic.Queue' bug (by @PierreYvesR)
* Fixed instance calls for generic comparers (by @ncave)

#### Python

Expand All @@ -30,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Fixed generic try_catch closure trait (by @ncave)
* Fixed `self` arg capture in methods (by @ncave)
* Fixed 'System.Collections.Generic.Queue' bug (by @PierreYvesR)
* Added support for generic comparers (by @ncave)

## 4.9.0 - 2023-12-14

Expand Down
15 changes: 11 additions & 4 deletions src/Fable.Transforms/Rust/Fable2Rust.fs
Original file line number Diff line number Diff line change
Expand Up @@ -980,12 +980,19 @@ module TypeInfo =
genArgs
: Rust.Ty
=
let nameParts =
getAbstractClassImportName com ctx entRef |> splitNameParts

let entName = getAbstractClassImportName com ctx entRef
let nameParts = entName |> splitNameParts
let genArgsOpt = transformGenArgs com ctx genArgs
let traitBound = mkTypeTraitGenericBound nameParts genArgsOpt
mkDynTraitTy [ traitBound ]

match entRef.FullName with
| "System.Collections.Generic.Comparer`1"
| "System.Collections.Generic.EqualityComparer`1" ->
// some abstract classes are implemented as non-abstract
makeFullNamePathTy entName genArgsOpt
| _ ->
// abstract classes implemented as interfaces
mkDynTraitTy [ traitBound ]

let (|HasEmitAttribute|_|) (ent: Fable.Entity) =
ent.Attributes
Expand Down
40 changes: 30 additions & 10 deletions src/fable-library-rust/src/System.Collections.Generic.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,36 @@ namespace System.Collections.Generic

open Global_

// type Comparer<'T when 'T: comparison>() =
// static member Default = Comparer<'T>()
// interface IComparer<'T> with
// member _.Compare(x, y) = LanguagePrimitives.GenericComparison x y

// type EqualityComparer<'T when 'T: equality>() =
// static member Default = EqualityComparer<'T>()
// interface IEqualityComparer<'T> with
// member _.Equals(x, y) = LanguagePrimitives.GenericEquality x y
// member _.GetHashCode(x) = LanguagePrimitives.GenericHash x
type Comparer<'T when 'T: comparison>(comparison: 'T -> 'T -> int) =

static member Default = Comparer<'T>(LanguagePrimitives.GenericComparison)

static member Create(comparison) = Comparer<'T>(comparison)

member _.Compare(x, y) = comparison x y

interface IComparer<'T> with
member _.Compare(x, y) = comparison x y

type EqualityComparer<'T when 'T: equality>
(equals: 'T -> 'T -> bool, getHashCode: 'T -> int)
=

static member Default =
EqualityComparer<'T>(
LanguagePrimitives.GenericEquality,
LanguagePrimitives.GenericHash
)

static member Create(equals, getHashCode) =
EqualityComparer<'T>(equals, getHashCode)

member _.Equals(x, y) = equals x y
member _.GetHashCode(x) = getHashCode x

interface IEqualityComparer<'T> with
member _.Equals(x, y) = equals x y
member _.GetHashCode(x) = getHashCode x

type Stack<'T when 'T: equality> private (initialContents: 'T[], initialCount) =
let mutable contents = initialContents
Expand Down
21 changes: 16 additions & 5 deletions tests/Js/Main/ComparisonTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,17 @@
x - 2 <= y && y <= x + 2
| _ -> false

let genericEquals (a:'T) (b:'T) : bool =
let genericEquals (a: 'T) (b: 'T) : bool =
let cmp = EqualityComparer<'T>.Default
cmp.Equals(a,b)
cmp.Equals(a, b)

let genericHash (x:'T) : int =
let genericHash (x: 'T) : int =
let cmp = EqualityComparer<'T>.Default
cmp.GetHashCode(x)

let genericCompare (a:'T) (b:'T) : int =
let genericCompare (a: 'T) (b: 'T) : int =
let cmp = Comparer<'T>.Default
cmp.Compare(a,b)
cmp.Compare(a, b)

let tests =
testList "Comparison" [
Expand Down Expand Up @@ -714,6 +714,11 @@
let distance: decimal<m> = LanguagePrimitives.DecimalWithMeasure 1.0m
distance |> equal 1.0m<m>

testCase "EqualityComparer.Create works" <| fun () ->
let cmp = EqualityComparer<'T>.Create((<>), hash)
cmp.Equals(1, 1) |> equal false

Check warning on line 719 in tests/Js/Main/ComparisonTests.fs

View workflow job for this annotation

GitHub Actions / build-javascript (ubuntu-latest)

This construct causes code to be less generic than indicated by the type annotations. The type variable 'T has been constrained to be type 'int'.

Check warning on line 719 in tests/Js/Main/ComparisonTests.fs

View workflow job for this annotation

GitHub Actions / build-javascript (windows-latest)

This construct causes code to be less generic than indicated by the type annotations. The type variable 'T has been constrained to be type 'int'.
cmp.Equals(1, 2) |> equal true

testCase "EqualityComparer.Equals works" <| fun () ->
genericEquals 1 1 |> equal true
genericEquals 1 2 |> equal false
Expand All @@ -729,4 +734,10 @@
genericCompare 1 2 |> equal -1
genericCompare 2 1 |> equal 1

testCase "Comparer.Create works" <| fun () ->
let cmp = Comparer<'T>.Create(fun x y -> -(compare x y))
cmp.Compare(1, 1) |> equal 0
cmp.Compare(1, 2) |> equal 1
cmp.Compare(2, 1) |> equal -1

]
61 changes: 37 additions & 24 deletions tests/Rust/tests/src/ComparisonTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,17 @@
// x - 2 <= y && y <= x + 2
// | _ -> false

// let genericEquals (a:'T) (b:'T) : bool =
// let cmp = EqualityComparer<'T>.Default
// cmp.Equals(a,b)
let genericEquals<'T when 'T: equality> (a: 'T) (b: 'T) : bool =
let cmp = EqualityComparer<'T>.Default
cmp.Equals(a, b)

// let genericHash (x:'T) : int =
// let cmp = EqualityComparer<'T>.Default
// cmp.GetHashCode(x)
let genericHash<'T when 'T: equality> (x: 'T) : int =
let cmp = EqualityComparer<'T>.Default
cmp.GetHashCode(x)

// let genericCompare (a:'T) (b:'T) : int =
// let cmp = Comparer<'T>.Default
// cmp.Compare(a,b)
let genericCompare<'T when 'T: comparison> (a: 'T) (b: 'T) : int =
let cmp = Comparer<'T>.Default
cmp.Compare(a, b)

[<Fact>]
let ``Typed array equality works`` () =
Expand Down Expand Up @@ -761,20 +761,33 @@
let distance: decimal<m> = LanguagePrimitives.DecimalWithMeasure 1.0m
distance |> equal 1.0m<m>

// [<Fact>]
// let ``EqualityComparer.Equals works`` () =
// genericEquals 1 1 |> equal true
// genericEquals 1 2 |> equal false
// genericEquals "1" "1" |> equal true
// genericEquals "1" "2" |> equal false
[<Fact>]
let ``EqualityComparer.Create works`` () =
let cmp = EqualityComparer<'T>.Create((<>), hash)
cmp.Equals(1, 1) |> equal false

Check warning on line 767 in tests/Rust/tests/src/ComparisonTests.fs

View workflow job for this annotation

GitHub Actions / build-rust (default)

This construct causes code to be less generic than indicated by the type annotations. The type variable 'T has been constrained to be type 'int'.

Check warning on line 767 in tests/Rust/tests/src/ComparisonTests.fs

View workflow job for this annotation

GitHub Actions / build-rust (no_std)

This construct causes code to be less generic than indicated by the type annotations. The type variable 'T has been constrained to be type 'int'.

Check warning on line 767 in tests/Rust/tests/src/ComparisonTests.fs

View workflow job for this annotation

GitHub Actions / build-rust (threaded)

This construct causes code to be less generic than indicated by the type annotations. The type variable 'T has been constrained to be type 'int'.
cmp.Equals(1, 2) |> equal true

// [<Fact>]
// let ``EqualityComparer.GetHashCode works`` () =
// genericHash 1 |> equal ((1).GetHashCode())
// genericHash "1" |> equal ("1".GetHashCode())
[<Fact>]
let ``EqualityComparer.Equals works`` () =
genericEquals 1 1 |> equal true
genericEquals 1 2 |> equal false
genericEquals "1" "1" |> equal true
genericEquals "1" "2" |> equal false

// [<Fact>]
// let ``Comparer.Compare works`` () =
// genericCompare 1 1 |> equal 0
// genericCompare 1 2 |> equal -1
// genericCompare 2 1 |> equal 1
[<Fact>]
let ``EqualityComparer.GetHashCode works`` () =
genericHash 1 |> equal ((1).GetHashCode())
genericHash "1" |> equal ("1".GetHashCode())

[<Fact>]
let ``Comparer.Compare works`` () =
genericCompare 1 1 |> equal 0
genericCompare 1 2 |> equal -1
genericCompare 2 1 |> equal 1

[<Fact>]
let ``Comparer.Create works`` () =
let cmp = Comparer<'T>.Create(fun x y -> -(compare x y))
cmp.Compare(1, 1) |> equal 0

Check warning on line 791 in tests/Rust/tests/src/ComparisonTests.fs

View workflow job for this annotation

GitHub Actions / build-rust (default)

This construct causes code to be less generic than indicated by the type annotations. The type variable 'T has been constrained to be type 'int'.

Check warning on line 791 in tests/Rust/tests/src/ComparisonTests.fs

View workflow job for this annotation

GitHub Actions / build-rust (no_std)

This construct causes code to be less generic than indicated by the type annotations. The type variable 'T has been constrained to be type 'int'.

Check warning on line 791 in tests/Rust/tests/src/ComparisonTests.fs

View workflow job for this annotation

GitHub Actions / build-rust (threaded)

This construct causes code to be less generic than indicated by the type annotations. The type variable 'T has been constrained to be type 'int'.
cmp.Compare(1, 2) |> equal 1
cmp.Compare(2, 1) |> equal -1
Loading