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
1 change: 1 addition & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

* [Python] Fix derived classes of generic abstract classes not being instantiable due to mismatched mangled method names between abstract stubs and overrides (by @dbrattli)
* [Dart/Rust] Fix `ResizeArray` reference equality (by @ncave)
* [JS/TS/Python/Beam] Fix `ResizeArray` (`System.Collections.Generic.List`) equality to use reference equality instead of structural equality (fixes #3718)
* [Beam] Fix `List.Cons` call replacement and test (by @ncave)
Expand Down
1 change: 1 addition & 0 deletions src/Fable.Compiler/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

* [Python] Fix derived classes of generic abstract classes not being instantiable due to mismatched mangled method names between abstract stubs and overrides (by @dbrattli)
* [Dart/Rust] Fix `ResizeArray` reference equality (by @ncave)
* [JS/TS/Python/Beam] Fix `ResizeArray` (`System.Collections.Generic.List`) equality to use reference equality instead of structural equality (fixes #3718)
* [Beam] Fix `List.Cons` call replacement and test (by @ncave)
Expand Down
10 changes: 6 additions & 4 deletions src/Fable.Transforms/Python/Fable2Python.Bases.fs
Original file line number Diff line number Diff line change
Expand Up @@ -335,12 +335,14 @@ module InterfaceNaming =
|> OverloadSuffix.getHash entityGenericParameters

/// Generates a mangled member name for interfaces/abstract classes.
/// Format: EntityFullPath_MemberName + OverloadSuffix (dots replaced with underscores)
/// Format: EntityFullName_MemberName + OverloadSuffix (dots and backticks replaced with underscores).
/// Uses the entity's FullName (which includes generic arity like ``2``) so the mangled name
/// matches the one produced for overrides in derived classes.
let getMangledMemberName (ent: Fable.Entity) (memb: Fable.MemberFunctionOrValue) =
let overloadSuffix = getOverloadSuffix ent memb
let lastDotIndex = memb.FullName.LastIndexOf '.'
let fullNamePath = memb.FullName.Substring(0, lastDotIndex)
$"%s{fullNamePath}.%s{memb.CompiledName}%s{overloadSuffix}".Replace(".", "_")

$"%s{ent.FullName}.%s{memb.CompiledName}%s{overloadSuffix}"
|> Fable.Py.Naming.cleanNameAsPyIdentifier

/// Generates abstract method stubs for abstract classes.
/// Creates stubs for dispatch slots (abstract members) that don't have default implementations.
Expand Down
24 changes: 24 additions & 0 deletions tests/Python/TestType.fs
Original file line number Diff line number Diff line change
Expand Up @@ -591,9 +591,25 @@
type ConcreteClass1() =
inherit MangledAbstractClass5(2)

// Generic abstract class with members - regression test for Python name-mangling
// where abstract stubs and overrides produced mismatched names for generic arities.
[<AbstractClass>]
type GenericAbstractRunner<'A, 'B>() =
abstract Encode: 'A -> string
abstract Decode: string -> 'A
abstract MapAToB: 'A -> 'B
abstract SomeProp: int with get

type ConcreteGenericRunner() =
inherit GenericAbstractRunner<int, string>()
override _.Encode(a: int) = string a
override _.Decode(s: string) = int s
override _.MapAToB(a: int) = string a
override _.SomeProp = 42

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 612 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 612 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 612 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 612 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 612 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 612 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 @@ -1794,6 +1810,14 @@
top.B.A |> equal 0
top.B.B |> equal false

[<Fact>]
let ``test Generic abstract class can be instantiated through derived class`` () =
let runner = ConcreteGenericRunner() :> GenericAbstractRunner<int, string>
runner.Encode 7 |> equal "7"
runner.Decode "42" |> equal 42
runner.MapAToB 3 |> equal "3"
runner.SomeProp |> equal 42

[<Fact>]
let ``test Abstract class property backed by captured variable in object expression works`` () =
let warnings = ResizeArray<string>()
Expand Down
Loading