Skip to content

[cdac] Extract ITypeHandle interface and add synthetic type handles#129800

Draft
max-charlamb wants to merge 3 commits into
mainfrom
dev/max-charlamb/rts-synthetic-typehandles
Draft

[cdac] Extract ITypeHandle interface and add synthetic type handles#129800
max-charlamb wants to merge 3 commits into
mainfrom
dev/max-charlamb/rts-synthetic-typehandles

Conversation

@max-charlamb

@max-charlamb max-charlamb commented Jun 24, 2026

Copy link
Copy Markdown
Member

Summary

Convert TypeHandle from a readonly struct to the ITypeHandle interface, enabling synthetic (reader-fabricated) TypeHandles for unloaded constructed types (Ptr, Byref, SzArray, Array).

Changes

  • Rename TypeHandle -> ITypeHandle across entire cDAC codebase
  • ITypeHandle interface in Abstractions with Address, IsNull, IsSynthetic
  • NullTypeHandle singleton in Abstractions (ITypeHandle.Null)
  • TargetTypeHandle struct in Contracts (real target-backed handles)
  • SyntheticTypeHandle internal class in Contracts (unloaded constructed types)
  • 10 RTS methods with synthetic-aware branches
  • GetConstructedType synthesizes on miss for Ptr/Byref/SzArray/Array instead of returning null
  • ReadOnlySpan<TypeHandle> replaced with ITypeHandle[] in public API

Motivation

When decoding method signatures, the runtime frequently has no loaded TypeHandle for constructed types like int*, ref T, or T[]. Previously GetConstructedType returned null, forcing consumers into ad-hoc side-channels. Now it returns a synthetic handle that downstream contracts can query uniformly.

Note

This content was generated with the assistance of GitHub Copilot.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors cDAC’s type identity from a concrete TypeHandle struct to an ITypeHandle abstraction, enabling “synthetic” (reader-fabricated) handles for unloaded constructed types (Ptr/Byref/SZArray/Array) and updating the contracts/legacy layers/tests accordingly.

Changes:

  • Introduces ITypeHandle (+ null/synthetic/target-backed implementations) and updates contracts to traffic in ITypeHandle.
  • Updates RuntimeTypeSystem_1.GetConstructedType to synthesize constructed types on miss for Ptr/Byref/SZArray/Array.
  • Updates legacy SOS/DacDbi code and cDAC unit/dump tests to consume the new handle abstraction.

Reviewed changes

Copilot reviewed 52 out of 52 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/native/managed/cdac/tests/UnitTests/TypeDescTests.cs Updates unit tests to use ITypeHandle and array-returning APIs.
src/native/managed/cdac/tests/UnitTests/SOSDacInterface5Tests.cs Switches test setup to TargetTypeHandle.
src/native/managed/cdac/tests/UnitTests/RuntimeMutableTypeSystemTests.cs Updates Moq setups and APIs to ITypeHandle.
src/native/managed/cdac/tests/UnitTests/ObjectTests.cs Updates object contract tests to use TargetTypeHandle.
src/native/managed/cdac/tests/UnitTests/MethodTableTests.cs Replaces TypeHandle usage with ITypeHandle across MT tests.
src/native/managed/cdac/tests/UnitTests/MethodDescTests.cs Updates generic-instantiation test assertions to use ITypeHandle[].
src/native/managed/cdac/tests/UnitTests/ExceptionTests.cs Updates exception contract tests to use TargetTypeHandle.
src/native/managed/cdac/tests/UnitTests/DacDbiImplTests.cs Updates DacDbi tests to use TargetTypeHandle and new APIs.
src/native/managed/cdac/tests/UnitTests/CodeVersionsTests.cs Updates code versions test helpers to use ITypeHandle.
src/native/managed/cdac/tests/DumpTests/RuntimeTypeSystemDumpTests.cs Updates dump tests to the ITypeHandle API surface.
src/native/managed/cdac/tests/DumpTests/ObjectiveCMarshalDumpTests.cs Updates dump test to ITypeHandle.
src/native/managed/cdac/tests/DumpTests/IXCLRDataValueDumpTests.cs Updates commentary/type references for ITypeHandle.
src/native/managed/cdac/tests/DumpTests/IXCLRDataMethodDefinitionDumpTests.cs Updates dump test to ITypeHandle.
src/native/managed/cdac/tests/DumpTests/DacDbi/DacDbiObjectDumpTests.cs Updates DacDbi dump tests and helpers to ITypeHandle.
src/native/managed/cdac/tests/DumpTests/DacDbi/DacDbiLoaderDumpTests.cs Updates DacDbi loader dump test to ITypeHandle.
src/native/managed/cdac/tests/DumpTests/DacDbi/DacDbiExactTypeHandleDumpTests.cs Updates exact-typehandle roundtrip dump test to ITypeHandle.
src/native/managed/cdac/tests/DumpTests/CCWDumpTests.cs Updates CCW dump test wording and ITypeHandle usage.
src/native/managed/cdac/tests/DumpTests/AsyncContinuationDumpTests.cs Updates continuation dump tests to ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/TypeNameBuilder.cs Updates type/method name formatting to consume ITypeHandle and array-returning instantiation APIs.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.IXCLRDataProcess.cs Updates IXCLRDataProcess implementation to ITypeHandle and array-returning APIs.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs Updates SOS DAC implementation to ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SigFormat.cs Updates signature formatting helpers to use ITypeHandle and array-returning APIs.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs Updates interop struct comments to refer to ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/Helpers/HeapWalk.cs Updates heap-walk helper to use ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs Updates DacDbi implementation to use ITypeHandle and constructed-type synthesis.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataMethodInstance.cs Updates method instance implementation to use ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataMethodDefinition.cs Updates generic-instantiation checks to ITypeHandle[].
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs Updates frame/type resolution paths to use ITypeHandle and array-returning instantiations.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/ExtensionMethods.cs Adapts type-handle helper extensions to operate on ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcSignatureTypeProvider.cs Updates GC signature decoding context and lookups to ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameHelpers.cs Updates frame helper logic to use ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.cs Updates signature type provider to produce/consume ITypeHandle and constructed types.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/Signature_1.cs Updates signature contract implementation to decode to ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/IRuntimeSignatureTypeProvider.cs Updates documentation for “internal type” handling to ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem/TypeHandleImplementations.cs Adds TargetTypeHandle and SyntheticTypeHandle implementations of ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs Core RuntimeTypeSystem changes: ITypeHandle plumbing + synthetic constructed-type behavior.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeMutableTypeSystem_1.cs Updates mutable type system APIs to accept ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs Updates object sizing/array logic to use ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ManagedTypeSource_1.cs Updates managed type resolution/caching to ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs Updates exception-clause reading path to use ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Exception_1.cs Updates exception contract implementation to use ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ConditionalWeakTable_1.cs Updates CWT contract implementation to use ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ComWrappers_1.cs Updates RCW detection to use the renamed generated type-handle accessor.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs Updates code-versions contract to use ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs Changes signature decoding contract to return ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs Introduces ITypeHandle/NullTypeHandle and updates the RuntimeTypeSystem contract signatures.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeMutableTypeSystem.cs Updates mutable-type-system contract signature to use ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IManagedTypeSource.cs Updates managed type source contract to use ITypeHandle.
src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CdacAttributes.cs Updates generator-related docs to refer to ITypeHandle.
src/native/managed/cdac/gen/TypeNameResolverSource.cs Updates generated helper to return ITypeHandle.
src/native/managed/cdac/gen/Emitter.cs Updates generated output to reference ITypeHandle.
src/native/managed/cdac/gen/CdacGenerator.cs Updates generator docs to refer to ITypeHandle.

Comment on lines +11 to +22
/// <summary>
/// An opaque handle to a runtime type. May represent a loaded type (backed by a
/// target-process MethodTable or TypeDesc address) or a synthetic type fabricated
/// by the reader for unloaded constructed types.
/// </summary>
public interface ITypeHandle : IEquatable<ITypeHandle>
{
// TODO-Layering: These members should be accessible only to contract implementations.
public TypeHandle(TargetPointer address)
{
Address = address;
}

public TargetPointer Address { get; }
TargetPointer Address { get; }
bool IsNull { get; }
bool IsSynthetic { get; }
static ITypeHandle Null { get; } = NullTypeHandle.Instance;
}
Comment on lines +20 to +25
public bool IsNull => Address == 0;
public bool IsSynthetic => false;

public bool Equals(ITypeHandle? other)
=> other is TargetTypeHandle t && Address == t.Address;
public bool Equals(TargetTypeHandle other) => Address == other.Address;
Comment on lines 465 to +477
@@ -474,14 +474,14 @@ public TypeHandle GetTypeHandle(TargetPointer typeHandlePointer)
// if we already validated this address, return a handle
if (_methodTables.ContainsKey(typeHandlePointer))
{
return new TypeHandle(typeHandlePointer);
return new TargetTypeHandle(typeHandlePointer);
Comment on lines +1184 to +1191
// No loaded match found. For Ptr/Byref/SzArray/Array, synthesize a handle
// so signature-decoding contracts can treat unloaded constructed types uniformly.
if (corElementType is CorElementType.Ptr or CorElementType.Byref or CorElementType.SzArray or CorElementType.Array)
{
var synthetic = new SyntheticTypeHandle(corElementType, typeHandle, rank);
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), synthetic);
return synthetic;
}
Max Charlamb and others added 2 commits June 24, 2026 10:42
Rename the TypeHandle struct to ITypeHandle across the entire cDAC
codebase in preparation for converting it to an interface that supports
synthetic type handles for unloaded constructed types.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…peHandle

Convert ITypeHandle from a readonly struct to an interface, enabling
polymorphic type handles. Add NullTypeHandle (Abstractions) and
TargetTypeHandle (Contracts) implementations. Replace ReadOnlySpan
with ITypeHandle[] in public API signatures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb max-charlamb force-pushed the dev/max-charlamb/rts-synthetic-typehandles branch from 1c85305 to 2050b89 Compare June 24, 2026 14:50
@rcj1

rcj1 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

ad-hoc side-channels

Am I missing something, or where are you removing said side channels with this abstraction? If this is a helper for a PR that you have in progress, what does it look like before and after?

@max-charlamb

Copy link
Copy Markdown
Member Author

ad-hoc side-channels

Am I missing something, or where are you removing said side channels with this abstraction? If this is a helper for a PR that you have in progress, what does it look like before and after?

I needed extra data for implementing the ArgIterator: https://github.com/dotnet/runtime/pull/129769/changes#r3468318817

I believe adding synthetic typehandles aligns with our long-term goal to not have to load things in the runtime just for diagnostics.

@rcj1

rcj1 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

ad-hoc side-channels

Am I missing something, or where are you removing said side channels with this abstraction? If this is a helper for a PR that you have in progress, what does it look like before and after?

I needed extra data for implementing the ArgIterator: https://github.com/dotnet/runtime/pull/129769/changes#r3468318817

I believe adding synthetic typehandles aligns with our long-term goal to not have to load things in the runtime just for diagnostics.

Could we accomplish this by adding (optional) fields and properties to the TypeHandle struct?

@max-charlamb

Copy link
Copy Markdown
Member Author

ad-hoc side-channels

Am I missing something, or where are you removing said side channels with this abstraction? If this is a helper for a PR that you have in progress, what does it look like before and after?

I needed extra data for implementing the ArgIterator: https://github.com/dotnet/runtime/pull/129769/changes#r3468318817
I believe adding synthetic typehandles aligns with our long-term goal to not have to load things in the runtime just for diagnostics.

Could we accomplish this by adding (optional) fields and properties to the TypeHandle struct?

The TypeHandle was always meant to be an opaque handle, moving to an interface better accomplishes that and matches the pattern in other contracts.

// an opaque handle to a type handle. See IMetadata.GetMethodTableData
public readonly struct TypeHandle
{
// TODO-Layering: These members should be accessible only to contract implementations.
public TypeHandle(TargetPointer address)
{
Address = address;
}
public TargetPointer Address { get; }
public bool IsNull => Address == 0;
}

Copilot AI review requested due to automatic review settings June 24, 2026 16:03
@max-charlamb max-charlamb force-pushed the dev/max-charlamb/rts-synthetic-typehandles branch from 2050b89 to 6522cf8 Compare June 24, 2026 16:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 54 out of 54 changed files in this pull request and generated 6 comments.

Comment on lines +27 to +30
public sealed class NullTypeHandle : ITypeHandle
{
public static readonly NullTypeHandle Instance = new();
private NullTypeHandle() { }
Comment on lines +23 to +25
public bool Equals(ITypeHandle? other)
=> other is TargetTypeHandle t && Address == t.Address;
public bool Equals(TargetTypeHandle other) => Address == other.Address;
Comment on lines +1193 to +1197
if (corElementType is CorElementType.Ptr or CorElementType.Byref or CorElementType.SzArray or CorElementType.Array)
{
var synthetic = new SyntheticTypeHandle(corElementType, typeHandle, rank);
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), synthetic);
return synthetic;
Comment on lines +1215 to 1218
foreach (ITypeHandle arg in retAndArgTypes)
{
if (arg.Address == TargetPointer.Null)
continue;
Comment on lines 1084 to 1087
// that we can return to SOS for pretty-printing.
// In the future we may want to return a TypeHandle instead of a MethodTable, and modify SOS to do more complete pretty-printing.
// DAC equivalent: src/coreclr/vm/typehandle.inl TypeHandle::GetMethodTable
// In the future we may want to return a ITypeHandle instead of a MethodTable, and modify SOS to do more complete pretty-printing.
// DAC equivalent: src/coreclr/vm/typehandle.inl ITypeHandle::GetMethodTable
if (rtsContract.IsFunctionPointer(foundTypeHandle, out _, out _) || rtsContract.IsPointer(foundTypeHandle))
Comment on lines 5384 to 5386
// Shared core implementation for TypeHandleToExpandedTypeInfo and GetObjectExpandedTypeInfo.
private void TypeHandleToExpandedTypeInfoImpl(IRuntimeTypeSystem rts, AreValueTypesBoxed boxed, TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo)
private void ITypeHandleToExpandedTypeInfoImpl(IRuntimeTypeSystem rts, AreValueTypesBoxed boxed, ITypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo)
{
Add SyntheticTypeHandle (internal) for Ptr, Byref, SzArray, and Array
types that the runtime has not loaded. GetConstructedType now returns
a synthetic handle on miss instead of null, enabling signature-decoding
contracts to treat all parameter types uniformly.

Synthetic-aware branches added to 10 RTS methods:
- GetSignatureCorElementType, HasTypeParam, GetTypeParam
- IsPointer, IsArray, ContainsGenericVariables
- GetModule, GetLoaderModule, IsLoaded
- GetConstructedType (synthesis on miss)

Also fixes GcSignatureTypeProvider to use IsNull instead of Address
comparison, so synthetics get properly classified.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb max-charlamb force-pushed the dev/max-charlamb/rts-synthetic-typehandles branch from 6522cf8 to 791163a Compare June 24, 2026 19:40
@rcj1

rcj1 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

The TypeHandle was always meant to be an opaque handle

Ok, sounds good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants