[linker] Fix comparison of methods wrt generic parameters [#30488]
authorSebastien Pouliot <sebastien@xamarin.com>
Wed, 1 Jul 2015 15:37:00 +0000 (11:37 -0400)
committerSebastien Pouliot <sebastien@xamarin.com>
Wed, 1 Jul 2015 15:37:00 +0000 (11:37 -0400)
The check from [1] is fine when it's done once _or_ with an identical
target. However there's nothing to ensure (if done more than once) that
it's being used to represent the same thing every time.

That makes the code from [1] match methods like:

> System.IObservable`1<TResult> System.Reactive.Linq.IQueryLanguage::Repeat(TResult)

and

> System.IObservable`1<TSource> System.Reactive.Linq.QueryLanguage::Repeat(System.IObservable`1<TSource>)

where `TResult` is different in both checks.

This patch tracks what is used as the generic parameter (on first check)
and make sure it's identical if it's used again in the same signature.

Note: this is a very old bug (i.e. it's always been that way) but recent
changes to mono started to report such issues (a good thing) and, for AOT
compilation on XI, that resulted in MT3001 errors.

[1] https://github.com/mono/mono/blob/mono-3.10.0/mcs/tools/linker/Mono.Linker.Steps/TypeMapStep.cs#L250

mcs/tools/linker/Mono.Linker.Steps/TypeMapStep.cs

index afb814239a0a0ba7a1e7e7d1925d8300ef51216d..4f0e2eeaaee1c8a73fc39e71e370ad57d515f442 100644 (file)
@@ -179,14 +179,18 @@ namespace Mono.Linker.Steps {
                        if (!type.HasMethods)
                                return null;
 
-                       foreach (MethodDefinition candidate in type.Methods)
-                               if (MethodMatch (candidate, method))
+                       Dictionary<string,string> gp = null;
+                       foreach (MethodDefinition candidate in type.Methods) {
+                               if (MethodMatch (candidate, method, ref gp))
                                        return candidate;
+                               if (gp != null)
+                                       gp.Clear ();
+                       }
 
                        return null;
                }
 
-               static bool MethodMatch (MethodDefinition candidate, MethodDefinition method)
+               static bool MethodMatch (MethodDefinition candidate, MethodDefinition method, ref Dictionary<string,string> genericParameters)
                {
                        if (!candidate.IsVirtual)
                                return false;
@@ -200,7 +204,9 @@ namespace Mono.Linker.Steps {
                        if (candidate.HasGenericParameters != method.HasGenericParameters)
                                return false;
 
-                       if (!TypeMatch (candidate.ReturnType, method.ReturnType))
+                       // we need to track what the generic parameter represent - as we cannot allow it to
+                       // differ between the return type or any parameter
+                       if (!TypeMatch (candidate.ReturnType, method.ReturnType, ref genericParameters))
                                return false;
 
                        if (!candidate.HasParameters)
@@ -212,37 +218,37 @@ namespace Mono.Linker.Steps {
                                return false;
 
                        for (int i = 0; i < cp.Count; i++) {
-                               if (!TypeMatch (cp [i].ParameterType, mp [i].ParameterType))
+                               if (!TypeMatch (cp [i].ParameterType, mp [i].ParameterType, ref genericParameters))
                                        return false;
                        }
 
                        return true;
                }
 
-               static bool TypeMatch (IModifierType a, IModifierType b)
+               static bool TypeMatch (IModifierType a, IModifierType b, ref Dictionary<string,string> gp)
                {
-                       if (!TypeMatch (a.ModifierType, b.ModifierType))
+                       if (!TypeMatch (a.ModifierType, b.ModifierType, ref gp))
                                return false;
 
-                       return TypeMatch (a.ElementType, b.ElementType);
+                       return TypeMatch (a.ElementType, b.ElementType, ref gp);
                }
 
-               static bool TypeMatch (TypeSpecification a, TypeSpecification b)
+               static bool TypeMatch (TypeSpecification a, TypeSpecification b, ref Dictionary<string,string> gp)
                {
                        var gita = a as GenericInstanceType;
                        if (gita != null)
-                               return TypeMatch (gita, (GenericInstanceType) b);
+                               return TypeMatch (gita, (GenericInstanceType) b, ref gp);
 
                        var mta = a as IModifierType;
                        if (mta != null)
-                               return TypeMatch (mta, (IModifierType) b);
+                               return TypeMatch (mta, (IModifierType) b, ref gp);
 
-                       return TypeMatch (a.ElementType, b.ElementType);
+                       return TypeMatch (a.ElementType, b.ElementType, ref gp);
                }
 
-               static bool TypeMatch (GenericInstanceType a, GenericInstanceType b)
+               static bool TypeMatch (GenericInstanceType a, GenericInstanceType b, ref Dictionary<string,string> gp)
                {
-                       if (!TypeMatch (a.ElementType, b.ElementType))
+                       if (!TypeMatch (a.ElementType, b.ElementType, ref gp))
                                return false;
 
                        if (a.HasGenericArguments != b.HasGenericArguments)
@@ -257,23 +263,34 @@ namespace Mono.Linker.Steps {
                                return false;
 
                        for (int i = 0; i < gaa.Count; i++) {
-                               if (!TypeMatch (gaa [i], gab [i]))
+                               if (!TypeMatch (gaa [i], gab [i], ref gp))
                                        return false;
                        }
 
                        return true;
                }
 
-               static bool TypeMatch (TypeReference a, TypeReference b)
+               static bool TypeMatch (TypeReference a, TypeReference b, ref Dictionary<string,string> gp)
                {
-                       if (a is GenericParameter)
-                               return true;
+                       var gpa = a as GenericParameter;
+                       if (gpa != null) {
+                               if (gp == null)
+                                       gp = new Dictionary<string, string> ();
+                               string match;
+                               if (!gp.TryGetValue (gpa.FullName, out match)) {
+                                       // first use, we assume it will always be used this way
+                                       gp.Add (gpa.FullName, b.ToString ());
+                                       return true;
+                               }
+                               // re-use, it should match the previous usage
+                               return match == b.ToString ();
+                       }
 
                        if (a is TypeSpecification || b is TypeSpecification) {
                                if (a.GetType () != b.GetType ())
                                        return false;
 
-                               return TypeMatch ((TypeSpecification) a, (TypeSpecification) b);
+                               return TypeMatch ((TypeSpecification) a, (TypeSpecification) b, ref gp);
                        }
 
                        return a.FullName == b.FullName;