Skip to content

[API Proposal]: MakeWeakTypeReference as trimming indicator #74307

@hez2010

Description

@hez2010

Background and motivation

While we were working with trimming, we found that we often want to express "code only needed if a type is preserved", but currently we do not have such thing in C#.

For example, in XAML frameworks, we use URLs to locate resources. With source generator, the code of the resource locator which is generated by a source generator may consist with a method with plenty of if statements, for example:

// auto-generated by a source generator
object? LoadResource(string path)
{
    if (path == "ms-appx://MyAssembly/My/Fancy/XamlControl1Style") return XamlLoader.Load<XamlControl1Style>();
    if (path == "ms-appx://MyAssembly/Another/XamlControl2Style") return XamlLoader.Load<XamlControl2Style>();
    return null;
}

With this source generator approach, we can achieve trimming compatibility. However, this would result in all types referenced in LoadResource being preserved even they are not being actually used. In this case, we only want XamlControl1Style to be preserved if XamlControl1 is referenced in other pieces of code.

API Proposal

namespace System.Diagnostics.CodeAnalysis;

public class ControlFlow
{
    public static bool MakeWeakTypeReference<T>() => true;
}

API Usage

object? LoadResource(string path)
{
    if (ControlFlow.MakeWeakTypeReference<XamlControl1>())
        if (path == "ms-appx://MyAssembly/My/Fancy/XamlControl1Style")
            return XamlLoader.Load<XamlControl1Style>();
    if (ControlFlow.MakeWeakTypeReference<XamlControl2>())
        if (path == "ms-appx://MyAssembly/Another/XamlControl2Style")
            return XamlLoader.Load<XamlControl2Style>();
    return null;
}

It behaves as a no-op which always returns true, but if there are no other type references to XamlControl1, the whole block of the if statement can be safely trimmed, which produces code after trimming like below:

object? LoadResource(string path)
{
    if (path == "ms-appx://MyAssembly/Another/XamlControl2Style")
        return XamlLoader.Load<XamlControl2Style>();
    return null;
}

Therefore, all types related to XamlControl1 and XamlControl1Type can also be trimmed away.

This should also support and and or so that we can express multiple types as a single prerequisite:

if (ControlFlow.MakeWeakTypeReference<Foo>() && ControlFlow.MakeWeakTypeReference<Bar>())
{
    // only preserved if both Foo and Bar are referenced
}

if (ControlFlow.MakeWeakTypeReference<Foo>() || ControlFlow.MakeWeakTypeReference<Bar>())
{
    // only preserved if either Foo or Bar is referenced
}

// Bogus
if (!ControlFlow.MakeWeakTypeReference<Foo>())
{
    // no-op, can never reach because `!ControlFlow.MakeWeakTypeReference<Foo>()` is always false, maybe a warning should be emitted by the compiler or illink?
}

Alternative Designs

No response

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-AssemblyLoader-coreclrlinkable-frameworkIssues associated with delivering a linker friendly framework

    Type

    No type

    Projects

    Status

    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions