Skip to content

Validate_ProxyTypeMapping test failing with trimming enabled #121912

@MichalStrehovsky

Description

@MichalStrehovsky

In #121697 I'm enabling trimming on the src/tests tree so that we're testing a configuration that is closer to the AOT default that we recommend externally (full trimming with TrimMode=full instead of TrimMode=partial).

I'm seeing the Validate_ProxyTypeMapping test failing.

Validate_ProxyTypeMapping
System.Collections.Generic.KeyNotFoundException: The given key 'C1' was not present in the dictionary.
   at System.Runtime.InteropServices.TypeMapLazyDictionary.AssociatedTypeMapDictionary.get_Item(Type) + 0x30
   at TypeMap.Validate_ProxyTypeMapping() + 0x34
   at __GeneratedMainWrapper.Main() + 0x12

[Fact]
public static void Validate_ProxyTypeMapping()
{
Console.WriteLine(nameof(Validate_ProxyTypeMapping));
IReadOnlyDictionary<Type, Type> map = TypeMapping.GetOrCreateProxyTypeMapping<TypicalUseCase>();
Assert.Equal(typeof(C1), map[typeof(C1)]);
Assert.Equal(typeof(S1), map[typeof(S1)]);
Assert.Equal(typeof(C1), map[typeof(Guid)]);
Assert.Equal(typeof(S1), map[typeof(string)]);
Assert.Equal(typeof(C1), map[typeof(List<int>)]);
Assert.Equal(typeof(S1), map[typeof(List<>)]);
Assert.Equal(typeof(C1), map[typeof(C1.I1)]);
Assert.Equal(typeof(S1), map[typeof(C1.I2<int>)]);
Assert.Equal(typeof(C1), map[typeof(C2<int>)]);
Assert.Equal(typeof(S1), map[typeof(C2<>)]);
Assert.Equal(typeof(C1), map[typeof(int[])]);
Assert.Equal(typeof(S1), map[typeof(int*)]);
Assert.True(map.TryGetValue(typeof(C1), out Type? _));
Assert.True(map.TryGetValue(typeof(S1), out Type? _));
Assert.True(map.TryGetValue(typeof(Guid), out Type? _));
Assert.True(map.TryGetValue(typeof(string), out Type? _));
Assert.True(map.TryGetValue(typeof(List<int>), out Type? _));
Assert.True(map.TryGetValue(typeof(List<>), out Type? _));
Assert.True(map.TryGetValue(typeof(C1.I1), out Type? _));
Assert.True(map.TryGetValue(typeof(C1.I2<int>), out Type? _));
Assert.True(map.TryGetValue(typeof(C2<int>), out Type? _));
Assert.True(map.TryGetValue(typeof(C2<>), out Type? _));
Assert.True(map.TryGetValue(typeof(int[]), out Type? _));
Assert.True(map.TryGetValue(typeof(int*), out Type? _));
// Validate strict type mapping, no implicit conversions.
Assert.False(map.TryGetValue(typeof(object), out Type? _));
Assert.False(map.TryGetValue(typeof(void*), out Type? _));
Assert.False(map.TryGetValue(typeof(IntPtr), out Type? _));
}

From my read of the TypeMap trimming behavior spec, this looks like a test bug, not a product bug.

An entry in the Proxy Type Map is included when the "source type" is referenced in one of the following ways:
- The argument to the `ldtoken` IL instruction when `DynamicallyAccessedMembersAttribute` is specified with one of the flags that preserves constructors for the storage location.
- Calls to `Type.GetType` with a constant string representing the type name when `DynamicallyAccessedMembersAttribute` is specified with one of the flags that preserves constructors for the storage location.
- The type of a method argument to the `newobj` instruction.
- The generic argument to the `Activator.CreateInstance<T>` method.
- The argument to the `box` instruction.
- The argument to the `newarr` instruction.
- The argument to the `mkrefany` instruction.
- The argument to the `refanyval` instruction.
If the type is an interface type and the user could possibly see a `RuntimeTypeHandle` for the type as part of a casting or virtual method resolution operation (such as with `System.Runtime.InteropServices.IDynamicInterfaceCastable`), then the following cases also apply:
- The argument to the `isinst` IL instruction.
- The argument to the `castclass` IL instruction.
- The owning type of the method argument to `callvirt`, or `ldvirtftn`.
Finally, if the trimming tool determines that it is impossible to retrieve a `System.Type` instance the represents the "source type" at runtime, then the entry may be omitted from the Proxy Type Map as its existence is unobservable.

The type is only referenced with a typeof (therefore maps to the ldtoken rule), but there's no annotation to force constructor reflectability (so the rest of the rule is not satisfied). The compiler therefore determines that the type cannot be seen as allocated and generates only limited metadata for it (and that includes not making it present in the TypeMap).

If we determine this is indeed a test bug, I can make a PR that switches the typeof to a new X().GetType() (which should satisfy the heuristic rules).

Otherwise we have a native AOT product bug.

Cc @Sergio0694 @AaronRobinsonMSFT @jkoritzinsky @dotnet/illink

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    No status

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions