Skip to content

Strange behavior in runtime #18417

Closed
Closed

Description

@ingted

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".

Activity

  1. added this to the Backlog milestone on Mar 27, 2025
  2. ingted commented on Mar 28, 2025

    @ingted
    ContributorAuthor

    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
    
  3. added
    Impact-Low(Internal MS Team use only) Describes an issue with limited impact on existing code.
    Area-Compiler-SRTPbugs in SRTP inference, resolution, witness passing, code gen
    and removed on Mar 31, 2025
  4. ingted commented on Apr 23, 2025

    @ingted
    ContributorAuthor

    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
    
  5. Martin521 commented on Apr 23, 2025

    @Martin521
    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.

  6. ingted commented on May 1, 2025

    @ingted
    ContributorAuthor

    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!!

  7. ingted commented on Oct 23, 2026

    @ingted
    ContributorAuthor
    Microsoft (R) F# Interactive version 15.2.402.0 for F# 10.0
    Copyright (c) Microsoft Corporation. All Rights Reserved.
    
    For help type #help;;
    
    > type MyType<^T when ^T: (member o: int)> (t:^T) =
    -
    -     member inline this.T = t;;
    type MyType<^T when ^T: (member o: int)> =
      new: t: ^T -> MyType<^T>
      member inline T: ^T
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area-Compiler-SRTPbugs in SRTP inference, resolution, witness passing, code genBugImpact-Low(Internal MS Team use only) Describes an issue with limited impact on existing code.

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions