diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index de914ea0bdfdc..8d040a82d1210 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -5435,6 +5435,23 @@ GenTree* Compiler::impCastClassOrIsInstToTree( bool shouldExpandInline = true; bool isClassExact = impIsClassExact(pResolvedToken->hClass); + // ECMA-335 III.4.3: If typeTok is a nullable type, Nullable, it is interpreted as "boxed" T + // We can convert constant-ish tokens of nullable to its underlying type. + // However, when the type is shared generic parameter like Nullable>, the actual type will require + // runtime lookup. It's too complex to add another level of indirection in op2, fallback to the cast helper instead. + if (isClassExact && !(info.compCompHnd->getClassAttribs(pResolvedToken->hClass) & CORINFO_FLG_SHAREDINST)) + { + CORINFO_CLASS_HANDLE hClass = info.compCompHnd->getTypeForBox(pResolvedToken->hClass); + + if (hClass != pResolvedToken->hClass) + { + bool runtimeLookup; + pResolvedToken->hClass = hClass; + op2 = impTokenToHandle(pResolvedToken, &runtimeLookup); + assert(!runtimeLookup); + } + } + // Profitability check. // // Don't bother with inline expansion when jit is trying to generate code quickly diff --git a/src/tests/JIT/opt/Casts/castclass_valuetype.il b/src/tests/JIT/opt/Casts/castclass_valuetype.il new file mode 100644 index 0000000000000..03eee56ae07ae --- /dev/null +++ b/src/tests/JIT/opt/Casts/castclass_valuetype.il @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +.assembly extern mscorlib { auto } +.assembly extern xunit.core {} +.assembly extern System.Console { auto } +.assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) } + +.assembly 'castclass_valuetype' { } + +.class public auto Program extends [System.Runtime]System.Object +{ + .method public hidebysig static int32 Main() cil managed + { + .custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = ( + 01 00 00 00 + ) + .entrypoint + .maxstack 8 + + .try + { + // castclass valuetype + ldc.i4 1234 + box int32 + castclass int32 + unbox int32 + call instance string [System.Runtime]System.Int32::ToString() + call void [System.Console]System.Console::WriteLine(class [System.Runtime]System.String) + + // castclass nullable + ldc.i4 1234 + box int32 + castclass valuetype [System.Runtime]System.Nullable`1 + unbox int32 + call instance string [System.Runtime]System.Int32::ToString() + call void [System.Console]System.Console::WriteLine(class [System.Runtime]System.String) + + leave.s SUCCESS + } + catch [System.Runtime]System.Object + { + pop + leave.s FAIL + } + +SUCCESS: + ldstr "SUCCESS" + call void [System.Console]System.Console::WriteLine(class [System.Runtime]System.String) + ldc.i4 100 + ret + +FAIL: + ldstr "FAILED" + call void [System.Console]System.Console::WriteLine(class [System.Runtime]System.String) + ldc.i4 0 + ret + } +} \ No newline at end of file diff --git a/src/tests/JIT/opt/Casts/castclass_valuetype.ilproj b/src/tests/JIT/opt/Casts/castclass_valuetype.ilproj new file mode 100644 index 0000000000000..a7a65728eacd4 --- /dev/null +++ b/src/tests/JIT/opt/Casts/castclass_valuetype.ilproj @@ -0,0 +1,11 @@ + + + 1 + + + PdbOnly + + + + +