Skip to content

Do not treat Windows Runtime attribute types as Windows Runtime types - #2497

Open
Sergio0694 wants to merge 1 commit into
staging/3.0from
user/sergiopedri/erase-winrt-attribute-generics
Open

Do not treat Windows Runtime attribute types as Windows Runtime types#2497
Sergio0694 wants to merge 1 commit into
staging/3.0from
user/sergiopedri/erase-winrt-attribute-generics

Conversation

@Sergio0694

@Sergio0694 Sergio0694 commented Jul 29, 2026

Copy link
Copy Markdown
Member

Problem

Using a projected Windows Runtime attribute type as a generic type argument failed the whole build:

error CSWINRTINTEROPGEN0055: Failed to generate the type signature for type 'TestComponentCSharp.MyStringAttribute'.

type.GetCustomAttributes<SomeAttribute>() is enough to trigger it — it creates IEnumerable<SomeAttribute> and IEnumerator<SomeAttribute> instantiations in the assembly.

Root cause

Windows Runtime attributes are metadata-only: they are never activated and never cross the ABI boundary, so the projection deliberately emits no marshalling infrastructure for them — no default interface, no IID, no Impl type, no marshaller (everything in ProjectionGenerator.Namespace.cs is guarded on !type.IsAttributeType).

It did however still stamp them with [WindowsRuntimeType], which is precisely the statement "this type participates in Windows Runtime marshalling". Both consumers believed it and then failed to find any marshalling logic:

  • Interop generator. IsWindowsRuntimeType classifies a generic instantiation as a Windows Runtime one only when every type argument is a Windows Runtime type. That is why IEnumerable<SomeManagedClass> is not one, is never tracked, and the CCW instead gets IEnumerable<object> from the covariance expansion. Attribute types wrongly passed that check, so IEnumerable<SomeAttribute> went down the Windows Runtime path and SignatureGenerator.Class found neither a default interface nor an IID → CSWINRTINTEROPGEN0055.
  • Runtime. WindowsRuntimeMarshallingInfo.GetMetadataProviderType short-circuits on [WindowsRuntimeType] for non-generic types and returns the type as its own metadata provider. GetOrCreateComInterfaceForObject then calls GetComWrappersMarshaller(), which throws NotSupportedException instead of falling through to the opaque-IInspectable path that every other managed object takes.

One cause, two symptoms — the second only became reachable once the first was worked around.

Fix

Stop reporting attribute types as Windows Runtime types.

  • Projection writer — don't emit [WindowsRuntimeType] on attribute types. The .winmd stem is still recorded in the centralized ABI.WindowsRuntimeMetadataTypes lookup, since that is build-time only data for the other generators.
  • Interop generator — exclude them in IsWindowsRuntimeType / IsNotExclusiveToWindowsRuntimeType. This is still needed on top of the above, because reference projections carry no per-type marker at all: IsReferenceProjectionWindowsRuntimeType is assembly-level, so every type in a NuGet-shipped reference projection would otherwise still qualify.

Attribute types now behave like any managed type: List<SomeAttribute> is handled exactly like List<SomeManagedClass> — it still gets its own proxy/type-map entry, its CCW gets IIterable<IInspectable> from the covariance expansion, and the attribute instances marshal as opaque IInspectable objects.

The detection is a direct base-type comparison against System.Attribute. Windows Runtime attributes always derive directly from it (the type system does not allow an attribute to derive from another one), so no base-class-chain walk is needed.

Notes

An earlier revision of this PR type-erased the attribute type arguments to object in the interop generator instead. That treated the symptom rather than the cause: it needed a dedicated eraser, erasure call sites in three discovery entry points, careful ordering against the resolvability guards, and an extra parameter threaded through TryAddExposedInterfaceType for the dedup invariant — and it still left the runtime failure in place. All of that is gone; the existing managed/covariance path already does the right thing once the classification is correct.

It also removes the IsAssignableFrom call that was tripping the NullReferenceException reported in Washi1337/AsmResolver#770.

Tests

Two tests in src/Tests/UnitTest:

  • TestGenericsOverWindowsRuntimeAttributeTypes — the original repro (GetCustomAttributes<T>), then marshals a List<SomeAttribute> across the ABI through an IIterable<Object> property and round-trips it back. The test building covers the CSWINRTINTEROPGEN0055 failure; the assertions cover the GetComWrappersMarshaller one, since the native side enumerates the collection and marshals the attribute instances.
  • TestWindowsRuntimeAttributeTypeInterfaceEntriesAreNotDuplicated — reads the CCW's IIDs via IInspectable::GetIids and asserts IIterable<IInspectable> appears exactly once (a duplicate entry is invisible to QueryInterface, so this is the only way to observe it).

Validation

  • All five build tools build clean.
  • Full CsWinRT pipeline (projection ref gen → SDK/merged projection gen → impl gen → interop gen) verified end-to-end on ObjectLifetimeTests.Lifted.
  • Regenerated the TestComponentCSharp implementation projection and confirmed MyStringAttribute no longer carries [WindowsRuntimeType], that real runtime classes such as NonAgileClass still carry it plus their marshaller, and that the .winmd stem entry is still emitted.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@Sergio0694
Sergio0694 force-pushed the user/sergiopedri/erase-winrt-attribute-generics branch from 4e9e553 to 5e67017 Compare July 29, 2026 05:20
@Sergio0694
Sergio0694 force-pushed the user/sergiopedri/erase-winrt-attribute-generics branch from 590330c to 3990dee Compare July 29, 2026 19:32
@Sergio0694
Sergio0694 marked this pull request as draft July 29, 2026 21:57
@Sergio0694
Sergio0694 force-pushed the user/sergiopedri/erase-winrt-attribute-generics branch from 0bb5607 to 92217c3 Compare July 29, 2026 22:26
@Sergio0694 Sergio0694 changed the title Erase Windows Runtime attribute types in generic instantiations Do not treat Windows Runtime attribute types as Windows Runtime types Jul 29, 2026
Windows Runtime attributes are metadata-only: they are never activated and
never cross the ABI boundary, so the projection deliberately emits no
marshalling infrastructure for them (no default interface, no IID, no 'Impl'
type, no marshaller). It did however still stamp them with the
'[WindowsRuntimeType]' marker, which is exactly the statement that a type
participates in Windows Runtime marshalling. Both consumers believed it and
then failed to find any marshalling logic:

  - The interop generator classifies a generic instantiation as a Windows
    Runtime one only when every type argument is a Windows Runtime type, so
    'IEnumerable<SomeAttribute>' was routed into the Windows Runtime path and
    then failed the whole build with 'CSWINRTINTEROPGEN0055', as no type
    signature can be computed for the attribute type.
  - The runtime resolved the attribute type as its own metadata provider and
    then threw from 'GetComWrappersMarshaller' when an instance was marshalled.

Attribute types are now excluded from both, so they behave like any managed
type: 'List<SomeAttribute>' is handled exactly like 'List<SomeManagedClass>'
(its CCW gets 'IIterable<IInspectable>' from the covariance expansion, and it
still gets its own proxy entry), and the attribute instances marshal as opaque
'IInspectable' objects. Reference projections need the structural check on the
generator side, as they carry no per-type marker at all.

The '.winmd' stem for attribute types is still recorded in the centralized
metadata lookup, as that is build-time only data for the other generators.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8e084ca2-e552-4f3c-82dd-cf30a5970335
@Sergio0694
Sergio0694 force-pushed the user/sergiopedri/erase-winrt-attribute-generics branch from 92217c3 to 8d143d3 Compare July 29, 2026 22:31
@Sergio0694
Sergio0694 marked this pull request as ready for review July 29, 2026 22:32
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

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.

Bug: interop generator fails on generic instantiations over Windows Runtime attribute types

1 participant