diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index f700a91832..d5f1434ffa 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* [JS/TS] Fix `super` call in multi-level generic class hierarchy generating wrong mangled method name (fix #3895) * [TS] Annotate `System.Collections.Generic.IList` as `MutableArray` (by @MangelMaxime) * [JS/TS] Fix `ResizeArray` index getter/setter not throwing `IndexOutOfRangeException` when index is out of bounds (fix #3812) (by @MangelMaxime) * [Beam] Fix unused term warning in try/catch when exception variable is not referenced (by @dbrattli) diff --git a/src/Fable.Compiler/CHANGELOG.md b/src/Fable.Compiler/CHANGELOG.md index 41339e79c3..1774e58243 100644 --- a/src/Fable.Compiler/CHANGELOG.md +++ b/src/Fable.Compiler/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* [JS/TS] Fix `super` call in multi-level generic class hierarchy generating wrong mangled method name (fix #3895) * [TS] Annotate `System.Collections.Generic.IList` as `MutableArray` (by @MangelMaxime) * [JS/TS] Fix `ResizeArray` index getter/setter not throwing `IndexOutOfRangeException` when index is out of bounds (fix #3812) (by @MangelMaxime) * [Beam] Fix unused term warning in try/catch when exception variable is not referenced (by @dbrattli) diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index e0b2eeb620..9a2209bc99 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -2939,7 +2939,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) @@ -2952,13 +2952,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/tests/Js/Main/TypeTests.fs b/tests/Js/Main/TypeTests.fs index 27c1c98ae2..e651133607 100644 --- a/tests/Js/Main/TypeTests.fs +++ b/tests/Js/Main/TypeTests.fs @@ -544,6 +544,30 @@ type MangledAbstractClass5(v) = 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>() = + let mutable _baseCallCount = 0 + abstract Attach: 'C -> unit + default _.Attach(_owner: 'C) = _baseCallCount <- _baseCallCount + 1 + member _.BaseCallCount = _baseCallCount + + interface IGenericAttach3895<'C> with + member this.Attach(owner: 'C) = this.Attach(owner) + +type GenericAttachMid3895<'Container>() = + inherit GenericAttachBase3895<'Container>() + override this.Attach(owner) = + base.Attach(owner) + +type GenericAttachLeaf3895() = + inherit GenericAttachMid3895() + override this.Attach(owner) = + base.Attach(owner) + 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 @@ -1391,6 +1415,12 @@ 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 obj = GenericAttachLeaf3895() + obj.Attach("hello") + obj.BaseCallCount |> equal 1 + // See #3328 testCase "SRTP works with byref" <| fun () -> let result = doubleIntByRef (TypeWithByRefMember()) 7