Skip to content

Make WindowsRuntimeDefaultInterface attribute trimmable#2350

Open
Sergio0694 wants to merge 9 commits intostaging/3.0from
dev/trimmable-default-interface-attribute
Open

Make WindowsRuntimeDefaultInterface attribute trimmable#2350
Sergio0694 wants to merge 9 commits intostaging/3.0from
dev/trimmable-default-interface-attribute

Conversation

@Sergio0694
Copy link
Member

Summary

Move the [WindowsRuntimeDefaultInterface] attribute from individual projected runtime class types to a centralized WindowsRuntimeDefaultInterfaces static class in the ABI namespace. This decouples the default interface Type references from the class types themselves, allowing the trimmer to remove unused interface types at publish time.

Previously, each projected runtime class carried a [WindowsRuntimeDefaultInterface(typeof(IDefaultInterface))] attribute directly on the class. Because attributes are not trimmable, this forced the linker to preserve the interface type even when it was never used at runtime. The attribute is only consumed by the interop generator (WinRT.Interop.Generator) to compute WinRT type signatures.

Changes

Attribute definition (WindowsRuntimeDefaultInterfaceAttribute)

  • Changed from single Type parameter (interfaceType) to two Type parameters (runtimeClassType, interfaceType)
  • Set AllowMultiple = true since the centralized type needs multiple attribute instances

Code generator (cswinrt.exe)

  • Removed per-class [WindowsRuntimeDefaultInterface] emission from write_class
  • Added add_default_interface_entry to accumulate (class, interface) pairs during parallel namespace processing
  • Added write_default_interfaces_class to emit a single WindowsRuntimeDefaultInterfaces.cs file after all namespaces are processed, with entries sorted for deterministic output
  • Guarded with !settings.reference_projection since the file is only needed for implementation assemblies
  • Used write_begin() for the file header, consistent with all other generated files

Interop generator (WinRT.Interop.Generator)

  • Updated TryGetDefaultInterfaceFromAttribute to look up the centralized ABI.WindowsRuntimeDefaultInterfaces type instead of searching for the attribute on each individual class
  • Added GetDefaultInterfacesLookup extension method on ModuleDefinition following the same caching pattern as GetTopLevelTypesLookup: a ConditionalWeakTable holding a FrozenDictionary<(Namespace, Name), TypeSignature> for O(1) lookups
  • Removed the InteropReferences parameter from TryGetDefaultInterfaceFromAttribute since attribute type validation is no longer needed (the centralized type only has these attributes)

Bug fix

  • Fixed write_begin_interface_iids in type_writers.h which was hardcoding version 0.0.0-private.0 instead of using VERSION_STRING

Generated output (before → after)

Before (on each class):

[WindowsRuntimeDefaultInterface(typeof(IHttpMethod))]
public sealed class HttpMethod : WindowsRuntimeObject, ...

After (centralized file):

// WindowsRuntimeDefaultInterfaces.cs
namespace ABI
{
[WindowsRuntimeDefaultInterface(typeof(global::Windows.Web.Http.HttpMethod), typeof(global::ABI.Windows.Web.Http.IHttpMethod))]
// ... one attribute per runtime class ...
internal static class WindowsRuntimeDefaultInterfaces;
}

@Sergio0694 Sergio0694 requested a review from manodasanW March 19, 2026 17:27
Sergio0694 and others added 5 commits March 19, 2026 10:48
Change the attribute to accept both the runtime class type and the default
interface type as parameters, rather than just the interface type. This
allows the attribute to be applied to a centralized lookup type instead
of individual class types, enabling trimming of unused interface types.

Also set AllowMultiple = true since the centralized type will need
multiple attribute instances for all projected runtime classes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of emitting [WindowsRuntimeDefaultInterface] on each projected
runtime class, accumulate all default interface entries during code
generation and emit them on a centralized 'WindowsRuntimeDefaultInterfaces'
static class in the ABI namespace. This decouples the interface type
references from the class types, enabling trimming of unused interfaces.

The entries are collected in a concurrent map during parallel namespace
processing and written in sorted order to a dedicated file after all
namespaces have been processed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update TryGetDefaultInterfaceFromAttribute to look up the
WindowsRuntimeDefaultInterfaces type in the ABI namespace of the
projection assembly, and iterate its attributes to find the default
interface for the requested class type. This replaces the previous
approach of looking up the attribute on each individual class type.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add GetDefaultInterfacesLookup extension method on ModuleDefinition,
following the same caching pattern as GetTopLevelTypesLookup. This builds
a FrozenDictionary keyed by (Namespace, Name) from the attributes on the
centralized WindowsRuntimeDefaultInterfaces type, cached via a
ConditionalWeakTable for automatic cleanup.

Update TryGetDefaultInterfaceFromAttribute to use this cached lookup
instead of iterating all attributes on each invocation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace use of GetTopLevelTypesLookup with a direct scan of module.TopLevelTypes to find the ABI.WindowsRuntimeDefaultInterfaces type and return an empty frozen map if it's missing. Simplify attribute handling by directly using classType.Namespace/name as the lookup key (no Resolve call) and collect runtime-class→default-interface pairs. Add a System using and a minor formatting cleanup for a TryGetValue call in SignatureGenerator.
@Sergio0694 Sergio0694 force-pushed the dev/trimmable-default-interface-attribute branch 2 times, most recently from 11573f5 to 57518ea Compare March 19, 2026 19:09
Sergio0694 and others added 2 commits March 19, 2026 12:57
Use write_file_header() followed by only the minimal usings needed:
'using WindowsRuntime;' for the attribute type and the CSWINRT3001
pragma suppression. This avoids importing namespaces that may not
exist in all projection compilation contexts.

Move write_file_header() above its first call site to avoid a
forward reference compilation error (C3861).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the hardcoded '0.0.0-private.0' version string with the
VERSION_STRING constant, matching all other generated file headers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Sergio0694 Sergio0694 force-pushed the dev/trimmable-default-interface-attribute branch from 57518ea to 9da7f1d Compare March 19, 2026 19:57
Sergio0694 and others added 2 commits March 19, 2026 14:18
Clear the writer's current namespace before generating interface
type names so that all type references, including generic type
arguments, are fully qualified with 'global::'. Without this,
types in the same namespace as the class being processed would
be emitted without qualification, causing compilation errors
when the names are emitted in the ABI namespace context.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Some generic type arguments (e.g. Guid) are emitted as unqualified
names by the writer. Add 'using System;' so these types resolve
correctly in the ABI namespace context.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant