Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic Base type parameterized with its inheritor => Stack Overflow on .GetType() of inheritor value #3607

Open
DunetsNM opened this issue Nov 22, 2023 · 5 comments

Comments

@DunetsNM
Copy link
Contributor

DunetsNM commented Nov 22, 2023

Description

Given a simple generic type hierarchy, where Base<> type is parameterized with its Inheritor type, it is possible to construct and use a value of Inheritor , but attempt to get runtime .GetType() results in stack overflow.

Repro code

Go to https://fable.io/repl/

[<AbstractClass>]
type Base<'Inheritor when 'Inheritor :> Base<'Inheritor>> () =
    member _.Dummy() = 1
    
type Inheritor() = inherit Base<Inheritor>()

let i = new Inheritor()
let t = i.GetType()

printf "hello world"
printf $"{t}"
printf "goodbye world"

Expected and actual results

Expected results:

hello world
{ some runtime type info printed }
goodbye world

Actual results:

Nothing printed, in Chrome dev tools Console following is found:

VM250 e85874de-2120-4703-a727-b32fbb1484f8:29 Uncaught RangeError: Maximum call stack size exceeded
at Inheritor_$reflection (VM250 e85874de-2120-4703-a727-b32fbb1484f8:29:5)
at Inheritor_$reflection (VM250 e85874de-2120-4703-a727-b32fbb1484f8:29:79)
at Inheritor_$reflection (VM250 e85874de-2120-4703-a727-b32fbb1484f8:29:79)
at Inheritor_$reflection (VM250 e85874de-2120-4703-a727-b32fbb1484f8:29:79)
at Inheritor_$reflection (VM250 e85874de-2120-4703-a727-b32fbb1484f8:29:79)
at Inheritor_$reflection (VM250 e85874de-2120-4703-a727-b32fbb1484f8:29:79)
...

Related information

  • Fable version: dotnet fable --version
    3.7.20

  • Operating system
    Windows 11 Pro

but probably will reproduce elsewhere

@DunetsNM
Copy link
Contributor Author

DunetsNM commented Nov 22, 2023

Note that only attempt to work with runtime type info results in stack overflow. It is still possible to construct and use the value, for example:

[<AbstractClass>]
type Base<'Inheritor when 'Inheritor :> Base<'Inheritor>> () =
    member _.Dummy() = 1
    
type Inheritor() = inherit Base<Inheritor>()

let i = new Inheritor()

printf "hello world"
printf $"Dummy value: {i.Dummy()}"
printf "goodbye world"

prints

hello world
Dummy value: 1
goodbye world

as expected

However in my case it is mission critical to have working runtime type info (specifically for Thoth JSON encoding via Encoding.Auto)

@MangelMaxime
Copy link
Member

This one is tricky.

The problem comes from this function:

export function Inheritor_$reflection() {
    return class_type("Test.Inheritor", void 0, Inheritor, Base$1_$reflection(Inheritor_$reflection()));
}

Which as you can see reference itself and this is what is causing the StackOverflow.

Perhaps, we need to look if we can use a Lazy value for the parent parameter or evaluate the function only when needed, I need to look where the parent arguments is used and for what.

@DunetsNM
Copy link
Contributor Author

DunetsNM commented Nov 26, 2023

why not just pass a null parent and then set it afterwards like this (pseudocode)

export function Inheritor_$reflection() {
    var x = class_type("Test.Inheritor", void 0, Inheritor, null);
    x.Parent = Base$1_$reflection(x);
    return x;
}

@MangelMaxime
Copy link
Member

Perhaps this can works, I am wondering if there are edges cases where this would not work or not.

I suppose the CI will tells us if this cause a regression or not, if it doesn't we will go with it unless another maintainer has another solutions. I am not yet familiar will the requirements of Fable reflection

@DunetsNM
Copy link
Contributor Author

sure, I'm not familiar with the code at all, and there might be other code paths that try to traverse reflection type hierarchy without the loop check, which also might stack overflow or hang, but at very least it will happen later

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants