From 6f4fbe1c83a0794833a9b02ac2390549a3994dfa Mon Sep 17 00:00:00 2001 From: Bill Holmes Date: Fri, 21 Jan 2022 11:57:58 -0500 Subject: [PATCH 1/2] vtable setup fix for generic default interface methods in mono runtime When processing the overrides from interface default methods we should check if the interface class is a generic type definition first and inflate with the interface class context. Test case included. --- src/mono/mono/metadata/class-setup-vtable.c | 6 ++--- .../regressions/github61244.cs | 25 +++++++++++++++++++ .../regressions/github61244.csproj | 9 +++++++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.cs create mode 100644 src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.csproj diff --git a/src/mono/mono/metadata/class-setup-vtable.c b/src/mono/mono/metadata/class-setup-vtable.c index e1d66fe05eb1e2..16b7dfa8bb80dd 100644 --- a/src/mono/mono/metadata/class-setup-vtable.c +++ b/src/mono/mono/metadata/class-setup-vtable.c @@ -1742,11 +1742,11 @@ mono_class_setup_vtable_general (MonoClass *klass, MonoMethod **overrides, int o for (int i = 0; i < iface_onum; i++) { MonoMethod *decl = iface_overrides [i*2]; MonoMethod *override = iface_overrides [i*2 + 1]; - if (decl->is_inflated) { + if (mono_class_is_gtd (override->klass)) { + override = mono_class_inflate_generic_method_full_checked (override, ic, mono_class_get_context (ic), error); + } else if (decl->is_inflated) { override = mono_class_inflate_generic_method_checked (override, mono_method_get_context (decl), error); mono_error_assert_ok (error); - } else if (mono_class_is_gtd (override->klass)) { - override = mono_class_inflate_generic_method_full_checked (override, ic, mono_class_get_context (ic), error); } if (!apply_override (klass, ic, vtable, decl, override, &override_map, &override_class_map, &conflict_map)) goto fail; diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.cs new file mode 100644 index 00000000000000..917d6a4c6917d9 --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +class Program +{ + static int Main(string[] args) + { + new TestClass(); + return 100; + } +} + +public interface FirstInterface +{ + void SomeFunc1(T someParam); +} + +public interface SecondInterface : FirstInterface +{ + void FirstInterface.SomeFunc1(T1 someParam) => Console.WriteLine ("Test"); +} + +public class TestClass : SecondInterface{} diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.csproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.csproj new file mode 100644 index 00000000000000..42c7a9e8ffc90f --- /dev/null +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.csproj @@ -0,0 +1,9 @@ + + + true + Exe + + + + + From ce9b19802212f81e64765127b0d44227a65147c8 Mon Sep 17 00:00:00 2001 From: Bill Holmes Date: Thu, 3 Feb 2022 10:03:14 -0500 Subject: [PATCH 2/2] Update test to ensure that the correct context is used --- .../regressions/github61244.cs | 72 ++++++++++++++++--- 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.cs b/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.cs index 917d6a4c6917d9..ae08369177388d 100644 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.cs +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/regressions/github61244.cs @@ -1,25 +1,81 @@ -// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; +// In GH issue 61244 the mono runtime aborted when inflating the default +// interface method because the context used was from the base interface. + +// The OneArgBaseInterface portion of this test handles the original bug +// where the base interface has less generic arguments than the derived +// interface and the runtime aborts. + +// The SecondInterface portion of this test handles an additional scenario +// where the number of generic arguments are the same in the base and +// derived interface contexts, but the order is changed (or different.) +// When this occurs the generic info is incorrect for the inflated method. + class Program { static int Main(string[] args) { - new TestClass(); - return 100; + return new TestClass().DoTest(); } } -public interface FirstInterface +public interface OneArgBaseInterface { - void SomeFunc1(T someParam); + int SomeFunc1(T1 someParam1, Type someParam1Type); } -public interface SecondInterface : FirstInterface +public interface TwoArgBaseInterface { - void FirstInterface.SomeFunc1(T1 someParam) => Console.WriteLine ("Test"); + int SomeFunc1(T1 someParam1, T2 someParam2, Type someParam1Type, Type someParam2Type); } -public class TestClass : SecondInterface{} +public interface SecondInterface : OneArgBaseInterface, TwoArgBaseInterface +{ + int OneArgBaseInterface.SomeFunc1(TParam1Type someParam1, Type someParam1Type) + { + if (typeof(TParam1Type) != someParam1Type) + { + Console.WriteLine("Failed => 101"); + return 101; + } + + return 100; + } + + int TwoArgBaseInterface.SomeFunc1(TParam1Type someParam1, TParam2Type someParam2, Type someParam1Type, Type someParam2Type) + { + if (typeof(TParam1Type) != someParam1Type) + { + Console.WriteLine("Failed => 102"); + return 102; + } + + if (typeof(TParam2Type) != someParam2Type) + { + Console.WriteLine("Failed => 103"); + return 103; + } + + return 100; + } +} + +public class TestClass : SecondInterface +{ + public int DoTest () + { + int ret = (this as OneArgBaseInterface).SomeFunc1("test string", typeof(string)); + if (ret != 100) + return ret; + + ret = (this as TwoArgBaseInterface).SomeFunc1("test string", 0, typeof(string), typeof(int)); + if (ret != 100) + return ret; + + Console.WriteLine("Passed => 100"); + return 100; + } +} \ No newline at end of file