From 3d5b3b50d002820de301ca12557db74c9698b7cd Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Sat, 7 Nov 2026 00:26:30 +0100 Subject: [PATCH 1/3] fix(all): distinguish delegates in overload suffixes --- src/Fable.Transforms/OverloadSuffix.fs | 8 +++---- .../src/System.Collections.Generic.fs | 8 ++++--- .../System.Collections.Generic.fs | 8 ++++--- tests/Beam/TypeTests.fs | 22 +++++++++++++++++++ tests/Dart/src/TypeTests.fs | 21 ++++++++++++++++++ tests/Js/Main/TypeTests.fs | 21 ++++++++++++++++++ tests/Python/TestType.fs | 22 +++++++++++++++++++ tests/Rust/tests/src/TypeTests.fs | 22 +++++++++++++++++++ 8 files changed, 122 insertions(+), 10 deletions(-) diff --git a/src/Fable.Transforms/OverloadSuffix.fs b/src/Fable.Transforms/OverloadSuffix.fs index 323543785b..6474215bf0 100644 --- a/src/Fable.Transforms/OverloadSuffix.fs +++ b/src/Fable.Transforms/OverloadSuffix.fs @@ -101,11 +101,11 @@ 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 " -> " + "delegate " + + (argTypes @ [ returnType ] + |> List.map (getTypeFastFullName genParams) + |> String.concat " -> ") | 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..5e4e94ffaa 100644 --- a/src/fable-library-ts/System.Collections.Generic.fs +++ b/src/fable-library-ts/System.Collections.Generic.fs @@ -8,7 +8,9 @@ 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)) member _.Compare(x, y) = comparison x y @@ -25,8 +27,8 @@ 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) = - 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 diff --git a/tests/Beam/TypeTests.fs b/tests/Beam/TypeTests.fs index 0160ed16d0..ca18d731c1 100644 --- a/tests/Beam/TypeTests.fs +++ b/tests/Beam/TypeTests.fs @@ -9,6 +9,18 @@ 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 + +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 +162,16 @@ 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 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..6dcf215296 100644 --- a/tests/Dart/src/TypeTests.fs +++ b/tests/Dart/src/TypeTests.fs @@ -606,8 +606,29 @@ 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 + +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 "Unit arguments work" <| fun () -> update () |> equal ((), ()) diff --git a/tests/Js/Main/TypeTests.fs b/tests/Js/Main/TypeTests.fs index 4f8b8e5bde..56bf009e2f 100644 --- a/tests/Js/Main/TypeTests.fs +++ b/tests/Js/Main/TypeTests.fs @@ -493,6 +493,18 @@ 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 + +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 +1230,15 @@ 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 "Type abbreviation works" <| fun () -> let t = T4() t.Value2 |> equal "Bye" diff --git a/tests/Python/TestType.fs b/tests/Python/TestType.fs index a2eef539b1..4eae846580 100644 --- a/tests/Python/TestType.fs +++ b/tests/Python/TestType.fs @@ -423,6 +423,18 @@ 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 + +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 +1029,16 @@ 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 Type abbreviation works`` () = let t = T4() diff --git a/tests/Rust/tests/src/TypeTests.fs b/tests/Rust/tests/src/TypeTests.fs index 42e093d1da..15671bb3bc 100644 --- a/tests/Rust/tests/src/TypeTests.fs +++ b/tests/Rust/tests/src/TypeTests.fs @@ -42,6 +42,18 @@ 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 + +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 +1098,16 @@ 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 work`` () = let t = TestType5("") From e4c23e6e30ac7c0ecfefb45f2765df5f7d70f44e Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Sat, 7 Nov 2026 01:41:26 +0100 Subject: [PATCH 2/3] fix(all): keep old comparer exports, delimit delegate hash --- src/Fable.Transforms/OverloadSuffix.fs | 10 ++++++---- .../System.Collections.Generic.fs | 7 +++++++ tests/Beam/TypeTests.fs | 17 ++++++++++++++++ tests/Dart/src/TypeTests.fs | 16 +++++++++++++++ tests/Js/Main/TypeTests.fs | 16 +++++++++++++++ tests/Python/TestMisc.fs | 20 +++++++++++++++++++ tests/Python/TestType.fs | 17 ++++++++++++++++ tests/Rust/tests/src/TypeTests.fs | 17 ++++++++++++++++ 8 files changed, 116 insertions(+), 4 deletions(-) diff --git a/src/Fable.Transforms/OverloadSuffix.fs b/src/Fable.Transforms/OverloadSuffix.fs index 6474215bf0..61173513e7 100644 --- a/src/Fable.Transforms/OverloadSuffix.fs +++ b/src/Fable.Transforms/OverloadSuffix.fs @@ -102,10 +102,12 @@ let rec private getTypeFastFullName (genParams: IDictionary<_, _>) (t: Fable.Typ |> List.map (getTypeFastFullName genParams) |> String.concat " -> " | Fable.DelegateType(argTypes, returnType) -> - "delegate " - + (argTypes @ [ returnType ] - |> List.map (getTypeFastFullName genParams) - |> String.concat " -> ") + let types = + argTypes @ [ returnType ] + |> List.map (getTypeFastFullName genParams) + |> String.concat "," + + "delegate(" + types + ")" | Fable.AnonymousRecordType(fieldNames, genArgs, isStruct) -> let fields = Seq.zip fieldNames genArgs diff --git a/src/fable-library-ts/System.Collections.Generic.fs b/src/fable-library-ts/System.Collections.Generic.fs index 5e4e94ffaa..191aa95d13 100644 --- a/src/fable-library-ts/System.Collections.Generic.fs +++ b/src/fable-library-ts/System.Collections.Generic.fs @@ -12,6 +12,9 @@ type Comparer<'T when 'T: comparison and 'T: null>(comparison: 'T -> 'T -> int) 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 interface IComparer<'T> with @@ -30,6 +33,10 @@ type EqualityComparer<'T when 'T: equality and 'T: null>(equals: 'T -> 'T -> boo 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 member _.GetHashCode(x) = getHashCode x diff --git a/tests/Beam/TypeTests.fs b/tests/Beam/TypeTests.fs index ca18d731c1..990ca45434 100644 --- a/tests/Beam/TypeTests.fs +++ b/tests/Beam/TypeTests.fs @@ -18,6 +18,14 @@ type DelegateOverloads = 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) + 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)) @@ -172,6 +180,15 @@ let ``test overloads distinguished by function or delegate work`` () = 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 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 6dcf215296..403f3501fb 100644 --- a/tests/Dart/src/TypeTests.fs +++ b/tests/Dart/src/TypeTests.fs @@ -615,6 +615,14 @@ type DelegateOverloads = 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) + 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)) @@ -629,6 +637,14 @@ let tests() = 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 "Unit arguments work" <| fun () -> update () |> equal ((), ()) diff --git a/tests/Js/Main/TypeTests.fs b/tests/Js/Main/TypeTests.fs index 56bf009e2f..e759189f38 100644 --- a/tests/Js/Main/TypeTests.fs +++ b/tests/Js/Main/TypeTests.fs @@ -502,6 +502,14 @@ type DelegateOverloads = 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) + 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)) @@ -1239,6 +1247,14 @@ let tests = 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 "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 4eae846580..f695c0a4f8 100644 --- a/tests/Python/TestType.fs +++ b/tests/Python/TestType.fs @@ -432,6 +432,14 @@ type DelegateOverloads = 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) + 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)) @@ -1039,6 +1047,15 @@ let ``test Overloads distinguished by function or delegate work`` () = 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 Type abbreviation works`` () = let t = T4() diff --git a/tests/Rust/tests/src/TypeTests.fs b/tests/Rust/tests/src/TypeTests.fs index 15671bb3bc..20d286e239 100644 --- a/tests/Rust/tests/src/TypeTests.fs +++ b/tests/Rust/tests/src/TypeTests.fs @@ -51,6 +51,14 @@ type DelegateOverloads = 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) + 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)) @@ -1108,6 +1116,15 @@ let ``Overloads distinguished by function or delegate work`` () = 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 work`` () = let t = TestType5("") From 68f9051607f6bd4ff42fc91c7f8614440fce2e7a Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Sat, 7 Nov 2026 02:19:21 +0100 Subject: [PATCH 3/3] fix(all): length-prefix delegate overload hash elements --- src/Fable.Transforms/OverloadSuffix.fs | 7 +++++-- tests/Beam/TypeTests.fs | 9 +++++++++ tests/Dart/src/TypeTests.fs | 8 ++++++++ tests/Js/Main/TypeTests.fs | 8 ++++++++ tests/Python/TestType.fs | 9 +++++++++ tests/Rust/tests/src/TypeTests.fs | 9 +++++++++ 6 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Fable.Transforms/OverloadSuffix.fs b/src/Fable.Transforms/OverloadSuffix.fs index 61173513e7..ab1af0a4ac 100644 --- a/src/Fable.Transforms/OverloadSuffix.fs +++ b/src/Fable.Transforms/OverloadSuffix.fs @@ -104,8 +104,11 @@ let rec private getTypeFastFullName (genParams: IDictionary<_, _>) (t: Fable.Typ | Fable.DelegateType(argTypes, returnType) -> let types = argTypes @ [ returnType ] - |> List.map (getTypeFastFullName genParams) - |> String.concat "," + |> List.map (fun t -> + let name: string = getTypeFastFullName genParams t + $"%d{name.Length}:%s{name}" + ) + |> String.concat "" "delegate(" + types + ")" | Fable.AnonymousRecordType(fieldNames, genArgs, isStruct) -> diff --git a/tests/Beam/TypeTests.fs b/tests/Beam/TypeTests.fs index 990ca45434..421e028285 100644 --- a/tests/Beam/TypeTests.fs +++ b/tests/Beam/TypeTests.fs @@ -26,6 +26,10 @@ 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)) @@ -189,6 +193,11 @@ let ``test overloads distinguished by nested delegate work`` () = 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 403f3501fb..be5f375074 100644 --- a/tests/Dart/src/TypeTests.fs +++ b/tests/Dart/src/TypeTests.fs @@ -623,6 +623,10 @@ 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)) @@ -645,6 +649,10 @@ let tests() = 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 e759189f38..d28f2e2651 100644 --- a/tests/Js/Main/TypeTests.fs +++ b/tests/Js/Main/TypeTests.fs @@ -510,6 +510,10 @@ 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)) @@ -1255,6 +1259,10 @@ let tests = 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/TestType.fs b/tests/Python/TestType.fs index f695c0a4f8..171ab6a15a 100644 --- a/tests/Python/TestType.fs +++ b/tests/Python/TestType.fs @@ -440,6 +440,10 @@ 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)) @@ -1056,6 +1060,11 @@ let ``test Overloads distinguished by nested delegate work`` () = 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 20d286e239..b4b23ccbd1 100644 --- a/tests/Rust/tests/src/TypeTests.fs +++ b/tests/Rust/tests/src/TypeTests.fs @@ -59,6 +59,10 @@ 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)) @@ -1125,6 +1129,11 @@ let ``Overloads distinguished by nested delegate work`` () = 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("")