Forward missing Type members in System.Reflection.Context - #134371
Merged
Merged
Conversation
Types mapped by CustomReflectionContext threw NotSupportedException
("Derived classes must provide an implementation") from IsByRefLike,
GetEnumValuesAsUnderlyingType, GetNullableUnderlyingType and
MakeFunctionPointerType: DelegatingType did not override them.
DelegatingType now forwards all four to the wrapped type, as it does
GetArrayRank. ProjectingType projects the Type results of
GetNullableUnderlyingType and MakeFunctionPointerType, and unprojects
the parameter types of the latter, as it does for MakeGenericType.
The overrides are conditioned on the target frameworks that have the
members.
Fix dotnet#133816
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Types mapped by CustomReflectionContext also answered other Type members from the base class instead of the wrapped type: IsSZArray, IsTypeDefinition and IsConstructedGenericType threw NotImplementedException; IsFunctionPointer and IsUnmanagedFunctionPointer returned false and the three GetFunctionPointer* methods threw NotSupportedException, including on the result of MakeFunctionPointerType; MemberType returned TypeInfo for a nested type. DelegatingType now forwards these to the wrapped type, and ProjectingType projects the types returned by the GetFunctionPointer* methods. Contributes to dotnet#133816 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
|
Tagging subscribers to this area: @steveisok, @dotnet/area-system-reflection |
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The observable behavioral changes require a breaking-change note or link before approval.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Forwards previously omitted Type members through CustomReflectionContext wrappers while preserving projected type identities.
Changes:
- Adds forwarding and projection for type-shape, nullable, enum, and function-pointer APIs.
- Adds regression coverage for forwarded values and projection behavior.
- Enables unsafe test declarations for function-pointer signatures.
| File | Description |
|---|---|
DelegatingType.cs |
Forwards missing Type members. |
ProjectingType.cs |
Projects returned types and unprojects inputs. |
ExtendedTypeTests.cs |
Tests all newly forwarded behavior. |
System.Reflection.Context.Tests.csproj |
Enables unsafe function-pointer tests. |
jkotas
approved these changes
Sep 21, 2026
This was referenced Sep 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Types mapped by a
CustomReflectionContextanswer someTypemembers from the base class instead of from the type they wrap, becauseDelegatingTypedoes not override them. #133816 reported the four whose base throwsNotSupportedException: Derived classes must provide an implementation.; enumerating the rest found more with the same cause.IsByRefLike,GetEnumValuesAsUnderlyingType(),GetNullableUnderlyingType(),MakeFunctionPointerType(...)NotSupportedExceptionIsSZArray,IsTypeDefinition,IsConstructedGenericType(andIsVariableBoundArray, computed fromIsSZArray)NotImplementedExceptionIsFunctionPointer,IsUnmanagedFunctionPointerfalse, including on the result ofMakeFunctionPointerTypeGetFunctionPointerReturnType(),GetFunctionPointerParameterTypes(),GetFunctionPointerCallingConventions()NotSupportedExceptionMemberTypeTypeInfofor a nested typeEvery member in the table except
GetNullableUnderlyingTypeandMakeFunctionPointerType, which are new in .NET 11, also reproduces with the released 10.0.12 package on .NET 10.0.12.Several of these members came with PRs that overrode them in other
Typesubclasses but not in this library: #81006 added the function pointer members toTypeDelegatorand MetadataLoadContext, #73057 addedGetEnumValuesAsUnderlyingTypeto MetadataLoadContext, #126905 addedGetNullableUnderlyingTypetoTypeDelegator, the Reflection.Emit builders and MetadataLoadContext, and #123819 addedMakeFunctionPointerTypetoTypeBuilderandEnumBuilder.DelegatingTypenow forwards each member in the table to the wrapped type, as it doesGetArrayRank;TypeDelegatorforwardsIsByRefLike,GetNullableUnderlyingTypeand every commit 2 member exceptMemberTypethe same way.ProjectingTypeprojects theTyperesults ofGetNullableUnderlyingType,MakeFunctionPointerTypeand the threeGetFunctionPointer*methods, and unprojects the parameter types passed toMakeFunctionPointerType, as it does forMakeGenericType.Commit 2 covers members #133816 does not name, so it is separate and can be dropped if this should stay limited to the issue;
MakeFunctionPointerTypewould then return a type whoseIsFunctionPointerisfalse.The sources are compiled for
net11.0,net10.0andnetstandard2.1.GetNullableUnderlyingTypeandMakeFunctionPointerTypeare under#if NET11_0_OR_GREATER;GetEnumValuesAsUnderlyingTypeand the function pointer members are under#if NET; the rest need no condition. MetadataLoadContext'sRoType.csuses the same conditions forGetNullableUnderlyingTypeandGetEnumValuesAsUnderlyingType.This changes the behaviour of every member in the table. I can draft a breaking-change note if one is needed.
Tests
New cases in
ExtendedTypeTests.cscover each member in the table and check that returned types are projected. Calling conventions are only reported on modified types, so that case maps the result ofGetModifiedParameterType()for anunmanaged[Cdecl]parameter; this needsAllowUnsafeBlocksin the test project, as inSystem.Reflection.Testsand the MetadataLoadContext tests.The new cases fail without the change, except
MemberTypeon a top-level type, whose expected value equals the base default. Reverting only theProjectingTypechanges fails the projection checks.Run locally on Windows x64, on a
clr+libs -rc checkedbuild of this branch unless noted:System.Reflection.Context.Tests: all pass; also with Release libraries on a local Release runtime build, and on a local x86 checked runtime build.System.ComponentModel.Composition.Registration.Tests(RegistrationBuilderderives fromCustomReflectionContext): all pass in the same three configurations./p:TestNativeAot=true): none failed; every new case passes.mono+libs -rc checked,/p:RuntimeFlavor=Mono), with the JIT and withMONO_ENV_OPTIONS=--interpreter: both test projects pass; every new case runs there except the two tests described below.net10.0andnetstandard2.1builds of the library, run on .NET 10.0.12 by a probe comparing mapped and unmapped values: thenet10.0build matches on every member in the table that .NET 10 has; thenetstandard2.1build matches on the members that surface has.The two tests that call
MakeFunctionPointerTypecarry the same[ActiveIssue("https://github.com/dotnet/runtime/issues/124149", TestRuntimes.Mono)]as theMakeFunctionPointerTypetests inSystem.Runtime.Tests'TypeTests.cs, since Mono does not implement it.Not in this PR
IsCollectiblereturns theMemberInfodefault,true. Forwarding it would makeTypeDescriptorkeep mapped types alive, since the caches added in Use AssemblyLoadContext-aware caches in TypeDescriptor to support unloading of assemblies cached by TypeDescriptor #114619 hold only collectible keys weakly: on a local build ofmain, a mapped type passed toTypeDescriptor.GetPropertieswas collected after a full GC with the 10.0.12 package, and stayed alive withIsCollectibleforwarded.HasSameMetadataDefinitionAsthrowsNotImplementedExceptionon mapped types and on every kind of mapped member, andGetMemberWithSameMetadataDefinitionAswith it.GetMethodoverloads takinggenericParameterCountthrowNotSupportedException.CustomType.GetMethodImplmerges in methods the context adds, so this is not a forward.GetModifiedFieldType()) loses its custom modifiers before the context sees it: it is not aTypeInfo, soGetTypeInfo()wraps it in aTypeDelegator, which does not forwardGetRequiredCustomModifiersorGetOptionalCustomModifiers.Resolves #133816
Note
AI-generated, written at my direction and reviewed by me before posting. The results above are from this branch's library and tests built locally on Windows x64 and run on the runtimes named; the enumeration of members, the members under "Not in this PR" and the 10.0.12 comparison were checked with throwaway tests and probes, not included here.