diff --git a/src/Fable.Transforms/OverloadSuffix.fs b/src/Fable.Transforms/OverloadSuffix.fs index 323543785b..ab1af0a4ac 100644 --- a/src/Fable.Transforms/OverloadSuffix.fs +++ b/src/Fable.Transforms/OverloadSuffix.fs @@ -101,11 +101,16 @@ let rec private getTypeFastFullName (genParams: IDictionary<_, _>) (t: Fable.Typ [ argType; returnType ] |> List.map (getTypeFastFullName genParams) |> String.concat " -> " - // TODO: Use Func` instead? | Fable.DelegateType(argTypes, returnType) -> - argTypes @ [ returnType ] - |> List.map (getTypeFastFullName genParams) - |> String.concat " -> " + let types = + argTypes @ [ returnType ] + |> List.map (fun t -> + let name: string = getTypeFastFullName genParams t + $"%d{name.Length}:%s{name}" + ) + |> String.concat "" + + "delegate(" + types + ")" | Fable.AnonymousRecordType(fieldNames, genArgs, isStruct) -> let fields = Seq.zip fieldNames genArgs diff --git a/src/fable-library-rust/src/System.Collections.Generic.fs b/src/fable-library-rust/src/System.Collections.Generic.fs index e4e5f79f17..2eeec2177f 100644 --- a/src/fable-library-rust/src/System.Collections.Generic.fs +++ b/src/fable-library-rust/src/System.Collections.Generic.fs @@ -11,7 +11,9 @@ type Comparer<'T>(comparison: 'T -> 'T -> int) = static member get_Default<'V when 'V: comparison>() = Comparer<'V>(LanguagePrimitives.GenericComparison) - static member Create(comparison) = Comparer<'T>(comparison) + // Same overload suffix as System.Comparison<'T>, which is what callers of Comparer.Create pass + static member Create(comparison: System.Func<'T, 'T, int>) = + Comparer<'T>(fun x y -> comparison.Invoke(x, y)) member _.Compare(x, y) = comparison x y @@ -23,8 +25,8 @@ type EqualityComparer<'T>(equals: 'T -> 'T -> bool, getHashCode: 'T -> int) = static member get_Default<'V when 'V: equality>() = EqualityComparer<'V>(LanguagePrimitives.GenericEquality, LanguagePrimitives.GenericHash) - static member Create(equals, getHashCode) = - EqualityComparer<'T>(equals, getHashCode) + static member Create(equals: System.Func<'T, 'T, bool>, getHashCode: System.Func<'T, int>) = + EqualityComparer<'T>((fun x y -> equals.Invoke(x, y)), (fun x -> getHashCode.Invoke x)) member _.Equals(x, y) = equals x y member _.GetHashCode(x) = getHashCode x diff --git a/src/fable-library-ts/System.Collections.Generic.fs b/src/fable-library-ts/System.Collections.Generic.fs index d282f67a14..191aa95d13 100644 --- a/src/fable-library-ts/System.Collections.Generic.fs +++ b/src/fable-library-ts/System.Collections.Generic.fs @@ -8,7 +8,12 @@ type Comparer<'T when 'T: comparison and 'T: null>(comparison: 'T -> 'T -> int) static member Default = Comparer<'T>(LanguagePrimitives.GenericComparison) - static member Create(comparison) = Comparer<'T>(comparison) + // Same overload suffix as System.Comparison<'T>, which is what callers of Comparer.Create pass + static member Create(comparison: System.Func<'T, 'T, int>) = + Comparer<'T>(fun x y -> comparison.Invoke(x, y)) + + // Exported for Python output compiled by older Fable versions + static member Create(comparison: 'T -> 'T -> int) = Comparer<'T>(comparison) member _.Compare(x, y) = comparison x y @@ -25,7 +30,11 @@ type EqualityComparer<'T when 'T: equality and 'T: null>(equals: 'T -> 'T -> boo static member Default = EqualityComparer<'T>(LanguagePrimitives.GenericEquality, LanguagePrimitives.GenericHash) - static member Create(equals, getHashCode) = + static member Create(equals: System.Func<'T, 'T, bool>, getHashCode: System.Func<'T, int>) = + EqualityComparer<'T>((fun x y -> equals.Invoke(x, y)), (fun x -> getHashCode.Invoke x)) + + // Exported for Python output compiled by older Fable versions + static member Create(equals: 'T -> 'T -> bool, getHashCode: 'T -> int) = EqualityComparer<'T>(equals, getHashCode) member _.Equals(x, y) = equals x y diff --git a/tests/Beam/TypeTests.fs b/tests/Beam/TypeTests.fs index 0160ed16d0..421e028285 100644 --- a/tests/Beam/TypeTests.fs +++ b/tests/Beam/TypeTests.fs @@ -9,6 +9,30 @@ type Type5Test(greeting: string) = member _.Overload(x: int) = x + x member _.Overload(x: int, y: int) = x + y +type DelegateOverloads = + static member Apply(f: int -> int, x: int) = f x + static member Apply(f: System.Func, x: int) = f.Invoke x + 100 + static member Apply(f: int -> int -> int, x: int) = f x x + static member Apply(f: System.Func, x: int) = f.Invoke(x, x) + 100 + static member Apply(f: System.Action, x: int) = + f.Invoke x + x + 200 + +type NestedDelegateOverloads = + static member C(f: System.Func -> int) = "fn:" + string (f (System.Func(fun x -> x + 1))) + static member C(f: System.Func) = "del:" + string (f.Invoke(1, 2)) + +type GenericNestedDelegateOverloads<'T> = + static member C(f: System.Func<'T, 'T> -> 'T, x: 'T) = f (System.Func<'T, 'T>(id)) + static member C(f: System.Func<'T, 'T, 'T>, x: 'T) = f.Invoke(x, x) + +type ConstrainedDelegateOverloads = + static member N<'U when 'U: comparison and 'U: null>(f: System.Func<'U, int>) = "one" + static member N<'A, 'B when 'A: comparison and 'B: null>(f: System.Func<'A, 'B, int>) = "two" + +let inline applyDelegateOverload< ^T, ^F when ^T: (static member Apply: ^F * int -> int)> (f: ^F) (x: int) = + (^T: (static member Apply: ^F * int -> int) (f, x)) + type Type6Test(x: int) = let mutable i = x member _.Value2 = i + i @@ -150,6 +174,30 @@ let ``test class constructor and property getter work`` () = let t = Type5Test("hello") equal "hello" t.Value +[] +let ``test overloads distinguished by function or delegate work`` () = + let inc: int -> int = fun x -> x + 1 + let add: int -> int -> int = fun x y -> x + y + applyDelegateOverload int> inc 1 |> equal 2 + DelegateOverloads.Apply(System.Func(fun x -> x + 1), 1) |> equal 102 + applyDelegateOverload int -> int> add 1 |> equal 2 + DelegateOverloads.Apply(System.Func(fun x y -> x + y), 1) |> equal 102 + DelegateOverloads.Apply(System.Action(ignore), 1) |> equal 201 + +[] +let ``test overloads distinguished by nested delegate work`` () = + let fn: System.Func -> int = fun g -> g.Invoke 1 + NestedDelegateOverloads.C(fn) |> equal "fn:2" + NestedDelegateOverloads.C(System.Func(fun x y -> x + y)) |> equal "del:3" + let fnG: System.Func -> int = fun g -> g.Invoke 5 + GenericNestedDelegateOverloads.C(fnG, 1) |> equal 5 + GenericNestedDelegateOverloads.C(System.Func(fun x y -> x + y), 4) |> equal 8 + +[] +let ``test overloads distinguished by constrained delegate generics work`` () = + ConstrainedDelegateOverloads.N(System.Func(fun _ -> 1)) |> equal "one" + ConstrainedDelegateOverloads.N(System.Func(fun x _ -> x)) |> equal "two" + [] let ``test class method overload with one arg works`` () = let t = Type5Test("") diff --git a/tests/Dart/src/TypeTests.fs b/tests/Dart/src/TypeTests.fs index fb6e41d578..be5f375074 100644 --- a/tests/Dart/src/TypeTests.fs +++ b/tests/Dart/src/TypeTests.fs @@ -606,8 +606,53 @@ type IdHolder(id: Id) = member _.Id = id member _.GetId() = id +type DelegateOverloads = + static member Apply(f: int -> int, x: int) = f x + static member Apply(f: System.Func, x: int) = f.Invoke x + 100 + static member Apply(f: int -> int -> int, x: int) = f x x + static member Apply(f: System.Func, x: int) = f.Invoke(x, x) + 100 + static member Apply(f: System.Action, x: int) = + f.Invoke x + x + 200 + +type NestedDelegateOverloads = + static member C(f: System.Func -> int) = "fn:" + string (f (System.Func(fun x -> x + 1))) + static member C(f: System.Func) = "del:" + string (f.Invoke(1, 2)) + +type GenericNestedDelegateOverloads<'T> = + static member C(f: System.Func<'T, 'T> -> 'T, x: 'T) = f (System.Func<'T, 'T>(id)) + static member C(f: System.Func<'T, 'T, 'T>, x: 'T) = f.Invoke(x, x) + +type ConstrainedDelegateOverloads = + static member N<'U when 'U: comparison and 'U: null>(f: System.Func<'U, int>) = "one" + static member N<'A, 'B when 'A: comparison and 'B: null>(f: System.Func<'A, 'B, int>) = "two" + +let inline applyDelegateOverload< ^T, ^F when ^T: (static member Apply: ^F * int -> int)> (f: ^F) (x: int) = + (^T: (static member Apply: ^F * int -> int) (f, x)) + let tests() = + testCase "Overloads distinguished by function or delegate work" <| fun () -> + let inc: int -> int = fun x -> x + 1 + let add: int -> int -> int = fun x y -> x + y + applyDelegateOverload int> inc 1 |> equal 2 + DelegateOverloads.Apply(System.Func(fun x -> x + 1), 1) |> equal 102 + applyDelegateOverload int -> int> add 1 |> equal 2 + DelegateOverloads.Apply(System.Func(fun x y -> x + y), 1) |> equal 102 + DelegateOverloads.Apply(System.Action(ignore), 1) |> equal 201 + + testCase "Overloads distinguished by nested delegate work" <| fun () -> + let fn: System.Func -> int = fun g -> g.Invoke 1 + NestedDelegateOverloads.C(fn) |> equal "fn:2" + NestedDelegateOverloads.C(System.Func(fun x y -> x + y)) |> equal "del:3" + let fnG: System.Func -> int = fun g -> g.Invoke 5 + GenericNestedDelegateOverloads.C(fnG, 1) |> equal 5 + GenericNestedDelegateOverloads.C(System.Func(fun x y -> x + y), 4) |> equal 8 + + testCase "Overloads distinguished by constrained delegate generics work" <| fun () -> + ConstrainedDelegateOverloads.N(System.Func(fun _ -> 1)) |> equal "one" + ConstrainedDelegateOverloads.N(System.Func(fun x _ -> x)) |> equal "two" + testCase "Unit arguments work" <| fun () -> update () |> equal ((), ()) diff --git a/tests/Js/Main/TypeTests.fs b/tests/Js/Main/TypeTests.fs index 4f8b8e5bde..d28f2e2651 100644 --- a/tests/Js/Main/TypeTests.fs +++ b/tests/Js/Main/TypeTests.fs @@ -493,6 +493,30 @@ type Distinct1 = static member OfList(_elements: list<'K * 'V>) = () static member OfList(_elements: list) = () +type DelegateOverloads = + static member Apply(f: int -> int, x: int) = f x + static member Apply(f: System.Func, x: int) = f.Invoke x + 100 + static member Apply(f: int -> int -> int, x: int) = f x x + static member Apply(f: System.Func, x: int) = f.Invoke(x, x) + 100 + static member Apply(f: System.Action, x: int) = + f.Invoke x + x + 200 + +type NestedDelegateOverloads = + static member C(f: System.Func -> int) = "fn:" + string (f (System.Func(fun x -> x + 1))) + static member C(f: System.Func) = "del:" + string (f.Invoke(1, 2)) + +type GenericNestedDelegateOverloads<'T> = + static member C(f: System.Func<'T, 'T> -> 'T, x: 'T) = f (System.Func<'T, 'T>(id)) + static member C(f: System.Func<'T, 'T, 'T>, x: 'T) = f.Invoke(x, x) + +type ConstrainedDelegateOverloads = + static member N<'U when 'U: comparison and 'U: null>(f: System.Func<'U, int>) = "one" + static member N<'A, 'B when 'A: comparison and 'B: null>(f: System.Func<'A, 'B, int>) = "two" + +let inline applyDelegateOverload< ^T, ^F when ^T: (static member Apply: ^F * int -> int)> (f: ^F) (x: int) = + (^T: (static member Apply: ^F * int -> int) (f, x)) + type FlexibleOverloads() = member _.For(xs: 'a list, f: 'a -> 'b list) : 'b list = xs |> List.collect f member _.For(xs: #seq<'a>, f: 'a -> unit) : int = @@ -1218,6 +1242,27 @@ let tests = t.For([|1; 2; 3|], fun i -> sum <- sum + i) |> equal 3 sum |> equal 6 + testCase "Overloads distinguished by function or delegate work" <| fun () -> + let inc: int -> int = fun x -> x + 1 + let add: int -> int -> int = fun x y -> x + y + applyDelegateOverload int> inc 1 |> equal 2 + DelegateOverloads.Apply(System.Func(fun x -> x + 1), 1) |> equal 102 + applyDelegateOverload int -> int> add 1 |> equal 2 + DelegateOverloads.Apply(System.Func(fun x y -> x + y), 1) |> equal 102 + DelegateOverloads.Apply(System.Action(ignore), 1) |> equal 201 + + testCase "Overloads distinguished by nested delegate work" <| fun () -> + let fn: System.Func -> int = fun g -> g.Invoke 1 + NestedDelegateOverloads.C(fn) |> equal "fn:2" + NestedDelegateOverloads.C(System.Func(fun x y -> x + y)) |> equal "del:3" + let fnG: System.Func -> int = fun g -> g.Invoke 5 + GenericNestedDelegateOverloads.C(fnG, 1) |> equal 5 + GenericNestedDelegateOverloads.C(System.Func(fun x y -> x + y), 4) |> equal 8 + + testCase "Overloads distinguished by constrained delegate generics work" <| fun () -> + ConstrainedDelegateOverloads.N(System.Func(fun _ -> 1)) |> equal "one" + ConstrainedDelegateOverloads.N(System.Func(fun x _ -> x)) |> equal "two" + testCase "Type abbreviation works" <| fun () -> let t = T4() t.Value2 |> equal "Bye" diff --git a/tests/Python/TestMisc.fs b/tests/Python/TestMisc.fs index 2e7d545f41..8cec2f880a 100644 --- a/tests/Python/TestMisc.fs +++ b/tests/Python/TestMisc.fs @@ -1166,6 +1166,26 @@ module private PythonExceptionBases = [] type TimeoutError() = inherit Exception() +module private LegacyLibraryExports = + open Fable.Core.PyInterop + + let comparerCreate: Func, System.Collections.Generic.IComparer> = + import "Comparer_1_Create_47C913C" "fable_library.system_collections_generic" + + let equalityComparerCreate: Func, Func, System.Collections.Generic.IEqualityComparer> = + import "EqualityComparer_1_Create_Z6EE254AB" "fable_library.system_collections_generic" + +[] +let ``test Comparer.Create exports used by older compilers still work`` () = + let comparer = LegacyLibraryExports.comparerCreate.Invoke(Func(fun x y -> compare y x)) + comparer.Compare(1, 2) |> equal 1 + + let equalityComparer = + LegacyLibraryExports.equalityComparerCreate.Invoke(Func(fun x y -> x <> y), Func(fun x -> x + 1)) + + equalityComparer.Equals(1, 1) |> equal false + equalityComparer.GetHashCode(1) |> equal 2 + [] let ``test Python ExceptionBase compatibility import preserves class identity`` () = obj.ReferenceEquals(PythonExceptionBases.exceptionBase, PythonExceptionBases.legacyExceptionBase) |> equal true diff --git a/tests/Python/TestType.fs b/tests/Python/TestType.fs index a2eef539b1..171ab6a15a 100644 --- a/tests/Python/TestType.fs +++ b/tests/Python/TestType.fs @@ -423,6 +423,30 @@ type Distinct1 = static member OfList(_elements: list<'K * 'V>) = () static member OfList(_elements: list) = () +type DelegateOverloads = + static member Apply(f: int -> int, x: int) = f x + static member Apply(f: System.Func, x: int) = f.Invoke x + 100 + static member Apply(f: int -> int -> int, x: int) = f x x + static member Apply(f: System.Func, x: int) = f.Invoke(x, x) + 100 + static member Apply(f: System.Action, x: int) = + f.Invoke x + x + 200 + +type NestedDelegateOverloads = + static member C(f: System.Func -> int) = "fn:" + string (f (System.Func(fun x -> x + 1))) + static member C(f: System.Func) = "del:" + string (f.Invoke(1, 2)) + +type GenericNestedDelegateOverloads<'T> = + static member C(f: System.Func<'T, 'T> -> 'T, x: 'T) = f (System.Func<'T, 'T>(id)) + static member C(f: System.Func<'T, 'T, 'T>, x: 'T) = f.Invoke(x, x) + +type ConstrainedDelegateOverloads = + static member N<'U when 'U: comparison and 'U: null>(f: System.Func<'U, int>) = "one" + static member N<'A, 'B when 'A: comparison and 'B: null>(f: System.Func<'A, 'B, int>) = "two" + +let inline applyDelegateOverload< ^T, ^F when ^T: (static member Apply: ^F * int -> int)> (f: ^F) (x: int) = + (^T: (static member Apply: ^F * int -> int) (f, x)) + type InfoA = { Foo: string } type InfoB = { InfoA: InfoA; Bar: string } @@ -1017,6 +1041,30 @@ let ``test Overloads work`` () = t.Overload(2) |> equal 4 t.Overload(2, 3) |> equal 5 +[] +let ``test Overloads distinguished by function or delegate work`` () = + let inc: int -> int = fun x -> x + 1 + let add: int -> int -> int = fun x y -> x + y + applyDelegateOverload int> inc 1 |> equal 2 + DelegateOverloads.Apply(System.Func(fun x -> x + 1), 1) |> equal 102 + applyDelegateOverload int -> int> add 1 |> equal 2 + DelegateOverloads.Apply(System.Func(fun x y -> x + y), 1) |> equal 102 + DelegateOverloads.Apply(System.Action(ignore), 1) |> equal 201 + +[] +let ``test Overloads distinguished by nested delegate work`` () = + let fn: System.Func -> int = fun g -> g.Invoke 1 + NestedDelegateOverloads.C(fn) |> equal "fn:2" + NestedDelegateOverloads.C(System.Func(fun x y -> x + y)) |> equal "del:3" + let fnG: System.Func -> int = fun g -> g.Invoke 5 + GenericNestedDelegateOverloads.C(fnG, 1) |> equal 5 + GenericNestedDelegateOverloads.C(System.Func(fun x y -> x + y), 4) |> equal 8 + +[] +let ``test Overloads distinguished by constrained delegate generics work`` () = + ConstrainedDelegateOverloads.N(System.Func(fun _ -> 1)) |> equal "one" + ConstrainedDelegateOverloads.N(System.Func(fun x _ -> x)) |> equal "two" + [] let ``test Type abbreviation works`` () = let t = T4() diff --git a/tests/Rust/tests/src/TypeTests.fs b/tests/Rust/tests/src/TypeTests.fs index 42e093d1da..b4b23ccbd1 100644 --- a/tests/Rust/tests/src/TypeTests.fs +++ b/tests/Rust/tests/src/TypeTests.fs @@ -42,6 +42,30 @@ type TestType5(greeting: string) = member _.Overload(x) = x + x member _.Overload(x, y) = x + y +type DelegateOverloads = + static member Apply(f: int -> int, x: int) = f x + static member Apply(f: System.Func, x: int) = f.Invoke x + 100 + static member Apply(f: int -> int -> int, x: int) = f x x + static member Apply(f: System.Func, x: int) = f.Invoke(x, x) + 100 + static member Apply(f: System.Action, x: int) = + f.Invoke x + x + 200 + +type NestedDelegateOverloads = + static member C(f: System.Func -> int) = "fn:" + string (f (System.Func(fun x -> x + 1))) + static member C(f: System.Func) = "del:" + string (f.Invoke(1, 2)) + +type GenericNestedDelegateOverloads<'T> = + static member C(f: System.Func<'T, 'T> -> 'T, x: 'T) = f (System.Func<'T, 'T>(id)) + static member C(f: System.Func<'T, 'T, 'T>, x: 'T) = f.Invoke(x, x) + +type ConstrainedDelegateOverloads = + static member N<'U when 'U: comparison and 'U: null>(f: System.Func<'U, int>) = "one" + static member N<'A, 'B when 'A: comparison and 'B: null>(f: System.Func<'A, 'B, int>) = "two" + +let inline applyDelegateOverload< ^T, ^F when ^T: (static member Apply: ^F * int -> int)> (f: ^F) (x: int) = + (^T: (static member Apply: ^F * int -> int) (f, x)) + type TestType8(?greeting) = member _.Value = defaultArg greeting "Hi" @@ -1086,6 +1110,30 @@ let ``Property names don't clash with built-in JS objects`` () = } gameState.Players.ContainsKey(1) |> equal false +[] +let ``Overloads distinguished by function or delegate work`` () = + let inc: int -> int = fun x -> x + 1 + let add: int -> int -> int = fun x y -> x + y + applyDelegateOverload int> inc 1 |> equal 2 + DelegateOverloads.Apply(System.Func(fun x -> x + 1), 1) |> equal 102 + applyDelegateOverload int -> int> add 1 |> equal 2 + DelegateOverloads.Apply(System.Func(fun x y -> x + y), 1) |> equal 102 + DelegateOverloads.Apply(System.Action(ignore), 1) |> equal 201 + +[] +let ``Overloads distinguished by nested delegate work`` () = + let fn: System.Func -> int = fun g -> g.Invoke 1 + NestedDelegateOverloads.C(fn) |> equal "fn:2" + NestedDelegateOverloads.C(System.Func(fun x y -> x + y)) |> equal "del:3" + let fnG: System.Func -> int = fun g -> g.Invoke 5 + GenericNestedDelegateOverloads.C(fnG, 1) |> equal 5 + GenericNestedDelegateOverloads.C(System.Func(fun x y -> x + y), 4) |> equal 8 + +[] +let ``Overloads distinguished by constrained delegate generics work`` () = + ConstrainedDelegateOverloads.N(System.Func(fun _ -> 1)) |> equal "one" + ConstrainedDelegateOverloads.N(System.Func(fun x _ -> x)) |> equal "two" + [] let ``Overloads work`` () = let t = TestType5("")