Bug 37436 - Invocation of Func<> based delegates fails on v4.2.1.124 on OSX & Linux: Delegate is never actually invoked by runtime
Summary: Invocation of Func<> based delegates fails on v4.2.1.124 on OSX & Linux: Dele...
Status: RESOLVED FIXED
Alias: None
Product: Runtime
Classification: Mono
Component: JIT (show other bugs)
Version: 4.2.0 (C6)
Hardware: All All
: --- normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2016-01-06 06:45 UTC by Pablo Ruiz García
Modified: 2016-01-06 15:55 UTC (History)
7 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Sample code/proyect demonstrating the issue (10.82 KB, application/gzip)
2016-01-06 06:45 UTC, Pablo Ruiz García
Details

Description Pablo Ruiz García 2016-01-06 06:45:17 UTC
Created attachment 14457 [details]
Sample code/proyect demonstrating the issue

Hi,

The code show at the end of this issue demonstrates a failure by which a method assigned to a "Func<T, T, bool>" variable, when invoked is silently ignored by runtime, returning false (default(bool)) as result, but never actually invoking the actual method:

This code has been tested (sucessfully) on MS.NET as well as mono-3.10.0/linux. While it has been found to fail on mono v4.2.1 (6dd2d0d) on OSX, and on mono v4.2.1.124 (from tarball) on Linux.

The output of a successfull execution (ie. MS.NET or mono v3.10.0 on linux) is as follows:

  [root@mono-3.10.0 ~]# mono /tmp/TestEquality.exe
  Testing left.Equals: True (1/1/2001 12:00:00 AM vs 1/1/2001 12:00:00 AM)
  Testing left.Equals: False (2/2/2002 12:00:00 AM vs 3/3/2003 12:00:00 AM)
  Success!!!

While a failing execution looks like:

   thismac:TestEquality dir$ mono ./bin/Debug/TestEquality.exe

   Unhandled Exception:
   System.InvalidOperationException: Inconsistent Equalitiy Comparer
     at TestEquality.Program.Main () <0x71ded8 + 0x00193> in <filename unknown>:0
   [ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: Inconsistent Equalitiy Comparer
     at TestEquality.Program.Main () <0x71ded8 + 0x00193> in <filename unknown>:0

In order to build/test the issue a sample project (build-able using xbuild) has been attached to this issue, including the actual binaries failing at my system.

NOTE: It looks like minor modifications to this code (like removing any method from 'IComparer' interface declaration) may provoke this same code to work fine.. So care must be taken to maintain the same code and method signatures in order to repro.

The actual failing code:

using System;

namespace TestEquality
{
	public interface IComparer
	{
		bool ApproximatelyEqual<TValue, TDifference>(TValue left, TValue right, TDifference delta);
		int Compare<T>(T left, T right);
		bool Equals(object left, object right);
		bool Equals<T>(T left, T right);
	}

	public class Comparer : IComparer
	{
		public new bool Equals(object left, object right)
		{
			return this.Equals<object>(left, right);
		}

		public bool Equals<T>(T left, T right)
		{
			if (object.ReferenceEquals(left, right))
			{
				Console.WriteLine("object.ReferenceEquals is true");
				return true;
			}
			if (object.ReferenceEquals(null, left) || object.ReferenceEquals(null, right))
			{
				Console.WriteLine("object.ReferenceEquals with null: return false");
				return false;
			}

			var result = left.Equals(right);
			Console.WriteLine("Testing left.Equals: {0} ({1} vs {2})", result, Convert.ToString(left), Convert.ToString(right));

			return result;
		}

		public int Compare<T>(T left, T right)
		{
			throw new NotImplementedException("Compare");
		}

		public bool ApproximatelyEqual<TValue, TDifference>(TValue left, TValue right, TDifference delta)
		{
			throw new NotImplementedException("Compare");
		}
	}

	public class Program
	{
		public static bool EqualsTest<T>(T left, T right)
		{
			IComparer ec = (IComparer)new Comparer();

			Func<T, T, bool> comparer = ec.Equals<T>;

			var result1 = comparer(left, right);

			return result1;
		}

		public static void Main()
		{
			var date1a = new DateTime(2001, 1, 1);
			var date1b = new DateTime(2001, 1, 1);
			var date2 = new DateTime(2002, 2, 2);
			var date3 = new DateTime(2003, 3, 3);

			var test1 = EqualsTest<DateTime>(date1a, date1b);
			var test2 = EqualsTest<DateTime>(date2, date3);
			if (test1 == test2)
				throw new InvalidOperationException("Inconsistent Equalitiy Comparer");

			Console.WriteLine("Success!!!");
		}
	}
}
Comment 1 Zoltan Varga 2016-01-06 08:32:28 UTC
Reduced testcase:
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
using System;

	public interface IComparer2
	{
		bool Equals<T>(T left, T right);
	}

	public class Comparer : IComparer2
	{
		public bool Equals<T>(T left, T right)
		{
			Console.WriteLine ("A: " + left);
			Console.WriteLine ("A: " + right);
			return false;
		}
	}

	public class Program
	{
		public static void Main()
		{
			var date1a = new DateTime(2001, 1, 1);
			var date1b = new DateTime(2001, 1, 1);

			IComparer2 ec = (IComparer2)new Comparer();

			Func<DateTime, DateTime, bool> comparer = ec.Equals<DateTime>;

			//Console.WriteLine (comparer.Method);
			comparer(date1a, date1b);
		}
	}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

The problem is probably in mono_get_delegate_virtual_invoke_impl () in mini-runtime.c.
This change seems to fix it:

diff --git a/mono/mini/mini-runtime.c b/mono/mini/mini-runtime.c
index 9f62e40..2f61cb1 100644
--- a/mono/mini/mini-runtime.c
+++ b/mono/mini/mini-runtime.c
@@ -2993,7 +2993,7 @@ mono_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *met
        is_interface = method->klass->flags & TYPE_ATTRIBUTE_INTERFACE ? TRUE : FALSE;
        load_imt_reg = is_virtual_generic || is_interface;
 
-       if (is_interface && !is_virtual_generic)
+       if (is_interface)
                offset = ((gint32)mono_method_get_imt_slot (method) - MONO_IMT_SIZE) * SIZEOF_VOID_P;
        else
                offset = G_STRUCT_OFFSET (MonoVTable, vtable) + ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
Comment 2 Zoltan Varga 2016-01-06 08:59:35 UTC
Fixed in mono master a0d5b21a455f3d5b11076e3e8b907a6a234b5fc6.
Comment 3 Pablo Ruiz García 2016-01-06 15:55:14 UTC
Amazing job Zoltan!.. Thnks ;)

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.


Create a new report for Bug 37436 on Developer Community or GitHub if you have new information to add and do not yet see a matching report.

  • Export the original title and description: Developer Community HTML or GitHub Markdown
  • Copy the title and description into the new report. Adjust them to be up-to-date if needed.
  • Add your new information.

In special cases on GitHub you might also want the comments: GitHub Markdown with public comments


Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Related Links: