diff --git a/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs index 47b229a184f84c..d3bdc11382b577 100644 --- a/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs @@ -1757,16 +1757,29 @@ private TypeCompareState compareTypesForCast(CORINFO_CLASS_STRUCT_* fromClass, C { result = TypeCompareState.Must; } - // For negative results, the unknown type parameter in - // fromClass might match some instantiated interface, - // either directly or via variance. + // We have __Canon parameter(s) in fromClass, somewhere. // - // However, CanCastTo will report failure in such cases since - // __Canon won't match the instantiated type on the - // interface (which can't be __Canon since we screened out - // canonical subtypes for toClass above). So only report - // failure if the interface is not instantiated. - else if (!toType.HasInstantiation) + // In CanCastTo, these __Canon(s) won't match the interface or + // instantiated types on the interface, so CanCastTo may + // return false negatives. + // + // Only report MustNot if the fromClass is not __Canon + // and the interface is not instantiated; then there is + // no way for the fromClass __Canon(s) to confuse things. + // + // __Canon -> IBar May + // IFoo<__Canon> -> IFoo May + // IFoo<__Canon> -> IBar MustNot + // + else if (fromType.IsCanonicalDefinitionType(CanonicalFormKind.Any)) + { + result = TypeCompareState.May; + } + else if (toType.HasInstantiation) + { + result = TypeCompareState.May; + } + else { result = TypeCompareState.MustNot; } diff --git a/src/coreclr/src/vm/jitinterface.cpp b/src/coreclr/src/vm/jitinterface.cpp index 6febb1f20b2e07..a822f559a1bc4c 100644 --- a/src/coreclr/src/vm/jitinterface.cpp +++ b/src/coreclr/src/vm/jitinterface.cpp @@ -4495,16 +4495,29 @@ TypeCompareState CEEInfo::compareTypesForCast( { result = TypeCompareState::Must; } - // For negative results, the unknown type parameter in - // fromClass might match some instantiated interface, - // either directly or via variance. + // We have __Canon parameter(s) in fromClass, somewhere. // - // However, CanCastTo will report failure in such cases since - // __Canon won't match the instantiated type on the - // interface (which can't be __Canon since we screened out - // canonical subtypes for toClass above). So only report - // failure if the interface is not instantiated. - else if (!toHnd.HasInstantiation()) + // In CanCastTo, these __Canon(s) won't match the interface or + // instantiated types on the interface, so CanCastTo may + // return false negatives. + // + // Only report MustNot if the fromClass is not __Canon + // and the interface is not instantiated; then there is + // no way for the fromClass __Canon(s) to confuse things. + // + // __Canon -> IBar May + // IFoo<__Canon> -> IFoo May + // IFoo<__Canon> -> IBar MustNot + // + else if (fromHnd == TypeHandle(g_pCanonMethodTableClass)) + { + result = TypeCompareState::May; + } + else if (toHnd.HasInstantiation()) + { + result = TypeCompareState::May; + } + else { result = TypeCompareState::MustNot; } diff --git a/src/coreclr/tests/src/JIT/opt/Casts/shared2.cs b/src/coreclr/tests/src/JIT/opt/Casts/shared2.cs new file mode 100644 index 00000000000000..1c155ec125fa71 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/Casts/shared2.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +interface IBar {} +interface IFoo {} +class C : IBar {} +class C : IFoo {} +struct S {} +struct SBar : IBar {} + +// More tests for shared types passing through compareTypesForCast + +class X +{ + static int _errors; + + static void IsTrue(bool expression, [CallerLineNumber] int line = 0, [CallerFilePath] string file = "") + { + if (!expression) + { + Console.WriteLine($"{file}:L{line} test failed (expected: true)."); + _errors++; + } + } + + static void IsFalse(bool expression, [CallerLineNumber] int line = 0, [CallerFilePath] string file = "") + { + if (expression) + { + Console.WriteLine($"{file}:L{line} test failed (expected: false)."); + _errors++; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool A1() + { + return typeof(IFoo).IsAssignableFrom(typeof(T)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool A2() + { + return typeof(IBar).IsAssignableFrom(typeof(T)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool I1(T t) + { + return t is IFoo; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool I2(T t) + { + return t is IBar; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool C1(T t) + { + return (t as IFoo) != null; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool C2(T t) + { + return (t as IBar) != null; + } + + public static int Main() + { + var c = new C(); + var ci = new C(); + var cs = new C(); + var s = new S(); + var sb = new SBar(); + + IsTrue(A1>()); + IsFalse(A1>()); + IsFalse(A1()); + IsFalse(A1()); + IsFalse(A1()); + + IsTrue(I1(cs)); + IsFalse(I1(ci)); + IsFalse(I1(c)); + IsFalse(I1(s)); + IsFalse(I1(sb)); + + IsTrue(C1(cs)); + IsFalse(C1(ci)); + IsFalse(C1(c)); + IsFalse(C1(s)); + IsFalse(C1(sb)); + + IsFalse(A2>()); + IsFalse(A2>()); + IsTrue(A2()); + IsFalse(A2()); + IsTrue(A2()); + + IsFalse(I2(cs)); + IsFalse(I2(ci)); + IsTrue(I2(c)); + IsFalse(I2(s)); + IsTrue(I2(sb)); + + IsFalse(C2(cs)); + IsFalse(C2(ci)); + IsTrue(C2(c)); + IsFalse(C2(s)); + IsTrue(C2(sb)); + + if (_errors == 0) Console.WriteLine("Passed"); + return _errors > 0 ? -1 : 100; + } +} diff --git a/src/coreclr/tests/src/JIT/opt/Casts/shared2.csproj b/src/coreclr/tests/src/JIT/opt/Casts/shared2.csproj new file mode 100644 index 00000000000000..97176f997545d6 --- /dev/null +++ b/src/coreclr/tests/src/JIT/opt/Casts/shared2.csproj @@ -0,0 +1,12 @@ + + + Exe + + + PdbOnly + True + + + + +