Skip to content

Commit 4524806

Browse files
Fix super call in multi-level generic class hierarchy using wrong mangled name (fix #3895)
When calling `base.Method()` in a class that inherits through a chain of generic classes, the overload hash suffix was computed incorrectly. For example, if `ProjectSelection` inherits `SelectionAspect<'Container>` which inherits `AspectBase<'C>`, calling `base.Attach(owner)` from `ProjectSelection.Attach` would generate `super["...AspectBase.Attach1505"]` but the method was defined as `"...AspectBase.Attach2B595"`, causing a runtime error. Root cause: `callAttachedMember` was called with the correct base entity (`AspectBase<'C>`) but the override member from the intermediate class (`SelectionAspect<'Container>.Attach`). `getOverloadSuffixFrom` uses the entity's generic param names (`['C']`) as normalization keys, but the member's parameter types use the intermediate class's names (`'Container`), causing the lookup to fail and producing a different hash. Fix: after finding the base entity with `tryFindBaseEntity`, also find the corresponding dispatch slot member in that entity so the hash is computed with matching entity and member generic parameter names. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ab82e32 commit 4524806

4 files changed

Lines changed: 51 additions & 8 deletions

File tree

src/Fable.Cli/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
* [JS/TS] Fix `super` call in multi-level generic class hierarchy generating wrong mangled method name (fix #3895)
1213
* [TS] Annotate `System.Collections.Generic.IList<T>` as `MutableArray<T>` (by @MangelMaxime)
1314
* [JS/TS] Fix `ResizeArray` index getter/setter not throwing `IndexOutOfRangeException` when index is out of bounds (fix #3812) (by @MangelMaxime)
1415
* [Beam] Fix unused term warning in try/catch when exception variable is not referenced (by @dbrattli)

src/Fable.Compiler/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
* [JS/TS] Fix `super` call in multi-level generic class hierarchy generating wrong mangled method name (fix #3895)
1213
* [TS] Annotate `System.Collections.Generic.IList<T>` as `MutableArray<T>` (by @MangelMaxime)
1314
* [JS/TS] Fix `ResizeArray` index getter/setter not throwing `IndexOutOfRangeException` when index is out of bounds (fix #3812) (by @MangelMaxime)
1415
* [Beam] Fix unused term warning in try/catch when exception variable is not referenced (by @dbrattli)

src/Fable.Transforms/FSharp2Fable.Util.fs

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

2955-
entity
2956-
|> tryFindBaseEntity (fun ent ->
2957-
tryFindAbstractMember com ent memb.CompiledName memb.IsInstanceMember paramTypes
2958-
|> Option.isSome
2959-
)
2960-
|> Option.defaultValue entity
2961-
| _ -> entity
2955+
let baseEntity =
2956+
entity
2957+
|> tryFindBaseEntity (fun ent ->
2958+
tryFindAbstractMember com ent memb.CompiledName memb.IsInstanceMember paramTypes
2959+
|> Option.isSome
2960+
)
2961+
|> Option.defaultValue entity
2962+
2963+
// Also find the member from the base entity so the overload hash is computed
2964+
// with matching generic parameter names. When 'memb' is an override from a
2965+
// subclass with different generic param names than the base entity, the hash
2966+
// would be wrong if we use the override member with the base entity. (#3895)
2967+
let baseMember =
2968+
tryFindAbstractMember com baseEntity memb.CompiledName memb.IsInstanceMember paramTypes
2969+
|> Option.defaultValue memb
2970+
2971+
baseEntity, baseMember
2972+
| _ -> entity, memb
29622973

29632974
callAttachedMember com ctx r typ callInfo entity memb
29642975

tests/Js/Main/TypeTests.fs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,30 @@ type MangledAbstractClass5(v) =
544544
type ConcreteClass1() =
545545
inherit MangledAbstractClass5(2)
546546

547+
// See #3895 - super call with generic class hierarchy uses wrong overload hash
548+
type IGenericAttach3895<'C> =
549+
abstract Attach: 'C -> unit
550+
551+
[<AbstractClass>]
552+
type GenericAttachBase3895<'C>() =
553+
let mutable _baseCallCount = 0
554+
abstract Attach: 'C -> unit
555+
default _.Attach(_owner: 'C) = _baseCallCount <- _baseCallCount + 1
556+
member _.BaseCallCount = _baseCallCount
557+
558+
interface IGenericAttach3895<'C> with
559+
member this.Attach(owner: 'C) = this.Attach(owner)
560+
561+
type GenericAttachMid3895<'Container>() =
562+
inherit GenericAttachBase3895<'Container>()
563+
override this.Attach(owner) =
564+
base.Attach(owner)
565+
566+
type GenericAttachLeaf3895() =
567+
inherit GenericAttachMid3895<string>()
568+
override this.Attach(owner) =
569+
base.Attach(owner)
570+
547571
type IndexedProps(v: int) =
548572
let mutable v = v
549573
member _.Item with get (v2: int) = v + v2 and set v2 (s: string) = v <- v2 + int s
@@ -1391,6 +1415,12 @@ let tests =
13911415
let c = ConcreteClass1()
13921416
c.MyMethod(4) |> equal 58
13931417

1418+
// See #3895 - super call in generic class hierarchy was using wrong mangled name
1419+
testCase "Super call works correctly in multi-level generic class hierarchy" <| fun () ->
1420+
let obj = GenericAttachLeaf3895()
1421+
obj.Attach("hello")
1422+
obj.BaseCallCount |> equal 1
1423+
13941424
// See #3328
13951425
testCase "SRTP works with byref" <| fun () ->
13961426
let result = doubleIntByRef (TypeWithByRefMember()) 7

0 commit comments

Comments
 (0)