diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index 30c47d8518..98abd05e27 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -2947,7 +2947,7 @@ module Util = // When calling `super` in an override, it may happen the method is not originally declared // by the immediate parent, so we need to go through the hierarchy until we find the original declaration // (this is important to get the correct mangled name) - let entity = + let entity, memb = match memb.IsOverrideOrExplicitInterfaceImplementation, callInfo.ThisArg with | true, Some(Fable.Value(Fable.BaseValue _, _)) -> // Only compare param types for overloads (single curried parameter group) @@ -2960,13 +2960,24 @@ module Util = else None - entity - |> tryFindBaseEntity (fun ent -> - tryFindAbstractMember com ent memb.CompiledName memb.IsInstanceMember paramTypes - |> Option.isSome - ) - |> Option.defaultValue entity - | _ -> entity + let baseEntity = + entity + |> tryFindBaseEntity (fun ent -> + tryFindAbstractMember com ent memb.CompiledName memb.IsInstanceMember paramTypes + |> Option.isSome + ) + |> Option.defaultValue entity + + // Also find the member from the base entity so the overload hash is computed + // with matching generic parameter names. When 'memb' is an override from a + // subclass with different generic param names than the base entity, the hash + // would be wrong if we use the override member with the base entity. (#3895) + let baseMember = + tryFindAbstractMember com baseEntity memb.CompiledName memb.IsInstanceMember paramTypes + |> Option.defaultValue memb + + baseEntity, baseMember + | _ -> entity, memb callAttachedMember com ctx r typ callInfo entity memb diff --git a/src/Fable.Transforms/Python/Fable2Python.Transforms.fs b/src/Fable.Transforms/Python/Fable2Python.Transforms.fs index 91ff116623..d400f59cb8 100644 --- a/src/Fable.Transforms/Python/Fable2Python.Transforms.fs +++ b/src/Fable.Transforms/Python/Fable2Python.Transforms.fs @@ -3996,6 +3996,24 @@ let transformAttachedMethod (com: IPythonCompiler) ctx (info: Fable.MemberFuncti let args, body, returnType = getMemberArgsAndBody com ctx (Attached isStatic) info.HasSpread memb.Args memb.Body + let args = + let declaredAsGenericSingleArg = + List.isEmpty args.Defaults + && List.length args.Args = 1 + && ( + match info.CurriedParameterGroups |> List.collect id with + | [ p ] -> + match p.Type with + | Fable.GenericParam _ -> true + | _ -> false + | _ -> false + ) + + if declaredAsGenericSingleArg then + { args with Defaults = [ libValue com ctx "util" "UNIT" ] } + else + args + let self = Arg.arg "self" let arguments = diff --git a/tests/Js/Main/TypeTests.fs b/tests/Js/Main/TypeTests.fs index 986d434c15..4bbb013759 100644 --- a/tests/Js/Main/TypeTests.fs +++ b/tests/Js/Main/TypeTests.fs @@ -670,6 +670,30 @@ type AbstractClassWithResizeArrayProp() = type ConcreteClass1() = inherit MangledAbstractClass5(2) +// See #3895 - super call with generic class hierarchy uses wrong overload hash +type IGenericAttach3895<'C> = + abstract Attach: 'C -> unit + +[] +type GenericAttachBase3895<'C>(log: ResizeArray) = + abstract Attach: 'C -> unit + default _.Attach(_owner: 'C) = log.Add("Base") + + interface IGenericAttach3895<'C> with + member this.Attach(owner: 'C) = this.Attach(owner) + +type GenericAttachMid3895<'Container>(log: ResizeArray) = + inherit GenericAttachBase3895<'Container>(log) + override this.Attach(owner) = + base.Attach(owner) + log.Add("Mid") + +type GenericAttachLeaf3895(log: ResizeArray) = + inherit GenericAttachMid3895(log) + override this.Attach(owner) = + base.Attach(owner) + log.Add("Leaf") + type IndexedProps(v: int) = let mutable v = v member _.Item with get (v2: int) = v + v2 and set v2 (s: string) = v <- v2 + int s @@ -1552,6 +1576,14 @@ let tests = let c = ConcreteClass1() c.MyMethod(4) |> equal 58 + // See #3895 - super call in generic class hierarchy was using wrong mangled name + testCase "Super call works correctly in multi-level generic class hierarchy" <| fun () -> + let log = ResizeArray() + let obj = GenericAttachLeaf3895(log) + obj.Attach("hello") + // Each override delegates to base before logging, so the chain unwinds Base -> Mid -> Leaf + log |> List.ofSeq |> equal [ "Base"; "Mid"; "Leaf" ] + // See #3328 testCase "SRTP works with byref" <| fun () -> let result = doubleIntByRef (TypeWithByRefMember()) 7 diff --git a/tests/Python/TestType.fs b/tests/Python/TestType.fs index 663a3649ed..9cf5458456 100644 --- a/tests/Python/TestType.fs +++ b/tests/Python/TestType.fs @@ -607,6 +607,30 @@ type ConcreteGenericRunner() = override _.MapAToB(a: int) = string a override _.SomeProp = 42 +// See #3895 - super call in multi-level generic class hierarchy used wrong mangled name +type IGenericAttach3895<'C> = + abstract Attach: 'C -> unit + +[] +type GenericAttachBase3895<'C>(log: ResizeArray) = + abstract Attach: 'C -> unit + default _.Attach(_owner: 'C) = log.Add("Base") + + interface IGenericAttach3895<'C> with + member this.Attach(owner: 'C) = this.Attach(owner) + +type GenericAttachMid3895<'Container>(log: ResizeArray) = + inherit GenericAttachBase3895<'Container>(log) + override this.Attach(owner) = + base.Attach(owner) + log.Add("Mid") + +type GenericAttachLeaf3895(log: ResizeArray) = + inherit GenericAttachMid3895(log) + override this.Attach(owner) = + base.Attach(owner) + log.Add("Leaf") + type IndexedProps(v: int) = let mutable v = v member _.Item with get (v2: int) = v + v2 and set v2 (s: string) = v <- v2 + int s @@ -1831,3 +1855,12 @@ let ``test Abstract class property backed by captured variable in object express reader.Warnings.Add("Warning 2") reader.Warnings.Count |> equal 2 + +// See #3895 - super call in generic class hierarchy was using wrong mangled name +[] +let ``test Super call works correctly in multi-level generic class hierarchy`` () = + let log = ResizeArray() + let obj = GenericAttachLeaf3895(log) + obj.Attach("hello") + // Each override delegates to base before logging, so the chain unwinds Base -> Mid -> Leaf + log |> List.ofSeq |> equal [ "Base"; "Mid"; "Leaf" ] diff --git a/tests/Rust/tests/src/TypeTests.fs b/tests/Rust/tests/src/TypeTests.fs index f3366cbb38..520564c7b7 100644 --- a/tests/Rust/tests/src/TypeTests.fs +++ b/tests/Rust/tests/src/TypeTests.fs @@ -512,6 +512,30 @@ type MangledAbstractClass5(v) = type ConcreteClass1() = inherit MangledAbstractClass5(2) +// See #3895 - super call in multi-level generic class hierarchy used wrong mangled name +type IGenericAttach3895<'C> = + abstract Attach: 'C -> unit + +[] +type GenericAttachBase3895<'C>(log: ResizeArray) = + abstract Attach: 'C -> unit + default _.Attach(_owner: 'C) = log.Add("Base") + + interface IGenericAttach3895<'C> with + member this.Attach(owner: 'C) = this.Attach(owner) + +type GenericAttachMid3895<'Container>(log: ResizeArray) = + inherit GenericAttachBase3895<'Container>(log) + override this.Attach(owner) = + base.Attach(owner) + log.Add("Mid") + +type GenericAttachLeaf3895(log: ResizeArray) = + inherit GenericAttachMid3895(log) + override this.Attach(owner) = + base.Attach(owner) + log.Add("Leaf") + type IndexedProps(v: int) = let mutable v = v member _.Item with get (v2: int) = v + v2 and set v2 (s: string) = v <- v2 + int s @@ -1372,3 +1396,12 @@ let ``Non-mangled interfaces work with classes`` () = let ``Can call the base version of a mangled abstract method that was declared above in the hierarchy`` () = let c = ConcreteClass1() c.MyMethod(4) |> equal 58 + +// See #3895 - super call in generic class hierarchy was using wrong mangled name +[] +let ``Super call works correctly in multi-level generic class hierarchy`` () = + let log = ResizeArray() + let obj = GenericAttachLeaf3895(log) + obj.Attach("hello") + // Each override delegates to base before logging, so the chain unwinds Base -> Mid -> Leaf + List.ofSeq log |> equal [ "Base"; "Mid"; "Leaf" ]