You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Debug builds: SRTP-constrained inline member runs the first call's specialization for later calls with other type arguments #20826
Since #19548 (SDK 10.0.400), Debug builds call inline members through generated <Name>__debug@N methods instead of inlining them. When an inline member with SRTP constraints defines local closures and is called more than once with different type arguments, every call after the first runs the first call's specialization.
Repro steps
One project with Lib.fs followed by Program.fs, run with dotnet run -c Debug.
Lib.fs:
moduleLibopenSystemlet inlinecreate(x:^a):Result<^b,'c>=((^aor^b):(static membercreate:^a-> Result<^b, 'c >) x)typeBuilder()=member valRegistered= ResizeArray<string>()memberthis.Convert<'a,'b>(f:Func<'a,'b>)=
this.Registered.Add($"{typeof<'a>.Name}->{typeof<'b>.Name}")
this
typeBuilderwith[<RequiresExplicitTypeArguments>]member inlinethis.Register<'domain,'serializedwhen('serialized or 'domain):(static member create:'serialized->Result<'domain,string>)and(^domain or ^serialized):(static member op_Explicit:^domain->^serialized)>()=letparse x =match create x with| Ok x -> x
| Error(msg: string)-> failwith msg
letgetValue x =((^domainor^serialized):(static memberop_Explicit:^domain->^serialized) x)
this.Convert<'serialized, 'domain>(parse).Convert<'domain, 'serialized>(getValue)|> ignore
this
Program.fs:
openLibtypeA=| A ofstringstatic membercreate(s:string)= Ok(A s)static memberop_Explicit(A s):string = s
typeB=| B ofintstatic membercreate(i:int)= Ok(B i)static memberop_Explicit(B i):int = i
letb= Builder()
b.Register<A, string>()|> ignore
b.Register<B, int>()|> ignore
printfn "%s"(String.concat ", " b.Registered)
Expected behavior
String->A, A->String, Int32->B, B->Int32, as in Release or with --always-inline+.
Actual behavior
String->A, A->String, String->A, A->String: the Register<B, int> call runs the specialization for A and string.
It also happens when Lib is a separate referenced project, and with a chained call (b.Register<A, string>().Register<B, int>()). A generic inline member without SRTP constraints is unaffected.
In our application, an inline member like this registers HotChocolate type converters. In Debug, every call registered the first call's type, so the app failed at startup.
Activity
T-Gro commentedon Oct 21, 2026
@auduchinok : Can you please have a look?
auduchinok commentedon Oct 21, 2026
I'll take a look.