Skip to content

Commit db1d4d2

Browse files
fix(all): fix super call in multi-level generic class hierarchy using wrong mangled name (#4414)
Co-authored-by: Mangel Maxime <me@mangelmaxime.fr>
1 parent 0435732 commit db1d4d2

5 files changed

Lines changed: 135 additions & 8 deletions

File tree

src/Fable.Transforms/FSharp2Fable.Util.fs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2947,7 +2947,7 @@ module Util =
29472947
// When calling `super` in an override, it may happen the method is not originally declared
29482948
// by the immediate parent, so we need to go through the hierarchy until we find the original declaration
29492949
// (this is important to get the correct mangled name)
2950-
let entity =
2950+
let entity, memb =
29512951
match memb.IsOverrideOrExplicitInterfaceImplementation, callInfo.ThisArg with
29522952
| true, Some(Fable.Value(Fable.BaseValue _, _)) ->
29532953
// Only compare param types for overloads (single curried parameter group)
@@ -2960,13 +2960,24 @@ module Util =
29602960
else
29612961
None
29622962

2963-
entity
2964-
|> tryFindBaseEntity (fun ent ->
2965-
tryFindAbstractMember com ent memb.CompiledName memb.IsInstanceMember paramTypes
2966-
|> Option.isSome
2967-
)
2968-
|> Option.defaultValue entity
2969-
| _ -> entity
2963+
let baseEntity =
2964+
entity
2965+
|> tryFindBaseEntity (fun ent ->
2966+
tryFindAbstractMember com ent memb.CompiledName memb.IsInstanceMember paramTypes
2967+
|> Option.isSome
2968+
)
2969+
|> Option.defaultValue entity
2970+
2971+
// Also find the member from the base entity so the overload hash is computed
2972+
// with matching generic parameter names. When 'memb' is an override from a
2973+
// subclass with different generic param names than the base entity, the hash
2974+
// would be wrong if we use the override member with the base entity. (#3895)
2975+
let baseMember =
2976+
tryFindAbstractMember com baseEntity memb.CompiledName memb.IsInstanceMember paramTypes
2977+
|> Option.defaultValue memb
2978+
2979+
baseEntity, baseMember
2980+
| _ -> entity, memb
29702981

29712982
callAttachedMember com ctx r typ callInfo entity memb
29722983

src/Fable.Transforms/Python/Fable2Python.Transforms.fs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3996,6 +3996,24 @@ let transformAttachedMethod (com: IPythonCompiler) ctx (info: Fable.MemberFuncti
39963996
let args, body, returnType =
39973997
getMemberArgsAndBody com ctx (Attached isStatic) info.HasSpread memb.Args memb.Body
39983998

3999+
let args =
4000+
let declaredAsGenericSingleArg =
4001+
List.isEmpty args.Defaults
4002+
&& List.length args.Args = 1
4003+
&& (
4004+
match info.CurriedParameterGroups |> List.collect id with
4005+
| [ p ] ->
4006+
match p.Type with
4007+
| Fable.GenericParam _ -> true
4008+
| _ -> false
4009+
| _ -> false
4010+
)
4011+
4012+
if declaredAsGenericSingleArg then
4013+
{ args with Defaults = [ libValue com ctx "util" "UNIT" ] }
4014+
else
4015+
args
4016+
39994017
let self = Arg.arg "self"
40004018

40014019
let arguments =

tests/Js/Main/TypeTests.fs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,30 @@ type AbstractClassWithResizeArrayProp() =
670670
type ConcreteClass1() =
671671
inherit MangledAbstractClass5(2)
672672

673+
// See #3895 - super call with generic class hierarchy uses wrong overload hash
674+
type IGenericAttach3895<'C> =
675+
abstract Attach: 'C -> unit
676+
677+
[<AbstractClass>]
678+
type GenericAttachBase3895<'C>(log: ResizeArray<string>) =
679+
abstract Attach: 'C -> unit
680+
default _.Attach(_owner: 'C) = log.Add("Base")
681+
682+
interface IGenericAttach3895<'C> with
683+
member this.Attach(owner: 'C) = this.Attach(owner)
684+
685+
type GenericAttachMid3895<'Container>(log: ResizeArray<string>) =
686+
inherit GenericAttachBase3895<'Container>(log)
687+
override this.Attach(owner) =
688+
base.Attach(owner)
689+
log.Add("Mid")
690+
691+
type GenericAttachLeaf3895(log: ResizeArray<string>) =
692+
inherit GenericAttachMid3895<string>(log)
693+
override this.Attach(owner) =
694+
base.Attach(owner)
695+
log.Add("Leaf")
696+
673697
type IndexedProps(v: int) =
674698
let mutable v = v
675699
member _.Item with get (v2: int) = v + v2 and set v2 (s: string) = v <- v2 + int s
@@ -1552,6 +1576,14 @@ let tests =
15521576
let c = ConcreteClass1()
15531577
c.MyMethod(4) |> equal 58
15541578

1579+
// See #3895 - super call in generic class hierarchy was using wrong mangled name
1580+
testCase "Super call works correctly in multi-level generic class hierarchy" <| fun () ->
1581+
let log = ResizeArray<string>()
1582+
let obj = GenericAttachLeaf3895(log)
1583+
obj.Attach("hello")
1584+
// Each override delegates to base before logging, so the chain unwinds Base -> Mid -> Leaf
1585+
log |> List.ofSeq |> equal [ "Base"; "Mid"; "Leaf" ]
1586+
15551587
// See #3328
15561588
testCase "SRTP works with byref" <| fun () ->
15571589
let result = doubleIntByRef (TypeWithByRefMember()) 7

tests/Python/TestType.fs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,30 @@ type ConcreteGenericRunner() =
607607
override _.MapAToB(a: int) = string a
608608
override _.SomeProp = 42
609609

610+
// See #3895 - super call in multi-level generic class hierarchy used wrong mangled name
611+
type IGenericAttach3895<'C> =
612+
abstract Attach: 'C -> unit
613+
614+
[<AbstractClass>]
615+
type GenericAttachBase3895<'C>(log: ResizeArray<string>) =
616+
abstract Attach: 'C -> unit
617+
default _.Attach(_owner: 'C) = log.Add("Base")
618+
619+
interface IGenericAttach3895<'C> with
620+
member this.Attach(owner: 'C) = this.Attach(owner)
621+
622+
type GenericAttachMid3895<'Container>(log: ResizeArray<string>) =
623+
inherit GenericAttachBase3895<'Container>(log)
624+
override this.Attach(owner) =
625+
base.Attach(owner)
626+
log.Add("Mid")
627+
628+
type GenericAttachLeaf3895(log: ResizeArray<string>) =
629+
inherit GenericAttachMid3895<string>(log)
630+
override this.Attach(owner) =
631+
base.Attach(owner)
632+
log.Add("Leaf")
633+
610634
type IndexedProps(v: int) =
611635
let mutable v = v
612636
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
18311855
reader.Warnings.Add("Warning 2")
18321856

18331857
reader.Warnings.Count |> equal 2
1858+
1859+
// See #3895 - super call in generic class hierarchy was using wrong mangled name
1860+
[<Fact>]
1861+
let ``test Super call works correctly in multi-level generic class hierarchy`` () =
1862+
let log = ResizeArray<string>()
1863+
let obj = GenericAttachLeaf3895(log)
1864+
obj.Attach("hello")
1865+
// Each override delegates to base before logging, so the chain unwinds Base -> Mid -> Leaf
1866+
log |> List.ofSeq |> equal [ "Base"; "Mid"; "Leaf" ]

tests/Rust/tests/src/TypeTests.fs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,30 @@ type MangledAbstractClass5(v) =
512512
type ConcreteClass1() =
513513
inherit MangledAbstractClass5(2)
514514

515+
// See #3895 - super call in multi-level generic class hierarchy used wrong mangled name
516+
type IGenericAttach3895<'C> =
517+
abstract Attach: 'C -> unit
518+
519+
[<AbstractClass>]
520+
type GenericAttachBase3895<'C>(log: ResizeArray<string>) =
521+
abstract Attach: 'C -> unit
522+
default _.Attach(_owner: 'C) = log.Add("Base")
523+
524+
interface IGenericAttach3895<'C> with
525+
member this.Attach(owner: 'C) = this.Attach(owner)
526+
527+
type GenericAttachMid3895<'Container>(log: ResizeArray<string>) =
528+
inherit GenericAttachBase3895<'Container>(log)
529+
override this.Attach(owner) =
530+
base.Attach(owner)
531+
log.Add("Mid")
532+
533+
type GenericAttachLeaf3895(log: ResizeArray<string>) =
534+
inherit GenericAttachMid3895<string>(log)
535+
override this.Attach(owner) =
536+
base.Attach(owner)
537+
log.Add("Leaf")
538+
515539
type IndexedProps(v: int) =
516540
let mutable v = v
517541
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`` () =
13721396
let ``Can call the base version of a mangled abstract method that was declared above in the hierarchy`` () =
13731397
let c = ConcreteClass1()
13741398
c.MyMethod(4) |> equal 58
1399+
1400+
// See #3895 - super call in generic class hierarchy was using wrong mangled name
1401+
[<Fact>]
1402+
let ``Super call works correctly in multi-level generic class hierarchy`` () =
1403+
let log = ResizeArray<string>()
1404+
let obj = GenericAttachLeaf3895(log)
1405+
obj.Attach("hello")
1406+
// Each override delegates to base before logging, so the chain unwinds Base -> Mid -> Leaf
1407+
List.ofSeq log |> equal [ "Base"; "Mid"; "Leaf" ]

0 commit comments

Comments
 (0)