Skip to content

Strange behavior in runtime #18417

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

Open
ingted opened this issue Mar 27, 2025 · 4 comments
Open

Strange behavior in runtime #18417

ingted opened this issue Mar 27, 2025 · 4 comments
Labels
Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen Bug Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code.
Milestone

Comments

@ingted
Copy link
Contributor

ingted commented Mar 27, 2025

According to this #15135

type MyType<^T when ^T: (member o: int)> (t:^T) =
    member inline this.T = t

Looks good in editor (no syntax error), but results

error FS1113: The value 'T' was marked inline but its implementation makes use of an internal or private function which is not sufficiently accessible

Is normal.

However

type MyType1<^T when ^T: (member o:int)> = {
    t: ^T
}
    with
        member inline this.T = this.t

Provides very similar functionality, but working...

type MyType1<^T when ^T: (member o: int)> =
  { t: ^T }
  member inline T: ^T

> 

Excuse me, is anyone kind to tell what's the difference? Since according to the past investigation,

I found the underlying reason. The CLR that handles generics recognizes a number of [constraints](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters), including type/interface constraints, but not explicit member constraints. The latter is a pure [F# thing](https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/generics/constraints). This means (now I am speculating) the compiler has to express it in IL by an interface constraint, and that can be done only if MyType is a "head type", i.e. statically known at compile time. These complications are also the reason why the F# Language Reference says that explicit member constraints are "not intended for common use".
@ingted
Copy link
Contributor Author

ingted commented Mar 28, 2025

Hi @Martin521,

I found an interesting stuff related to you investigation.

type a () =
    member this.o x = x + 1

type A<'T> (t:'T) =
    member val T = t

type MT<^T when ^T: (member o:int -> int)> (v:^T) =
    inherit A<^T> (v)

module o =
    let v = (MT<a> (a()) :> A<a>).T.o 1

In this case, the explicit member constraint successfully has been expressed it in IL by an interface constraint (successfully executed in fsi, SDK 9.0.103), And type ^T is NOT a "head type"... How do you think about this case?

I mean... maybe the following code snipets should be valid for F# itself (not related to CLR)

type MyType<'T when ^T: (member o: int)> (t:^T) =
    member inline this.T = t

or

type MyType<'T when ^T: (member o: int)> (t:^T) =
    member this.T = t

@abonie abonie added Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code. Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen Bug and removed Needs-Triage labels Mar 31, 2025
@ingted
Copy link
Contributor Author

ingted commented Apr 23, 2025

If anyone come across here, I got the workaround:


type WithBasedData<'T> (t:'T) =
    member val _base = t

type MyType<^T when ^T: (member o: int)> (t:^T) =
    inherit WithBasedData<'T>(t)
    do
        ()

type MyType<^T when ^T: (member o: int)> with
    member inline this.T = this._base

@Martin521
Copy link
Contributor

@ingted I missed the comment you made a month ago.
I am sorry, but this is now beyond my amateur knowledge of escape analysis and related type checking procedures.
But good that you found a workaround.

@ingted
Copy link
Contributor Author

ingted commented May 1, 2025

A much quicker workaround:

type O<'T when 'T:(member i:int)>(t:'T) as this =
    [<DefaultValue>]
    val mutable internalState : 'T
    do
        this.internalState <- t

type O<'T when 'T:(member i:int)> with
    member inline this.i2 = this.internalState.i

No inherit required!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compiler-SRTP bugs in SRTP inference, resolution, witness passing, code gen Bug Impact-Low (Internal MS Team use only) Describes an issue with limited impact on existing code.
Projects
Status: New
Development

No branches or pull requests

3 participants