CORINFO_RESOLVED_TOKEN* pResolvedToken,
bool isCastClass);
+ GenTree* impOptimizeCastClassOrIsInst(GenTree* op1, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool isCastClass);
+
bool VarTypeIsMultiByteAndCanEnreg(var_types type,
CORINFO_CLASS_HANDLE typeClass,
unsigned* typeSize,
return type;
}
+//------------------------------------------------------------------------
+// impOptimizeCastClassOrIsInst: attempt to resolve a cast when jitting
+//
+// Arguments:
+// op1 - value to cast
+// pResolvedToken - resolved token for type to cast to
+// isCastClass - true if this is a castclass, false if isinst
+//
+// Return Value:
+// tree representing optimized cast, or null if no optimization possible
+
+GenTree* Compiler::impOptimizeCastClassOrIsInst(GenTree* op1, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool isCastClass)
+{
+ assert(op1->TypeGet() == TYP_REF);
+
+ // Don't optimize for minopts or debug codegen.
+ if (opts.compDbgCode || opts.MinOpts())
+ {
+ return nullptr;
+ }
+
+ // See what we know about the type of the object being cast.
+ bool isExact = false;
+ bool isNonNull = false;
+ CORINFO_CLASS_HANDLE fromClass = gtGetClassHandle(op1, &isExact, &isNonNull);
+ GenTree* optResult = nullptr;
+
+ if (fromClass != nullptr)
+ {
+ CORINFO_CLASS_HANDLE toClass = pResolvedToken->hClass;
+ JITDUMP("\nConsidering optimization of %s from %s%p (%s) to %p (%s)\n", isCastClass ? "castclass" : "isinst",
+ isExact ? "exact " : "", fromClass, info.compCompHnd->getClassName(fromClass), toClass,
+ info.compCompHnd->getClassName(toClass));
+
+ // Perhaps we know if the cast will succeed or fail.
+ TypeCompareState castResult = info.compCompHnd->compareTypesForCast(fromClass, toClass);
+
+ if (castResult == TypeCompareState::Must)
+ {
+ // Cast will succeed, result is simply op1.
+ JITDUMP("Cast will succeed, optimizing to simply return input\n");
+ return op1;
+ }
+ else if (castResult == TypeCompareState::MustNot)
+ {
+ // See if we can sharpen exactness by looking for final classes
+ if (!isExact)
+ {
+ DWORD flags = info.compCompHnd->getClassAttribs(fromClass);
+ DWORD flagsMask = CORINFO_FLG_FINAL | CORINFO_FLG_MARSHAL_BYREF | CORINFO_FLG_CONTEXTFUL |
+ CORINFO_FLG_VARIANCE | CORINFO_FLG_ARRAY;
+ isExact = ((flags & flagsMask) == CORINFO_FLG_FINAL);
+ }
+
+ // Cast to exact type will fail. Handle case where we have
+ // an exact type (that is, fromClass is not a subtype)
+ // and we're not going to throw on failure.
+ if (isExact && !isCastClass)
+ {
+ JITDUMP("Cast will fail, optimizing to return null\n");
+ GenTree* result = gtNewIconNode(0, TYP_REF);
+
+ // If the cast was fed by a box, we can remove that too.
+ if (op1->IsBoxedValue())
+ {
+ JITDUMP("Also removing upstream box\n");
+ gtTryRemoveBoxUpstreamEffects(op1);
+ }
+
+ return result;
+ }
+ else if (isExact)
+ {
+ JITDUMP("Not optimizing failing castclass (yet)\n");
+ }
+ else
+ {
+ JITDUMP("Can't optimize since fromClass is inexact\n");
+ }
+ }
+ else
+ {
+ JITDUMP("Result of cast unknown, must generate runtime test\n");
+ }
+ }
+ else
+ {
+ JITDUMP("\nCan't optimize since fromClass is unknown\n");
+ }
+
+ return nullptr;
+}
+
//------------------------------------------------------------------------
// impCastClassOrIsInstToTree: build and import castclass/isinst
//
break;
case CEE_ISINST:
-
+ {
/* Get the type token */
assertImp(sz == sizeof(unsigned));
op1 = impPopStack().val;
-#ifdef FEATURE_READYTORUN_COMPILER
- if (opts.IsReadyToRun())
+ GenTree* optTree = impOptimizeCastClassOrIsInst(op1, &resolvedToken, false);
+
+ if (optTree != nullptr)
+ {
+ impPushOnStack(optTree, tiRetVal);
+ }
+ else
{
- GenTreeCall* opLookup =
- impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_ISINSTANCEOF, TYP_REF,
- gtNewArgList(op1));
- usingReadyToRunHelper = (opLookup != nullptr);
- op1 = (usingReadyToRunHelper ? opLookup : op1);
- if (!usingReadyToRunHelper)
+#ifdef FEATURE_READYTORUN_COMPILER
+ if (opts.IsReadyToRun())
{
- // TODO: ReadyToRun: When generic dictionary lookups are necessary, replace the lookup call
- // and the isinstanceof_any call with a single call to a dynamic R2R cell that will:
- // 1) Load the context
- // 2) Perform the generic dictionary lookup and caching, and generate the appropriate stub
- // 3) Perform the 'is instance' check on the input object
- // Reason: performance (today, we'll always use the slow helper for the R2R generics case)
+ GenTreeCall* opLookup =
+ impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_ISINSTANCEOF, TYP_REF,
+ gtNewArgList(op1));
+ usingReadyToRunHelper = (opLookup != nullptr);
+ op1 = (usingReadyToRunHelper ? opLookup : op1);
- op2 = impTokenToHandle(&resolvedToken, nullptr, FALSE);
- if (op2 == nullptr)
- { // compDonotInline()
- return;
+ if (!usingReadyToRunHelper)
+ {
+ // TODO: ReadyToRun: When generic dictionary lookups are necessary, replace the lookup call
+ // and the isinstanceof_any call with a single call to a dynamic R2R cell that will:
+ // 1) Load the context
+ // 2) Perform the generic dictionary lookup and caching, and generate the appropriate
+ // stub
+ // 3) Perform the 'is instance' check on the input object
+ // Reason: performance (today, we'll always use the slow helper for the R2R generics case)
+
+ op2 = impTokenToHandle(&resolvedToken, nullptr, FALSE);
+ if (op2 == nullptr)
+ { // compDonotInline()
+ return;
+ }
}
}
- }
- if (!usingReadyToRunHelper)
+ if (!usingReadyToRunHelper)
#endif
- {
- op1 = impCastClassOrIsInstToTree(op1, op2, &resolvedToken, false);
- }
- if (compDonotInline())
- {
- return;
- }
-
- impPushOnStack(op1, tiRetVal);
+ {
+ op1 = impCastClassOrIsInstToTree(op1, op2, &resolvedToken, false);
+ }
+ if (compDonotInline())
+ {
+ return;
+ }
+ impPushOnStack(op1, tiRetVal);
+ }
break;
+ }
case CEE_REFANYVAL:
// At this point we expect typeRef to contain the token, op1 to contain the value being cast,
// and op2 to contain code that creates the type handle corresponding to typeRef
CASTCLASS:
+ {
+ GenTree* optTree = impOptimizeCastClassOrIsInst(op1, &resolvedToken, true);
-#ifdef FEATURE_READYTORUN_COMPILER
- if (opts.IsReadyToRun())
+ if (optTree != nullptr)
+ {
+ impPushOnStack(optTree, tiRetVal);
+ }
+ else
{
- GenTreeCall* opLookup = impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_CHKCAST,
- TYP_REF, gtNewArgList(op1));
- usingReadyToRunHelper = (opLookup != nullptr);
- op1 = (usingReadyToRunHelper ? opLookup : op1);
- if (!usingReadyToRunHelper)
+#ifdef FEATURE_READYTORUN_COMPILER
+ if (opts.IsReadyToRun())
{
- // TODO: ReadyToRun: When generic dictionary lookups are necessary, replace the lookup call
- // and the chkcastany call with a single call to a dynamic R2R cell that will:
- // 1) Load the context
- // 2) Perform the generic dictionary lookup and caching, and generate the appropriate stub
- // 3) Check the object on the stack for the type-cast
- // Reason: performance (today, we'll always use the slow helper for the R2R generics case)
+ GenTreeCall* opLookup =
+ impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_CHKCAST, TYP_REF,
+ gtNewArgList(op1));
+ usingReadyToRunHelper = (opLookup != nullptr);
+ op1 = (usingReadyToRunHelper ? opLookup : op1);
- op2 = impTokenToHandle(&resolvedToken, nullptr, FALSE);
- if (op2 == nullptr)
- { // compDonotInline()
- return;
+ if (!usingReadyToRunHelper)
+ {
+ // TODO: ReadyToRun: When generic dictionary lookups are necessary, replace the lookup call
+ // and the chkcastany call with a single call to a dynamic R2R cell that will:
+ // 1) Load the context
+ // 2) Perform the generic dictionary lookup and caching, and generate the appropriate
+ // stub
+ // 3) Check the object on the stack for the type-cast
+ // Reason: performance (today, we'll always use the slow helper for the R2R generics case)
+
+ op2 = impTokenToHandle(&resolvedToken, nullptr, FALSE);
+ if (op2 == nullptr)
+ { // compDonotInline()
+ return;
+ }
}
}
- }
- if (!usingReadyToRunHelper)
+ if (!usingReadyToRunHelper)
#endif
- {
- op1 = impCastClassOrIsInstToTree(op1, op2, &resolvedToken, true);
- }
- if (compDonotInline())
- {
- return;
- }
+ {
+ op1 = impCastClassOrIsInstToTree(op1, op2, &resolvedToken, true);
+ }
+ if (compDonotInline())
+ {
+ return;
+ }
- /* Push the result back on the stack */
- impPushOnStack(op1, tiRetVal);
- break;
+ /* Push the result back on the stack */
+ impPushOnStack(op1, tiRetVal);
+ }
+ }
+ break;
case CEE_THROW:
{
IAsyncStateMachineBox box = GetStateMachineBox(ref stateMachine);
- // TODO https://github.com/dotnet/coreclr/issues/12877:
- // Once the JIT is able to recognize "awaiter is ITaskAwaiter" and "awaiter is IConfiguredTaskAwaiter",
- // use those in order to a) consolidate a lot of this code, and b) handle all Task/Task<T> and not just
- // the few types special-cased here. For now, handle common {Configured}TaskAwaiter. Having the types
- // explicitly listed here allows the JIT to generate the best code for them; otherwise we'll fall through
- // to the later workaround.
- if (typeof(TAwaiter) == typeof(TaskAwaiter) ||
- typeof(TAwaiter) == typeof(TaskAwaiter<object>) ||
- typeof(TAwaiter) == typeof(TaskAwaiter<string>) ||
- typeof(TAwaiter) == typeof(TaskAwaiter<byte[]>) ||
- typeof(TAwaiter) == typeof(TaskAwaiter<bool>) ||
- typeof(TAwaiter) == typeof(TaskAwaiter<byte>) ||
- typeof(TAwaiter) == typeof(TaskAwaiter<int>) ||
- typeof(TAwaiter) == typeof(TaskAwaiter<long>))
+ // TThe null tests here ensure that the jit can optimize away the interface
+ // tests when TAwaiter is is a ref type.
+ if ((null != (object)default(TAwaiter)) && (awaiter is ITaskAwaiter))
{
ref TaskAwaiter ta = ref Unsafe.As<TAwaiter, TaskAwaiter>(ref awaiter); // relies on TaskAwaiter/TaskAwaiter<T> having the same layout
TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, continueOnCapturedContext: true);
}
- else if (
- typeof(TAwaiter) == typeof(ConfiguredTaskAwaitable.ConfiguredTaskAwaiter) ||
- typeof(TAwaiter) == typeof(ConfiguredTaskAwaitable<object>.ConfiguredTaskAwaiter) ||
- typeof(TAwaiter) == typeof(ConfiguredTaskAwaitable<string>.ConfiguredTaskAwaiter) ||
- typeof(TAwaiter) == typeof(ConfiguredTaskAwaitable<byte[]>.ConfiguredTaskAwaiter) ||
- typeof(TAwaiter) == typeof(ConfiguredTaskAwaitable<bool>.ConfiguredTaskAwaiter) ||
- typeof(TAwaiter) == typeof(ConfiguredTaskAwaitable<byte>.ConfiguredTaskAwaiter) ||
- typeof(TAwaiter) == typeof(ConfiguredTaskAwaitable<int>.ConfiguredTaskAwaiter) ||
- typeof(TAwaiter) == typeof(ConfiguredTaskAwaitable<long>.ConfiguredTaskAwaiter))
+ else if ((null != (object)default(TAwaiter)) && (awaiter is IConfiguredTaskAwaiter))
{
ref ConfiguredTaskAwaitable.ConfiguredTaskAwaiter ta = ref Unsafe.As<TAwaiter, ConfiguredTaskAwaitable.ConfiguredTaskAwaiter>(ref awaiter);
TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, ta.m_continueOnCapturedContext);
TaskAwaiter.UnsafeOnCompletedInternal(vta.AsTask(), box, vta._continueOnCapturedContext);
}
- // To catch all Task/Task<T> awaits, do the currently more expensive interface checks.
- // Eventually these and the above Task/Task<T> checks should be replaced by "is" checks,
- // once that's recognized and optimized by the JIT. We do these after all of the hardcoded
- // checks above so that they don't incur the costs of these checks.
- else if (InterfaceIsCheckWorkaround<TAwaiter>.IsITaskAwaiter)
- {
- ref TaskAwaiter ta = ref Unsafe.As<TAwaiter, TaskAwaiter>(ref awaiter);
- TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, continueOnCapturedContext: true);
- }
- else if (InterfaceIsCheckWorkaround<TAwaiter>.IsIConfiguredTaskAwaiter)
- {
- ref ConfiguredTaskAwaitable.ConfiguredTaskAwaiter ta = ref Unsafe.As<TAwaiter, ConfiguredTaskAwaitable.ConfiguredTaskAwaiter>(ref awaiter);
- TaskAwaiter.UnsafeOnCompletedInternal(ta.m_task, box, ta.m_continueOnCapturedContext);
- }
-
// The awaiter isn't specially known. Fall back to doing a normal await.
else
{
new Task<TResult>(false, result, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default(CancellationToken));
}
- /// <summary>Temporary workaround for https://github.com/dotnet/coreclr/issues/12877.</summary>
- internal static class InterfaceIsCheckWorkaround<TAwaiter>
- {
- internal static readonly bool IsITaskAwaiter = typeof(TAwaiter).GetInterface("ITaskAwaiter") != null;
- internal static readonly bool IsIConfiguredTaskAwaiter = typeof(TAwaiter).GetInterface("IConfiguredTaskAwaiter") != null;
- }
-
/// <summary>
/// An interface implemented by all <see cref="AsyncStateMachineBox{TStateMachine, TResult}"/> instances, regardless of generics.
/// </summary>
CORINFO_CLASS_HANDLE fromClass,
CORINFO_CLASS_HANDLE toClass)
{
- // Stub for now
- return TypeCompareState::May;
+ CONTRACTL {
+ SO_TOLERANT;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ } CONTRACTL_END;
+
+ TypeCompareState result = TypeCompareState::May;
+
+ JIT_TO_EE_TRANSITION();
+
+ TypeHandle fromHnd = (TypeHandle) fromClass;
+ TypeHandle toHnd = (TypeHandle) toClass;
+
+#ifdef FEATURE_COMINTEROP
+ // If casting from a com object class, don't try to optimize.
+ if (fromHnd.IsComObjectType())
+ {
+ result = TypeCompareState::May;
+ }
+ else
+#endif // FEATURE_COMINTEROP
+
+ // If casting from ICastable, don't try to optimize
+ if (fromHnd.GetMethodTable()->IsICastable())
+ {
+ result = TypeCompareState::May;
+ }
+ // If casting to Nullable<T>, don't try to optimize
+ else if (Nullable::IsNullableType(toHnd))
+ {
+ result = TypeCompareState::May;
+ }
+ // If the types are not shared, we can check directly.
+ else if (!fromHnd.IsCanonicalSubtype() && !toHnd.IsCanonicalSubtype())
+ {
+ result = fromHnd.CanCastTo(toHnd) ? TypeCompareState::Must : TypeCompareState::MustNot;
+ }
+ // Casting from a shared type to an unshared type.
+ else if (fromHnd.IsCanonicalSubtype() && !toHnd.IsCanonicalSubtype())
+ {
+ // Only handle casts to interface types for now
+ if (toHnd.IsInterface())
+ {
+ // Do a preliminary check.
+ BOOL canCast = fromHnd.CanCastTo(toHnd);
+
+ // Pass back positive results unfiltered. The unknown type
+ // parameters in fromClass did not come into play.
+ if (canCast)
+ {
+ result = TypeCompareState::Must;
+ }
+ // For negative results, the unknown type parameter in
+ // fromClass might match some instantiated interface,
+ // either directly or via variance.
+ //
+ // 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())
+ {
+ result = TypeCompareState::MustNot;
+ }
+ }
+ }
+
+#ifdef FEATURE_READYTORUN_COMPILER
+ // In R2R it is a breaking change for a previously positive
+ // cast to become negative, but not for a previously negative
+ // cast to become positive. So in R2R a negative result is
+ // always reported back as May.
+ if (IsReadyToRunCompilation() && (result == TypeCompareState::MustNot))
+ {
+ result = TypeCompareState::May;
+ }
+#endif // FEATURE_READYTORUN_COMPILER
+
+ EE_TO_JIT_TRANSITION();
+
+ return result;
}
/*********************************************************************/
--- /dev/null
+// 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 I<T>
+{
+ int E(T t);
+}
+
+sealed class J<T> : I<T>
+{
+ public int E(T t) { return 3; }
+}
+
+class Z
+{
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool F0<T>(I<T> i)
+ {
+ return i is I<object>;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool F1<T>(J<T> j)
+ {
+ return j is I<string>;
+ }
+
+ public static int Main()
+ {
+ var j0 = new J<object>();
+ var j1 = new J<string>();
+ bool b00 = F0(j0);
+ bool b01 = F0(j1);
+ bool b10 = F1(j0);
+ bool b11 = F1(j1);
+
+ int a = 0;
+ if (b00) a += 1;
+ if (b01) a += 2;
+ if (b10) a += 4;
+ if (b11) a += 8;
+
+ Console.WriteLine($"a = {a}");
+
+ return a == 9 ? 100 : 0;
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT .0\UITestExtensionPackages</ReferencePath>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <PropertyGroup>
+ <DebugType>PdbOnly</DebugType>
+ <Optimize>True</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="shared.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+// 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 I<T>
+{
+ int E(T t);
+}
+
+sealed class J : I<string>
+{
+ public int E(string s)
+ {
+ return s.Length;
+ }
+}
+
+class K : I<string>
+{
+ public int E(string s)
+ {
+ return s.GetHashCode();
+ }
+}
+
+sealed class L : K, I<object>
+{
+ public int E(object o)
+ {
+ return o.GetHashCode();
+ }
+}
+
+class F
+{
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsIString<T>(I<T> i)
+ {
+ return i is I<string>;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsI<T,U>(I<U> i)
+ {
+ return i is I<T>;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsJI<T>(J j)
+ {
+ return j is I<T>;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsKI<T>(K k)
+ {
+ return k is I<T>;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsLI<T>(L l)
+ {
+ return l is I<T>;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsJIString(J j)
+ {
+ return j is I<string>;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsKIString(K k)
+ {
+ return k is I<string>;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsLIString(L l)
+ {
+ return l is I<string>;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsJIObject(J j)
+ {
+ return j is I<object>;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsKIObject(K k)
+ {
+ return k is I<object>;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsLIObject(L l)
+ {
+ return l is I<object>;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsIStringJ(I<string> i)
+ {
+ return i is J;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsIStringK(I<string> i)
+ {
+ return i is K;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsIStringL(I<string> i)
+ {
+ return i is L;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsIJ<T>(I<T> i)
+ {
+ return i is J;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsIK<T>(I<T> i)
+ {
+ return i is K;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static bool IsIL<T>(I<T> i)
+ {
+ return i is K;
+ }
+
+ public static int Main()
+ {
+ var j = new J();
+ var k = new K();
+ var l = new L();
+
+ bool b0 = IsIString(j);
+ bool b1 = IsIString(k);
+ bool b2 = IsIString<string>(l);
+ bool b3 = IsIString<object>(l);
+
+ bool c0 = IsI<string,string>(j);
+ bool c1 = IsI<string,string>(k);
+ bool c2 = IsI<string,string>(l);
+
+ bool d0 = IsI<object,string>(j);
+ bool d1 = IsI<object,string>(k);
+ bool d2 = IsI<object,string>(l);
+
+ bool e0 = IsJI<string>(j);
+ bool e1 = IsKI<string>(k);
+ bool e2 = IsKI<string>(l);
+ bool e3 = IsLI<string>(l);
+
+ bool f0 = IsJIString(j);
+ bool f1 = IsKIString(k);
+ bool f2 = IsKIString(l);
+ bool f3 = IsLIString(l);
+
+ bool g0 = IsIStringJ(j);
+ bool g1 = IsIStringJ(k);
+ bool g2 = IsIStringJ(l);
+ bool g3 = IsIStringK(j);
+ bool g4 = IsIStringK(k);
+ bool g5 = IsIStringK(l);
+ bool g6 = IsIStringL(j);
+ bool g7 = IsIStringL(k);
+ bool g8 = IsIStringL(l);
+
+ bool h0 = IsIJ<string>(j);
+ bool h1 = IsIJ<string>(k);
+ bool h2 = IsIJ<string>(l);
+ bool h3 = IsIK<string>(j);
+ bool h4 = IsIK<string>(k);
+ bool h5 = IsIK<string>(l);
+ bool h6 = IsIL<string>(j);
+ bool h7 = IsIL<string>(k);
+ bool h8 = IsIL<string>(l);
+
+ bool j0 = IsJIObject(j);
+ bool j1 = IsKIObject(k);
+ bool j2 = IsKIObject(l);
+ bool j3 = IsLIObject(l);
+
+ bool pos =
+ b0 & b1 & b2 & b3
+ & c0 & c1 & c2
+ & d2
+ & e0 & e1 & e2 & e3
+ & f0 & f1 & f2 & f3
+ & g0 & g4 & g5 & g8
+ & h0 & h4 & h5 & h8
+ & j2 & j3;
+
+ bool neg =
+ d0 & d1
+ & g1 & g2 & g6 & g7
+ & h1 & h2 & h6 & h7
+ & j0 & j1;
+
+ return pos & !neg ? 100 : 0;
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT .0\UITestExtensionPackages</ReferencePath>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <PropertyGroup>
+ <DebugType>PdbOnly</DebugType>
+ <Optimize>True</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="tests.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
\ No newline at end of file