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
Using the System.Text.Json source generator to deserialize a generic type whose constructor is inaccessible (e.g. a private constructor annotated with [JsonConstructor]) generates code that compiles but throws System.MissingMethodException at runtime. The equivalent non-generic type works.
Deserialization succeeds and prints 42, the same as for a non-generic type with an inaccessible [JsonConstructor].
Actual
System.MissingMethodException: Method not found: 'Wrapper`1..ctor'.
Cause
For an inaccessible constructor the generator emits a constructor accessor. For a generic type it emits a flat[UnsafeAccessor(UnsafeAccessorKind.Constructor)] extern that names the closed generic type directly:
UnsafeAccessor does not resolve a constructor against a directly-named closed generic type — it must live inside a generic wrapper class whose type parameters flow into the signature, which is what the generator already does for generic member (get/set/field) accessors but not for constructors. This is confirmed by an isolated test:
usingSystem;usingSystem.Runtime.CompilerServices;classGen<T>{privateGen(Tvalue){Value=value;}publicTValue;}staticclassFlat{[UnsafeAccessor(UnsafeAccessorKind.Constructor)]publicstaticexternGen<int>Create(intvalue);// what STJ emits today}staticclassWrapper<T>{[UnsafeAccessor(UnsafeAccessorKind.Constructor)]publicstaticexternGen<T>Create(Tvalue);// what STJ should emit}classProgram{staticvoidMain(){try{Console.WriteLine("flat OK: "+Flat.Create(1).Value);}catch(Exceptione){Console.WriteLine("flat FAIL: "+e.GetType().Name);}try{Console.WriteLine("wrapper OK: "+Wrapper<int>.Create(2).Value);}catch(Exceptione){Console.WriteLine("wrapper FAIL: "+e.GetType().Name);}}}// Output:// flat FAIL: MissingMethodException// wrapper OK: 2
Relevant source:
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs:940-943 — the constructor accessor is enabled for generic types (gated on SupportsGenericUnsafeAccessors).
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs:1396-1397 — emits private static extern {closedTypeFQN} __ctor_...(...) with no generic wrapper class (unlike the member accessors, which use __GenericAccessors_<T>).
Version / regression
Introduced by #124650 ("[STJ Source gen] Use UnsafeAccessor/reflection for inaccessible members"). Present on main and in release/11.0-preview4 through release/11.0-preview7, so it ships in .NET 11 previews. Released .NET 10 is unaffected: there an inaccessible generic constructor produces SYSLIB1222 and falls back rather than emitting a constructor accessor.
Proposed fix
Emit the constructor extern inside a generic wrapper class (as the member accessors already do), e.g. __GenericCtorAccessor_<TypeFriendlyName><T>.__ctor_..., referenced through the closed type args at the call site.
Note
This issue (including the repro) was created with the help of GitHub Copilot.
Description
Using the System.Text.Json source generator to deserialize a generic type whose constructor is inaccessible (e.g. a
privateconstructor annotated with[JsonConstructor]) generates code that compiles but throwsSystem.MissingMethodExceptionat runtime. The equivalent non-generic type works.Repro
Expected
Deserialization succeeds and prints
42, the same as for a non-generic type with an inaccessible[JsonConstructor].Actual
Cause
For an inaccessible constructor the generator emits a constructor accessor. For a generic type it emits a flat
[UnsafeAccessor(UnsafeAccessorKind.Constructor)]extern that names the closed generic type directly:UnsafeAccessor does not resolve a constructor against a directly-named closed generic type — it must live inside a generic wrapper class whose type parameters flow into the signature, which is what the generator already does for generic member (get/set/field) accessors but not for constructors. This is confirmed by an isolated test:
Relevant source:
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs:940-943— the constructor accessor is enabled for generic types (gated onSupportsGenericUnsafeAccessors).src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs:1396-1397— emitsprivate static extern {closedTypeFQN} __ctor_...(...)with no generic wrapper class (unlike the member accessors, which use__GenericAccessors_<T>).Version / regression
Introduced by #124650 ("[STJ Source gen] Use UnsafeAccessor/reflection for inaccessible members"). Present on
mainand inrelease/11.0-preview4throughrelease/11.0-preview7, so it ships in .NET 11 previews. Released .NET 10 is unaffected: there an inaccessible generic constructor producesSYSLIB1222and falls back rather than emitting a constructor accessor.Proposed fix
Emit the constructor extern inside a generic wrapper class (as the member accessors already do), e.g.
__GenericCtorAccessor_<TypeFriendlyName><T>.__ctor_..., referenced through the closed type args at the call site.Note
This issue (including the repro) was created with the help of GitHub Copilot.