Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix handling of variant casting in the managed type system #91788

Merged
merged 1 commit into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions src/coreclr/tools/Common/TypeSystem/Common/CastingHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,9 @@ private static bool CanCastToClassOrInterface(this TypeDesc thisType, TypeDesc o

private static bool CanCastToInterface(this TypeDesc thisType, TypeDesc otherType, StackOverflowProtect protect)
{
if (!otherType.HasVariance)
// Interfaces that don't have variance can still behave variantly when arrays are involved.
bool arrayCovariance = thisType.IsSzArray && otherType.HasInstantiation;
if (!otherType.HasVariance && !arrayCovariance)
{
return thisType.CanCastToNonVariantInterface(otherType);
}
Expand All @@ -404,7 +406,7 @@ private static bool CanCastToInterface(this TypeDesc thisType, TypeDesc otherTyp

foreach (var interfaceType in thisType.RuntimeInterfaces)
{
if (interfaceType.CanCastByVarianceToInterfaceOrDelegate(otherType, protect))
if (interfaceType.CanCastByVarianceToInterfaceOrDelegate(otherType, protect, arrayCovariance))
{
return true;
}
Expand Down Expand Up @@ -432,7 +434,7 @@ private static bool CanCastToNonVariantInterface(this TypeDesc thisType, TypeDes
return false;
}

private static bool CanCastByVarianceToInterfaceOrDelegate(this TypeDesc thisType, TypeDesc otherType, StackOverflowProtect protectInput)
private static bool CanCastByVarianceToInterfaceOrDelegate(this TypeDesc thisType, TypeDesc otherType, StackOverflowProtect protectInput, bool arrayCovariance = false)
{
if (thisType == otherType)
{
Expand Down Expand Up @@ -467,23 +469,24 @@ private static bool CanCastByVarianceToInterfaceOrDelegate(this TypeDesc thisTyp

if (!arg.IsEquivalentTo(targetArg))
{
GenericParameterDesc openArgType = (GenericParameterDesc)instantiationOpen[i];
GenericVariance variance = arrayCovariance
? GenericVariance.Covariant : ((GenericParameterDesc)instantiationOpen[i]).Variance;

switch (openArgType.Variance)
switch (variance)
{
case GenericVariance.Covariant:
if (!arg.IsBoxedAndCanCastTo(targetArg, protect))
if (!arg.IsBoxedAndCanCastTo(targetArg, protect, arrayCovariance))
return false;
break;

case GenericVariance.Contravariant:
if (!targetArg.IsBoxedAndCanCastTo(arg, protect))
if (!targetArg.IsBoxedAndCanCastTo(arg, protect, arrayCovariance))
return false;
break;

default:
// non-variant
Debug.Assert(openArgType.Variance == GenericVariance.None);
Debug.Assert(variance == GenericVariance.None);
return false;
}
}
Expand Down Expand Up @@ -548,7 +551,7 @@ private static bool CanCastToClass(this TypeDesc thisType, TypeDesc otherType, S
return false;
}

private static bool IsBoxedAndCanCastTo(this TypeDesc thisType, TypeDesc otherType, StackOverflowProtect protect)
private static bool IsBoxedAndCanCastTo(this TypeDesc thisType, TypeDesc otherType, StackOverflowProtect protect, bool arrayCovariance)
{
TypeDesc fromUnderlyingType = thisType.UnderlyingType;

Expand All @@ -564,6 +567,14 @@ private static bool IsBoxedAndCanCastTo(this TypeDesc thisType, TypeDesc otherTy
return genericVariableFromParam.CanCastToInternal(otherType, protect);
}
}
else if (arrayCovariance && fromUnderlyingType.IsPrimitive)
{
TypeDesc toUnderlyingType = otherType.UnderlyingType;
if (GetNormalizedIntegralArrayElementType(fromUnderlyingType) == GetNormalizedIntegralArrayElementType(toUnderlyingType))
{
return true;
}
}

return false;
}
Expand Down
18 changes: 18 additions & 0 deletions src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/CastingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,36 @@ public void TestSameSizeArrayTypeCasting()
public void TestArrayInterfaceCasting()
{
TypeDesc intType = _context.GetWellKnownType(WellKnownType.Int32);
TypeDesc byteType = _context.GetWellKnownType(WellKnownType.Byte);
TypeDesc objectType = _context.GetWellKnownType(WellKnownType.Object);
TypeDesc stringType = _context.GetWellKnownType(WellKnownType.String);
TypeDesc intBasedEnumType = _testModule.GetType("Casting", "IntBasedEnum");
MetadataType iListType = _context.SystemModule.GetType("System.Collections", "IList");
MetadataType iListOfTType = _context.SystemModule.GetType("System.Collections.Generic", "IList`1");

InstantiatedType iListOfIntType = iListOfTType.MakeInstantiatedType(intType);
InstantiatedType iListOfObjectType = iListOfTType.MakeInstantiatedType(objectType);
InstantiatedType iListOfStringType = iListOfTType.MakeInstantiatedType(stringType);
TypeDesc intSzArrayType = intType.MakeArrayType();
TypeDesc byteSzArrayType = byteType.MakeArrayType();
TypeDesc objectSzArrayType = objectType.MakeArrayType();
TypeDesc stringSzArrayType = stringType.MakeArrayType();
TypeDesc intArrayType = intType.MakeArrayType(1);
TypeDesc intBasedEnumSzArrayType = intBasedEnumType.MakeArrayType();

Assert.True(intSzArrayType.CanCastTo(iListOfIntType));
Assert.True(intSzArrayType.CanCastTo(iListType));

Assert.False(intArrayType.CanCastTo(iListOfIntType));
Assert.True(intArrayType.CanCastTo(iListType));

Assert.True(intBasedEnumSzArrayType.CanCastTo(iListOfIntType));
Assert.False(byteSzArrayType.CanCastTo(iListOfIntType));

Assert.True(stringSzArrayType.CanCastTo(iListOfObjectType));
Assert.True(stringSzArrayType.CanCastTo(iListOfStringType));
Assert.True(objectSzArrayType.CanCastTo(iListOfObjectType));
Assert.False(objectSzArrayType.CanCastTo(iListOfStringType));
}

[Fact]
Expand Down
1 change: 0 additions & 1 deletion src/libraries/System.Linq/tests/ToArrayTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,6 @@ private enum Enum1
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/85146", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
public void ToArray_Cast()
{
Enum0[] source = { Enum0.First, Enum0.Second, Enum0.Third };
Expand Down
Loading