summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>2019-01-18 13:18:49 +0100
committerGitHub <noreply@github.com>2019-01-18 13:18:49 +0100
commite3932d49956acae70e0732ff3cfde7d8dc51db67 (patch)
treeaf81f360070c254d05cd1b2cd78a8440496a331d
parent0a2596ce3d7fb0253f30b2461fa910c65840f9b3 (diff)
downloadcoreclr-e3932d49956acae70e0732ff3cfde7d8dc51db67.tar.gz
coreclr-e3932d49956acae70e0732ff3cfde7d8dc51db67.tar.bz2
coreclr-e3932d49956acae70e0732ff3cfde7d8dc51db67.zip
Handle complex constrained calls with default interface methods (#21978)
This adds handling for the interface dispatch corner case where: * We have a constrained callsite to a method on a generic interface in shared code * The callsite cannot be statically resolved because the result of dispatch depends on the generic context * At runtime, the dispatch resolves to a default interface method This would require us to have infrastructure to build "boxing thunks" - thunks that would box their first argument before dispatching to the default interface method implementation. Since this is a corner case and the fix is actually quite involved, we're making the runtime just throw in this situation. The test is written so that it should pass both if the runtime chooses to throw, or if the runtime makes the boxing thunk (we're not hardcoding the implementation limitation).
-rw-r--r--src/dlls/mscorrc/mscorrc.rc1
-rw-r--r--src/dlls/mscorrc/resource.h1
-rw-r--r--src/vm/genericdict.cpp31
-rw-r--r--tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.cs189
-rw-r--r--tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.il590
5 files changed, 182 insertions, 630 deletions
diff --git a/src/dlls/mscorrc/mscorrc.rc b/src/dlls/mscorrc/mscorrc.rc
index 59aa7c338f..f3244218f9 100644
--- a/src/dlls/mscorrc/mscorrc.rc
+++ b/src/dlls/mscorrc/mscorrc.rc
@@ -761,6 +761,7 @@ BEGIN
IDS_CLASSLOAD_MI_BAD_SIG "Type '%1' from assembly '%2' contains an invalid method implementation signature."
IDS_CLASSLOAD_MI_FINAL_IMPL "Method implementation on an interface '%1' from assembly '%2' must be a final method."
IDS_CLASSLOAD_AMBIGUOUS_OVERRIDE "Could not call method '%1' on interface '%2' with type '%3' from assembly '%4' because there are multiple incompatible interface methods overriding this method."
+ IDS_CLASSLOAD_UNSUPPORTED_DISPATCH "Could not make constrained call to method '%1' on interface '%2' with type '%3' from assembly '%4'. Dispatch to default interface methods is not supported in this situation."
IDS_CLASSLOAD_MISSINGMETHODRVA "Could not load type '%1' from assembly '%2' because the method '%3' has no implementation (no RVA)."
SECURITY_E_INCOMPATIBLE_EVIDENCE "Assembly '%1' already loaded without additional security evidence."
diff --git a/src/dlls/mscorrc/resource.h b/src/dlls/mscorrc/resource.h
index 735f315c33..1b71333385 100644
--- a/src/dlls/mscorrc/resource.h
+++ b/src/dlls/mscorrc/resource.h
@@ -466,6 +466,7 @@
#define IDS_EE_HWINTRINSIC_NGEN_DISALLOWED 0x1ac7
#define IDS_CLASSLOAD_MI_FINAL_IMPL 0x1ac8
#define IDS_CLASSLOAD_AMBIGUOUS_OVERRIDE 0x1ac9
+#define IDS_CLASSLOAD_UNSUPPORTED_DISPATCH 0x1aca
#define BFA_INVALID_TOKEN_TYPE 0x2001
#define BFA_INVALID_TOKEN 0x2003
diff --git a/src/vm/genericdict.cpp b/src/vm/genericdict.cpp
index 906f5c57fa..90af6395d7 100644
--- a/src/vm/genericdict.cpp
+++ b/src/vm/genericdict.cpp
@@ -1120,6 +1120,37 @@ Dictionary::PopulateEntry(
if (!pResolvedMD)
COMPlusThrowHR(COR_E_BADIMAGEFORMAT);
+#if FEATURE_DEFAULT_INTERFACES
+ // If we resolved the constrained call on a value type into a method on a reference type, this is a
+ // default interface method implementation.
+ // In such case we would need to box the value type before we can dispatch to the implementation.
+ // This would require us to make a "boxing stub". For now we leave the boxing stubs unimplemented.
+ // It's not clear if anyone would need them and the implementation complexity is not worth it at this time.
+ if (!pResolvedMD->GetMethodTable()->IsValueType() && constraintType.GetMethodTable()->IsValueType())
+ {
+ SString assemblyName;
+
+ constraintType.GetMethodTable()->GetAssembly()->GetDisplayName(assemblyName);
+
+ SString strInterfaceName;
+ TypeString::AppendType(strInterfaceName, ownerType);
+
+ SString strMethodName;
+ TypeString::AppendMethod(strMethodName, pMethod, pMethod->GetMethodInstantiation());
+
+ SString strTargetClassName;
+ TypeString::AppendType(strTargetClassName, constraintType.GetMethodTable());
+
+ COMPlusThrow(
+ kNotSupportedException,
+ IDS_CLASSLOAD_UNSUPPORTED_DISPATCH,
+ strMethodName,
+ strInterfaceName,
+ strTargetClassName,
+ assemblyName);
+ }
+#endif
+
result = (CORINFO_GENERIC_HANDLE)pResolvedMD->GetMultiCallableAddrOfCode();
}
else
diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.cs b/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.cs
deleted file mode 100644
index 3f3d1153ee..0000000000
--- a/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.cs
+++ /dev/null
@@ -1,189 +0,0 @@
-// 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.Collections.Generic;
-
-interface IFoo
-{
- int Foo(int c);
-}
-
-interface IAdd
-{
- int Add(int c);
-}
-
-// Only needed for writing IFoo.Foo code
-class IFoo_Impl : IFoo
-{
- public int Foo(int c)
- {
- IAdd adder = (IAdd) this;
- return adder.Add(c);
- }
-}
-
-struct FooValue : IFoo, IAdd
-{
- public int val;
-
- public int Foo(int c)
- {
- val +=c;
- return val;
- }
-
- public int Add(int c)
- {
- val +=c;
- return val;
- }
-}
-
-interface IHorrible<T>
-{
- int GetLocalVal();
- void SetLocalVal(int val);
- int Horrible();
-}
-
-// Only needed for the default interface implementation
-class IHorrible_Impl<T> : IHorrible<T>
-{
- public int GetLocalVal() { return 0; }
- public void SetLocalVal(int val) {}
- public int Horrible()
- {
- int val = GetLocalVal();
- val++;
- SetLocalVal(val);
- return val;
- }
-}
-
-struct HorribleCase<Z> : IHorrible<IList<Z>>, IHorrible<IEnumerable<Z>>
-{
- int localVal;
- public int GetLocalVal() { return localVal; }
- public void SetLocalVal(int val) { localVal = val; }
- int IHorrible<IList<Z>>.Horrible() { return ++localVal; }
-
- // Remove
- int IHorrible<IEnumerable<Z>>.Horrible() { return ++localVal; }
-}
-
-class HorribleTest
-{
- public static int Horror<T,U>(T t) where T:IHorrible<U>
- {
- return t.Horrible() + t.Horrible();
- }
-
- public static void RunTest()
- {
- Test.Assert(Horror<HorribleCase<object>,IEnumerable<object>>(new HorribleCase<object>())) == 2, "Fail");
- Test.Assert(Horror<HorribleCase<object>,IList<object>>(default(HorribleCase<object>)) == 3, "Fail");
- }
-}
-
-/*
-interface IFoo<T>
-{
- int Foo(int c);
-}
-
-interface IAdd
-{
- int Add(int c);
-}
-
-// Only needed for writing IFoo.Foo code
-class IFoo_Impl<T> : IFoo<T>
-{
- public int Foo(int c)
- {
- IAdd adder = (IAdd) this;
- return adder.Add(c);
- }
-}
-
-struct FooValue<T> : IFoo<T>, IAdd
-{
- public int val;
-
- public int Foo(int c)
- {
- val +=c;
- return val;
- }
-
- public int Add(int c)
- {
- val +=c;
- return val;
- }
-}
-*/
-
-class SimpleConstraintTest
-{
- public static int CallFoo_WithConstraints<T>(ref T foo, int val) where T : IFoo
- {
- return foo.Foo(val);
- }
-
- /*
- public static int CallFoo_WithConstraints<T>(ref T foo, int val) where T : IFoo<object>
- {
- return foo.Foo(val);
- }
- */
-
- public static void RunTest()
- {
- FooValue foo = new FooValue();
- foo.val = 10;
-
- Console.WriteLine("Calling CallFoo_WithConstraints on FooValue - expecting IFoo::Foo");
- Test.Assert(CallFoo_WithConstraints(ref foo, 10) == 20, "Calling CallFoo_WithConstraints on FooValue");
-
- Test.Assert(foo.val == 10, "Expecting boxing on CallFoo_WithConstraints");
- }
-}
-
-class Program
-{
- public static int Main()
- {
- HorribleTest.RunTest();
- SimpleConstraintTest.RunTest();
-
- return Test.Ret();
- }
-}
-
-class Test
-{
- private static bool Pass = true;
-
- public static int Ret()
- {
- return Pass? 100 : 101;
- }
-
- public static void Assert(bool cond, string msg)
- {
- if (cond)
- {
- Console.WriteLine("PASS");
- }
- else
- {
- Console.WriteLine("FAIL: " + msg);
- Pass = false;
- }
- }
-}
-
diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.il b/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.il
index 6e2ca954be..47d51eeed2 100644
--- a/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.il
+++ b/tests/src/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.il
@@ -2,458 +2,166 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
-// Copyright (c) Microsoft Corporation. All rights reserved.
+.assembly extern mscorlib { }
+.assembly constrainedcall { }
-
-
-// Metadata version: v4.0.30319
-.assembly extern mscorlib
-{
- .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
- .ver 4:0:0:0
-}
-.assembly constrainedcall
-{
- .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
- .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
- 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
-
- // --- The following custom attribute is added automatically, do not uncomment -------
- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )
-
- .hash algorithm 0x00008004
- .ver 0:0:0:0
-}
-.module constrainedcall.exe
-// MVID: {6171EA0F-1009-482D-8EF6-C944886D5D66}
-.imagebase 0x00400000
-.file alignment 0x00000200
-.stackreserve 0x00100000
-.subsystem 0x0003 // WINDOWS_CUI
-.corflags 0x00000001 // ILONLY
-// Image base: 0x01860000
-
-
-// =============== CLASS MEMBERS DECLARATION ===================
-
-.class interface private abstract auto ansi IFoo
-{
- .method public hidebysig newslot virtual
- instance int32 Foo(int32 c) cil managed
- {
- // Code size 20 (0x14)
- .maxstack 2
- .locals init (class IAdd V_0,
- int32 V_1)
- IL_0000: nop
- IL_0001: ldarg.0
- IL_0002: castclass IAdd
- IL_0007: stloc.0
- IL_0008: ldloc.0
- IL_0009: ldarg.1
- IL_000a: callvirt instance int32 IAdd::Add(int32)
- IL_000f: stloc.1
- IL_0010: br.s IL_0012
-
- IL_0012: ldloc.1
- IL_0013: ret
- } // end of method IFoo::Foo
-
-} // end of class IFoo
-
-.class interface private abstract auto ansi IAdd
+.class interface private abstract auto ansi IAdder`1<T>
{
- .method public hidebysig newslot abstract virtual
- instance int32 Add(int32 c) cil managed
+ .method public hidebysig newslot abstract virtual instance int32 Add(int32)
{
- } // end of method IAdd::Add
-
-} // end of class IAdd
+ }
-.class private sequential ansi sealed beforefieldinit FooValue
- extends [mscorlib]System.ValueType
- implements IFoo,
- IAdd
-{
- .field public int32 val
-
- .method public hidebysig newslot virtual final
- instance int32 Add(int32 c) cil managed
+ .method public hidebysig newslot virtual instance int32 PlusPlus()
{
- // Code size 26 (0x1a)
- .maxstack 3
- .locals init (int32 V_0)
- IL_0000: nop
- IL_0001: ldarg.0
- IL_0002: ldarg.0
- IL_0003: ldfld int32 FooValue::val
- IL_0008: ldarg.1
- IL_0009: add
- IL_000a: stfld int32 FooValue::val
- IL_000f: ldarg.0
- IL_0010: ldfld int32 FooValue::val
- IL_0015: stloc.0
- IL_0016: br.s IL_0018
-
- IL_0018: ldloc.0
- IL_0019: ret
- } // end of method FooValue::Add
-
-} // end of class FooValue
-
-.class interface private abstract auto ansi IHorrible`1<T>
-{
- .method public hidebysig newslot abstract virtual
- instance int32 GetLocalVal() cil managed
- {
- } // end of method IHorrible`1::GetLocalVal
-
- .method public hidebysig newslot abstract virtual
- instance void SetLocalVal(int32 val) cil managed
- {
- } // end of method IHorrible`1::SetLocalVal
-
- .method public hidebysig newslot virtual
- instance int32 Horrible() cil managed
- {
- // Code size 26 (0x1a)
- .maxstack 2
- .locals init (int32 V_0,
- int32 V_1)
- IL_0000: nop
- IL_0001: ldarg.0
- IL_0002: callvirt instance int32 class IHorrible`1<!T>::GetLocalVal()
- IL_0007: stloc.0
- IL_0008: ldloc.0
- IL_0009: ldc.i4.1
- IL_000a: add
- IL_000b: stloc.0
- IL_000c: ldarg.0
- IL_000d: ldloc.0
- IL_000e: callvirt instance void class IHorrible`1<!T>::SetLocalVal(int32)
- IL_0013: nop
- IL_0014: ldloc.0
- IL_0015: stloc.1
- IL_0016: br.s IL_0018
-
- IL_0018: ldloc.1
- IL_0019: ret
- } // end of method IHorrible`1::Horrible
-
-} // end of class IHorrible`1
-
-.class private sequential ansi sealed beforefieldinit HorribleCase`1<Z>
- extends [mscorlib]System.ValueType
- implements class IHorrible`1<class [mscorlib]System.Collections.Generic.IList`1<!Z>>,
- class IHorrible`1<class [mscorlib]System.Collections.Generic.IEnumerable`1<!Z>>
-{
- .field private int32 localVal
- .method public hidebysig newslot virtual final
- instance int32 GetLocalVal() cil managed
- {
- // Code size 12 (0xc)
- .maxstack 1
- .locals init (int32 V_0)
- IL_0000: nop
- IL_0001: ldarg.0
- IL_0002: ldfld int32 valuetype HorribleCase`1<!Z>::localVal
- IL_0007: stloc.0
- IL_0008: br.s IL_000a
-
- IL_000a: ldloc.0
- IL_000b: ret
- } // end of method HorribleCase`1::GetLocalVal
-
- .method public hidebysig newslot virtual final
- instance void SetLocalVal(int32 val) cil managed
- {
- // Code size 9 (0x9)
- .maxstack 8
- IL_0000: nop
- IL_0001: ldarg.0
- IL_0002: ldarg.1
- IL_0003: stfld int32 valuetype HorribleCase`1<!Z>::localVal
- IL_0008: ret
- } // end of method HorribleCase`1::SetLocalVal
-
- .method private hidebysig newslot virtual final
- instance int32 'IHorrible<System.Collections.Generic.IList<Z>>.Horrible'() cil managed
- {
- .override method instance int32 class IHorrible`1<class [mscorlib]System.Collections.Generic.IList`1<!Z>>::Horrible()
- // Code size 23 (0x17)
- .maxstack 3
- .locals init (int32 V_0,
- int32 V_1)
- IL_0000: nop
- IL_0001: ldarg.0
- IL_0002: ldarg.0
- IL_0003: ldfld int32 valuetype HorribleCase`1<!Z>::localVal
- IL_0008: ldc.i4.1
- IL_0009: add
- IL_000a: stloc.0
- IL_000b: ldloc.0
- IL_000c: stfld int32 valuetype HorribleCase`1<!Z>::localVal
- IL_0011: ldloc.0
- IL_0012: stloc.1
- IL_0013: br.s IL_0015
+ ldarg.0
+ ldc.i4.0
+ callvirt instance int32 class IAdder`1<!0>::Add(int32)
+ ret
+ }
+}
- IL_0015: ldloc.1
- IL_0016: ret
- } // end of method HorribleCase`1::'IHorrible<System.Collections.Generic.IList<Z>>.Horrible'
-} // end of class HorribleCase`1
+.class interface IGen1`1<T> { }
+.class interface IGen2`1<T> { }
+.class interface IGen3`1<T> { }
-.class private auto ansi beforefieldinit HorribleTest
- extends [mscorlib]System.Object
+.class value Adder`1<T> implements class IAdder`1<class IGen1`1<!T>>, class IAdder`1<class IGen2`1<!T>>, class IAdder`1<class IGen3`1<!T>>
{
- .method public hidebysig static int32 Horror<(class IHorrible`1<!!U>) T,U>(!!T t) cil managed
- {
- // Code size 33 (0x21)
- .maxstack 2
- .locals init (int32 V_0)
- IL_0000: nop
- IL_0001: ldarga.s t
- IL_0003: constrained. !!T
- IL_0009: callvirt instance int32 class IHorrible`1<!!U>::Horrible()
- IL_000e: ldarga.s t
- IL_0010: constrained. !!T
- IL_0016: callvirt instance int32 class IHorrible`1<!!U>::Horrible()
- IL_001b: add
- IL_001c: stloc.0
- IL_001d: br.s IL_001f
-
- IL_001f: ldloc.0
- IL_0020: ret
- } // end of method HorribleTest::Horror
-
- .method public hidebysig static void RunTest() cil managed
- {
- // Code size 58 (0x3a)
- .maxstack 2
- .locals init (valuetype HorribleCase`1<object> V_0)
- IL_0000: nop
- IL_0001: ldloca.s V_0
- IL_0003: initobj valuetype HorribleCase`1<object>
- IL_0009: ldloc.0
- IL_000a: call int32 HorribleTest::Horror<valuetype HorribleCase`1<object>,class [mscorlib]System.Collections.Generic.IEnumerable`1<object>>(!!0)
- IL_000f: ldc.i4.3
- IL_0010: ceq
- IL_0012: ldstr "Fail"
- IL_0017: call void Test::Assert(bool,
- string)
- IL_001c: nop
- IL_001d: ldloca.s V_0
- IL_001f: initobj valuetype HorribleCase`1<object>
- IL_0025: ldloc.0
- IL_0026: call int32 HorribleTest::Horror<valuetype HorribleCase`1<object>,class [mscorlib]System.Collections.Generic.IList`1<object>>(!!0)
- IL_002b: ldc.i4.0
- IL_002c: ceq
- IL_002e: ldstr "Fail"
- IL_0033: call void Test::Assert(bool,
- string)
- IL_0038: nop
- IL_0039: ret
- } // end of method HorribleTest::RunTest
-
- .method public hidebysig specialname rtspecialname
- instance void .ctor() cil managed
- {
- // Code size 8 (0x8)
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: call instance void [mscorlib]System.Object::.ctor()
- IL_0006: nop
- IL_0007: ret
- } // end of method HorribleTest::.ctor
-
-} // end of class HorribleTest
+ .field private int32 _field
+
+ .method public hidebysig newslot virtual instance int32 Add(int32) cil managed
+ {
+ ldarg.0
+ dup
+ ldfld int32 valuetype Adder`1<!T>::_field
+ ldarg.1
+ add
+ stfld int32 valuetype Adder`1<!T>::_field
+ ldarg.0
+ ldfld int32 valuetype Adder`1<!T>::_field
+ ret
+ }
+
+ .method private hidebysig newslot virtual instance int32 'IAdder<IGen1<T>>.PlusPlus'()
+ {
+ .override method instance int32 class IAdder`1<class IGen1`1<!T>>::PlusPlus()
+ ldarg.0
+ ldc.i4.1
+ call instance int32 valuetype Adder`1<!T>::Add(int32)
+ ret
+ }
+
+ .method private hidebysig newslot virtual instance int32 'IAdder<IGen2<T>>.PlusPlus'()
+ {
+ .override method instance int32 class IAdder`1<class IGen2`1<!T>>::PlusPlus()
+ ldarg.0
+ ldc.i4.2
+ call instance int32 valuetype Adder`1<!T>::Add(int32)
+ ret
+ }
+}
-.class private auto ansi beforefieldinit SimpleConstraintTest
- extends [mscorlib]System.Object
+.method public hidebysig static int32 Check<(class IAdder`1<!!U>) T,U>(!!T t)
{
- .method public hidebysig static int32 CallFoo_WithConstraints<(IFoo) T>(!!T& foo,
- int32 val) cil managed
- {
- // Code size 19 (0x13)
- .maxstack 2
- .locals init (int32 V_0)
- IL_0000: nop
- IL_0001: ldarg.0
- IL_0002: ldarg.1
- IL_0003: constrained. !!T
- IL_0009: callvirt instance int32 IFoo::Foo(int32)
- IL_000e: stloc.0
- IL_000f: br.s IL_0011
-
- IL_0011: ldloc.0
- IL_0012: ret
- } // end of method SimpleConstraintTest::CallFoo_WithConstraints
-
- .method public hidebysig static void RunTest() cil managed
- {
- // Code size 75 (0x4b)
- .maxstack 2
- .locals init (valuetype FooValue V_0)
- IL_0000: nop
- IL_0001: ldloca.s V_0
- IL_0003: initobj FooValue
- IL_0009: ldloca.s V_0
- IL_000b: ldc.i4.s 10
- IL_000d: stfld int32 FooValue::val
- IL_0012: ldstr "Calling CallFoo_WithConstraints on FooValue - expe"
- + "cting IFoo::Foo"
- IL_0017: call void [mscorlib]System.Console::WriteLine(string)
- IL_001c: nop
- IL_001d: ldloca.s V_0
- IL_001f: ldc.i4.s 10
- IL_0021: call int32 SimpleConstraintTest::CallFoo_WithConstraints<valuetype FooValue>(!!0&,
- int32)
- IL_0026: ldc.i4.s 20
- IL_0028: ceq
- IL_002a: ldstr "Calling CallFoo_WithConstraints on FooValue"
- IL_002f: call void Test::Assert(bool,
- string)
- IL_0034: nop
- IL_0035: ldloc.0
- IL_0036: ldfld int32 FooValue::val
- IL_003b: ldc.i4.s 10
- IL_003d: ceq
- IL_003f: ldstr "Expecting boxing on CallFoo_WithConstraints"
- IL_0044: call void Test::Assert(bool,
- string)
- IL_0049: nop
- IL_004a: ret
- } // end of method SimpleConstraintTest::RunTest
-
- .method public hidebysig specialname rtspecialname
- instance void .ctor() cil managed
- {
- // Code size 8 (0x8)
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: call instance void [mscorlib]System.Object::.ctor()
- IL_0006: nop
- IL_0007: ret
- } // end of method SimpleConstraintTest::.ctor
-
-} // end of class SimpleConstraintTest
+ // The constrained calls below may or may not box for a valuetype T
+ // depending on whether IAdder`1<!!U>::PlusPlus() ends up calling
+ // the default implementation of the interface method.
+
+ ldarga.s 0
+ constrained. !!T
+ callvirt instance int32 class IAdder`1<!!U>::PlusPlus()
+ ldarga.s 0
+ constrained. !!T
+ callvirt instance int32 class IAdder`1<!!U>::PlusPlus()
+ add
+ ret
+}
-.class private auto ansi beforefieldinit Program
- extends [mscorlib]System.Object
+.method public hidebysig static int32 Main()
{
- .method public hidebysig static int32 Main() cil managed
- {
.entrypoint
- // Code size 23 (0x17)
- .maxstack 1
- .locals init (int32 V_0)
- IL_0000: nop
- IL_0001: call void HorribleTest::RunTest()
- IL_0006: nop
- IL_0007: call void SimpleConstraintTest::RunTest()
- IL_000c: nop
- IL_000d: call int32 Test::Ret()
- IL_0012: stloc.0
- IL_0013: br.s IL_0015
-
- IL_0015: ldloc.0
- IL_0016: ret
- } // end of method Program::Main
-
- .method public hidebysig specialname rtspecialname
- instance void .ctor() cil managed
- {
- // Code size 8 (0x8)
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: call instance void [mscorlib]System.Object::.ctor()
- IL_0006: nop
- IL_0007: ret
- } // end of method Program::.ctor
-} // end of class Program
-
-.class private auto ansi beforefieldinit Test
- extends [mscorlib]System.Object
-{
- .field private static bool Pass
- .method public hidebysig static int32 Ret() cil managed
- {
- // Code size 19 (0x13)
- .maxstack 1
- .locals init (int32 V_0)
- IL_0000: nop
- IL_0001: ldsfld bool Test::Pass
- IL_0006: brtrue.s IL_000c
-
- IL_0008: ldc.i4.s 101
- IL_000a: br.s IL_000e
-
- IL_000c: ldc.i4.s 100
- IL_000e: stloc.0
- IL_000f: br.s IL_0011
-
- IL_0011: ldloc.0
- IL_0012: ret
- } // end of method Test::Ret
-
- .method public hidebysig static void Assert(bool cond,
- string msg) cil managed
- {
- // Code size 47 (0x2f)
- .maxstack 2
- .locals init (bool V_0)
- IL_0000: nop
- IL_0001: ldarg.0
- IL_0002: stloc.0
- IL_0003: ldloc.0
- IL_0004: brfalse.s IL_0015
-
- IL_0006: nop
- IL_0007: ldstr "PASS"
- IL_000c: call void [mscorlib]System.Console::WriteLine(string)
- IL_0011: nop
- IL_0012: nop
- IL_0013: br.s IL_002e
-
- IL_0015: nop
- IL_0016: ldstr "FAIL: "
- IL_001b: ldarg.1
- IL_001c: call string [mscorlib]System.String::Concat(string,
- string)
- IL_0021: call void [mscorlib]System.Console::WriteLine(string)
- IL_0026: nop
- IL_0027: ldc.i4.0
- IL_0028: stsfld bool Test::Pass
- IL_002d: nop
- IL_002e: ret
- } // end of method Test::Assert
-
- .method public hidebysig specialname rtspecialname
- instance void .ctor() cil managed
- {
- // Code size 8 (0x8)
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: call instance void [mscorlib]System.Object::.ctor()
- IL_0006: nop
- IL_0007: ret
- } // end of method Test::.ctor
-
- .method private hidebysig specialname rtspecialname static
- void .cctor() cil managed
- {
- // Code size 7 (0x7)
- .maxstack 8
- IL_0000: ldc.i4.1
- IL_0001: stsfld bool Test::Pass
- IL_0006: ret
- } // end of method Test::.cctor
-
-} // end of class Test
-
-
-// =============================================================
-
-// *********** DISASSEMBLY COMPLETE ***********************
-// WARNING: Created Win32 resource file constrainedcall.res
+ .locals init (
+ valuetype Adder`1<object>,
+ int32
+ )
+
+ // This will end up calling the implementation of IAdder<IGen1<object>>.PlusPlus
+ // provided by the Adder valuetype.
+ // The sum returned by the Check method will be 1+2 = 3.
+ ldloc.0
+ call int32 Check<valuetype Adder`1<object>,class IGen1`1<object>>(!!0)
+ ldc.i4.3
+ ceq
+ brtrue IGen1_OK
+
+ ldc.i4.1
+ ret
+
+IGen1_OK:
+
+ // This will end up calling the implementation of IAdder<IGen2<object>>.PlusPlus
+ // provided by the Adder valuetype.
+ // The sum returned by the Check method will be 2+4 = 6.
+ ldloca 0
+ initobj valuetype Adder`1<object>
+ ldloc.0
+ call int32 Check<valuetype Adder`1<object>,class IGen2`1<object>>(!!0)
+ ldc.i4.6
+ ceq
+ brtrue IGen2_OK
+
+ ldc.i4.2
+ ret
+
+IGen2_OK:
+
+ ldstr "Constrained calls that require runtime lookup are OK"
+ call void [mscorlib]System.Console::WriteLine(string)
+
+
+ // Store a successful result in case the implementation throws.
+ // We consider both a throw and a successful dispatch a success.
+ // The throwing behavior is an implementation limitation.
+ // Successful dispatch is what the spec mandates.
+ ldc.i4.2
+ stloc.1
+
+ ldloca 0
+ initobj valuetype Adder`1<object>
+
+ // This will end up calling the default implementation of IAdder<IGen3<object>>.PlusPlus
+ // Since each constrained call in Check is going to box, the sum will end up 1+1 = 2.
+ .try
+ {
+ ldloc.0
+ call int32 Check<valuetype Adder`1<object>,class IGen3`1<object>>(!!0)
+ stloc.1
+
+ ldstr "Runtime supports lookups with runtime determined boxing"
+ call void [mscorlib]System.Console::WriteLine(string)
+
+ leave AfterBoxingCall
+ }
+ catch [mscorlib]System.Exception
+ {
+ pop
+ leave AfterFailedBoxingCall
+ }
+
+AfterFailedBoxingCall:
+ ldstr "Runtime does not support lookups with runtime determined boxing"
+ call void [mscorlib]System.Console::WriteLine(string)
+
+AfterBoxingCall:
+ ldloc.1
+ ldc.i4.2
+ ceq
+ brtrue AllOK
+
+ ldc.i4.3
+ ret
+
+AllOK:
+ ldc.i4 100
+ ret
+}