Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions src/Fable.Transforms/FSharp2Fable.Util.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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

Expand Down
18 changes: 18 additions & 0 deletions src/Fable.Transforms/Python/Fable2Python.Transforms.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
32 changes: 32 additions & 0 deletions tests/Js/Main/TypeTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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

[<AbstractClass>]
type GenericAttachBase3895<'C>(log: ResizeArray<string>) =
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<string>) =
inherit GenericAttachBase3895<'Container>(log)
override this.Attach(owner) =
base.Attach(owner)
log.Add("Mid")

type GenericAttachLeaf3895(log: ResizeArray<string>) =
inherit GenericAttachMid3895<string>(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
Expand Down Expand Up @@ -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<string>()
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
Expand Down
33 changes: 33 additions & 0 deletions tests/Python/TestType.fs
Original file line number Diff line number Diff line change
Expand Up @@ -607,9 +607,33 @@
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

[<AbstractClass>]
type GenericAttachBase3895<'C>(log: ResizeArray<string>) =
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<string>) =
inherit GenericAttachBase3895<'Container>(log)
override this.Attach(owner) =
base.Attach(owner)
log.Add("Mid")

type GenericAttachLeaf3895(log: ResizeArray<string>) =
inherit GenericAttachMid3895<string>(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

Check warning on line 636 in tests/Python/TestType.fs

View workflow job for this annotation

GitHub Actions / build-python (ubuntu-latest, 3.12)

An indexed property's getter and setter must have the same type. Property 'Item' has getter of type 'int' but setter of type 'string'.

Check warning on line 636 in tests/Python/TestType.fs

View workflow job for this annotation

GitHub Actions / build-python (ubuntu-latest, 3.13)

An indexed property's getter and setter must have the same type. Property 'Item' has getter of type 'int' but setter of type 'string'.

Check warning on line 636 in tests/Python/TestType.fs

View workflow job for this annotation

GitHub Actions / build-python (ubuntu-latest, 3.14)

An indexed property's getter and setter must have the same type. Property 'Item' has getter of type 'int' but setter of type 'string'.

Check warning on line 636 in tests/Python/TestType.fs

View workflow job for this annotation

GitHub Actions / build-python (windows-latest, 3.14)

An indexed property's getter and setter must have the same type. Property 'Item' has getter of type 'int' but setter of type 'string'.

Check warning on line 636 in tests/Python/TestType.fs

View workflow job for this annotation

GitHub Actions / build-python (windows-latest, 3.13)

An indexed property's getter and setter must have the same type. Property 'Item' has getter of type 'int' but setter of type 'string'.

Check warning on line 636 in tests/Python/TestType.fs

View workflow job for this annotation

GitHub Actions / build-python (windows-latest, 3.12)

An indexed property's getter and setter must have the same type. Property 'Item' has getter of type 'int' but setter of type 'string'.
member _.Item with get (v2: float) = float v + v2 / 2.

[<Interface>]
Expand Down Expand Up @@ -1831,3 +1855,12 @@
reader.Warnings.Add("Warning 2")

reader.Warnings.Count |> equal 2

// See #3895 - super call in generic class hierarchy was using wrong mangled name
[<Fact>]
let ``test Super call works correctly in multi-level generic class hierarchy`` () =
let log = ResizeArray<string>()
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" ]
33 changes: 33 additions & 0 deletions tests/Rust/tests/src/TypeTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -512,9 +512,33 @@
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

[<AbstractClass>]
type GenericAttachBase3895<'C>(log: ResizeArray<string>) =
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<string>) =
inherit GenericAttachBase3895<'Container>(log)
override this.Attach(owner) =
base.Attach(owner)
log.Add("Mid")

type GenericAttachLeaf3895(log: ResizeArray<string>) =
inherit GenericAttachMid3895<string>(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

Check warning on line 541 in tests/Rust/tests/src/TypeTests.fs

View workflow job for this annotation

GitHub Actions / build-rust (default)

An indexed property's getter and setter must have the same type. Property 'Item' has getter of type 'int' but setter of type 'string'.

Check warning on line 541 in tests/Rust/tests/src/TypeTests.fs

View workflow job for this annotation

GitHub Actions / build-rust (no_std)

An indexed property's getter and setter must have the same type. Property 'Item' has getter of type 'int' but setter of type 'string'.

Check warning on line 541 in tests/Rust/tests/src/TypeTests.fs

View workflow job for this annotation

GitHub Actions / build-rust (threaded)

An indexed property's getter and setter must have the same type. Property 'Item' has getter of type 'int' but setter of type 'string'.
member _.Item with get (v2: float) = float v + v2 / 2.

[<Interface>]
Expand Down Expand Up @@ -1372,3 +1396,12 @@
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
[<Fact>]
let ``Super call works correctly in multi-level generic class hierarchy`` () =
let log = ResizeArray<string>()
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" ]
Loading