[mcs] Improve member lookup rules when type parameter has both non-object effective...
authorMarek Safar <marek.safar@gmail.com>
Thu, 15 Sep 2016 11:36:35 +0000 (13:36 +0200)
committerMarek Safar <marek.safar@gmail.com>
Thu, 15 Sep 2016 11:40:15 +0000 (13:40 +0200)
mcs/errors/cs0029-37.cs [new file with mode: 0644]
mcs/errors/cs1070-3.cs [new file with mode: 0644]
mcs/errors/cs1501-19.cs [new file with mode: 0644]
mcs/mcs/ecore.cs
mcs/mcs/expression.cs
mcs/mcs/generic.cs
mcs/mcs/membercache.cs
mcs/tests/gtest-639.cs [new file with mode: 0644]
mcs/tests/ver-il-net_4_x.xml

diff --git a/mcs/errors/cs0029-37.cs b/mcs/errors/cs0029-37.cs
new file mode 100644 (file)
index 0000000..80f8e8c
--- /dev/null
@@ -0,0 +1,24 @@
+// CS0029: Cannot implicitly convert type `string' to `int'
+// Line: 8
+
+class A<T> where T : CB, IA
+{
+       void Foo (T t)
+       {
+               t.Prop = "3";
+       }
+}
+
+class CB : CA
+{
+}
+
+class CA
+{
+       public int Prop { get; set; }
+}
+
+interface IA
+{
+       string Prop { get; set; }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs1070-3.cs b/mcs/errors/cs1070-3.cs
new file mode 100644 (file)
index 0000000..18811dc
--- /dev/null
@@ -0,0 +1,11 @@
+// CS1070: The type `C' has been forwarded to an assembly that is not referenced. Consider adding a reference to assembly `CS1070-lib-missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'
+// Line: 5
+// Compiler options: -r:CS1070-lib.dll
+
+public class D
+{
+       static void Main ()
+       {
+               new C ();
+       }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs1501-19.cs b/mcs/errors/cs1501-19.cs
new file mode 100644 (file)
index 0000000..68095c1
--- /dev/null
@@ -0,0 +1,26 @@
+// CS1501: No overload for method `Call' takes `0' arguments
+// Line: 8
+
+class A<T> where T : CB, IA
+{
+       void Foo (T t)
+       {
+               t.Call ();
+       }
+}
+
+class CB : CA
+{
+}
+
+class CA
+{
+       public void Call (int arg)
+       {
+       }
+}
+
+interface IA
+{
+       void Call (bool arg, int arg2);
+}
index 42eba97ee475763eb15186ea8a25bbe36fc1fca9..0937671675d24484f996e4146ae29a38c5ac1137 100644 (file)
@@ -841,22 +841,29 @@ namespace Mono.CSharp {
                public static Expression MemberLookup (IMemberContext rc, bool errorMode, TypeSpec queried_type, string name, int arity, MemberLookupRestrictions restrictions, Location loc)
                {
                        var members = MemberCache.FindMembers (queried_type, name, false);
-                       if (members == null)
-                               return null;
 
-                       Expression expr;
-                       do {
-                               expr = MemberLookupToExpression (rc, members, errorMode, queried_type, name, arity, restrictions, loc);
-                               if (expr != null)
-                                       return expr;
+                       if (members != null) {
+                               Expression expr;
+                               do {
+                                       expr = MemberLookupToExpression (rc, members, errorMode, queried_type, name, arity, restrictions, loc);
+                                       if (expr != null)
+                                               return expr;
 
-                               if (members [0].DeclaringType.BaseType == null)
-                                       members = null;
-                               else
-                                       members = MemberCache.FindMembers (members [0].DeclaringType.BaseType, name, false);
-                       } while (members != null);
+                                       if (members [0].DeclaringType.BaseType == null)
+                                               members = null;
+                                       else
+                                               members = MemberCache.FindMembers (members [0].DeclaringType.BaseType, name, false);
+                               } while (members != null);
+                       }
 
-                       return expr;
+                       var tps = queried_type as TypeParameterSpec;
+                       if (tps != null) {
+                               members = MemberCache.FindInterfaceMembers (tps, name);
+                               if (members != null)
+                                       return MemberLookupToExpression (rc, members, errorMode, queried_type, name, arity, restrictions, loc);
+                       }
+
+                       return null;
                }
 
                public static Expression MemberLookupToExpression (IMemberContext rc, IList<MemberSpec> members, bool errorMode, TypeSpec queried_type, string name, int arity, MemberLookupRestrictions restrictions, Location loc)
@@ -901,15 +908,6 @@ namespace Mono.CSharp {
 
                                if ((restrictions & MemberLookupRestrictions.InvocableOnly) != 0) {
                                        if (member is MethodSpec) {
-                                               //
-                                               // Interface members that are hidden by class members are removed from the set. This
-                                               // step only has an effect if T is a type parameter and T has both an effective base 
-                                               // class other than object and a non-empty effective interface set
-                                               //
-                                               var tps = queried_type as TypeParameterSpec;
-                                               if (tps != null && tps.HasTypeConstraint)
-                                                       members = RemoveHiddenTypeParameterMethods (members);
-
                                                return new MethodGroupExpr (members, queried_type, loc);
                                        }
 
@@ -959,57 +957,6 @@ namespace Mono.CSharp {
                        return null;
                }
 
-               static IList<MemberSpec> RemoveHiddenTypeParameterMethods (IList<MemberSpec> members)
-               {
-                       if (members.Count < 2)
-                               return members;
-
-                       //
-                       // If M is a method, then all non-method members declared in an interface declaration
-                       // are removed from the set, and all methods with the same signature as M declared in
-                       // an interface declaration are removed from the set
-                       //
-
-                       bool copied = false;
-                       for (int i = 0; i < members.Count; ++i) {
-                               var method = members[i] as MethodSpec;
-                               if (method == null) {
-                                       if (!copied) {
-                                               copied = true;
-                                               members = new List<MemberSpec> (members);
-                                       } 
-                                       
-                                       members.RemoveAt (i--);
-                                       continue;
-                               }
-
-                               if (!method.DeclaringType.IsInterface)
-                                       continue;
-
-                               for (int ii = 0; ii < members.Count; ++ii) {
-                                       var candidate = members[ii] as MethodSpec;
-                                       if (candidate == null || !candidate.DeclaringType.IsClass)
-                                               continue;
-
-                                       if (!TypeSpecComparer.Override.IsEqual (candidate.Parameters, method.Parameters))
-                                               continue;
-
-                                       if (!AParametersCollection.HasSameParameterDefaults (candidate.Parameters, method.Parameters))
-                                               continue;
-
-                                       if (!copied) {
-                                               copied = true;
-                                               members = new List<MemberSpec> (members);
-                                       }
-
-                                       members.RemoveAt (i--);
-                                       break;
-                               }
-                       }
-
-                       return members;
-               }
-
                protected static void Error_NamedArgument (NamedArgument na, Report Report)
                {
                        Report.Error (1742, na.Location, "An element access expression cannot use named argument");
@@ -3776,7 +3723,7 @@ namespace Mono.CSharp {
                // For extension methodgroup we are not looking for base members but parent
                // namespace extension methods
                //
-               public override IList<MemberSpec> GetBaseMembers (TypeSpec baseType)
+               public override IList<MemberSpec> GetBaseMembers (TypeSpec type)
                {
                        // TODO: candidates are null only when doing error reporting, that's
                        // incorrect. We have to discover same extension methods in error mode
@@ -4233,9 +4180,19 @@ namespace Mono.CSharp {
 
                #region IBaseMembersProvider Members
 
-               public virtual IList<MemberSpec> GetBaseMembers (TypeSpec baseType)
+               public virtual IList<MemberSpec> GetBaseMembers (TypeSpec type)
                {
-                       return baseType == null ? null : MemberCache.FindMembers (baseType, Methods [0].Name, false);
+                       var baseType = type.BaseType;
+                       
+                       IList<MemberSpec> members = baseType == null ? null : MemberCache.FindMembers (baseType, Methods [0].Name, false);
+
+                       if (members == null && !type.IsInterface) {
+                               var tps = queried_type as TypeParameterSpec;
+                               if (tps != null)
+                                       members = MemberCache.FindInterfaceMembers (tps, Methods [0].Name);
+                       }
+
+                       return members;
                }
 
                public IParametersMember GetOverrideMemberParameters (MemberSpec member)
@@ -5632,7 +5589,7 @@ namespace Mono.CSharp {
                                                // Restore expanded arguments
                                                candidate_args = args;
                                        }
-                               } while (best_candidate_rate != 0 && (type_members = base_provider.GetBaseMembers (type_members[0].DeclaringType.BaseType)) != null);
+                               } while (best_candidate_rate != 0 && (type_members = base_provider.GetBaseMembers (type_members[0].DeclaringType)) != null);
 
                                //
                                // We've found exact match
index db50ba39b8e9b2642e53e57951b1402edd637d4f..ae27eb24900d5eed8c6ee3ae36589ff54643b1cc 100644 (file)
@@ -10952,9 +10952,18 @@ namespace Mono.CSharp
 
                #region IBaseMembersProvider Members
 
-               IList<MemberSpec> OverloadResolver.IBaseMembersProvider.GetBaseMembers (TypeSpec baseType)
+               IList<MemberSpec> OverloadResolver.IBaseMembersProvider.GetBaseMembers (TypeSpec type)
                {
-                       return baseType == null ? null : MemberCache.FindMembers (baseType, MemberCache.IndexerNameAlias, false);
+                       var baseType = type.BaseType;
+                       var members = baseType == null ? null : MemberCache.FindMembers (baseType, MemberCache.IndexerNameAlias, false);
+
+                       if (members == null && !type.IsInterface) {
+                               var tps = queried_type as TypeParameterSpec;
+                               if (tps != null)
+                                       members = MemberCache.FindInterfaceMembers (tps, MemberCache.IndexerNameAlias);
+                       }
+
+                       return members;
                }
 
                IParametersMember OverloadResolver.IBaseMembersProvider.GetOverrideMemberParameters (MemberSpec member)
index b34f2dc1ec56dfbadb8ff468da7940d1f49b3ca4..48d2dc01b96c28e35cd2981830306cdbef1d0302 100644 (file)
@@ -773,6 +773,7 @@ namespace Mono.CSharp {
                TypeSpec[] targs;
                TypeSpec[] ifaces_defined;
                TypeSpec effective_base;
+               MemberCache interface_cache;
 
                //
                // Creates type owned type parameter
@@ -882,6 +883,12 @@ namespace Mono.CSharp {
                        }
                }
 
+               public MemberCache InterfaceCache {
+                       get {
+                               return interface_cache;
+                       }
+               }
+
                //
                // Unexpanded interfaces list
                //
@@ -1351,13 +1358,27 @@ namespace Mono.CSharp {
                        // For a type parameter the membercache is the union of the sets of members of the types
                        // specified as a primary constraint or secondary constraint
                        //
+                       bool has_user_base_type = false;
                        if (BaseType.BuiltinType != BuiltinTypeSpec.Type.Object && BaseType.BuiltinType != BuiltinTypeSpec.Type.ValueType) {
                                cache.AddBaseType (BaseType);
+                               has_user_base_type = true;
                        }
 
                        if (InterfacesDefined != null) {
+                               var icache = cache;
+                               if (has_user_base_type) {
+                                       //
+                                       // type-parameter lookup rules are more complicated that other types lookup rules.
+                                       // Effective base class and its base types member have priority over interface
+                                       // constraints which means we cannot lookup interface members before class members
+                                       // hence we setup secondary cache for such cases.
+                                       //
+                                       interface_cache = new MemberCache ();
+                                       icache = interface_cache;
+                               }
+
                                foreach (var iface_type in InterfacesDefined) {
-                                       cache.AddInterface (iface_type);
+                                       icache.AddInterface (iface_type);
                                }
                        }
 
@@ -1370,8 +1391,14 @@ namespace Mono.CSharp {
                                        var ifaces = tps == null ? ta.Interfaces : tps.InterfacesDefined;
 
                                        if (ifaces != null) {
+                                               var icache = cache;
+                                               if (has_user_base_type) {
+                                                       interface_cache = new MemberCache ();
+                                                       icache = interface_cache;
+                                               }
+
                                                foreach (var iface_type in ifaces) {
-                                                       cache.AddInterface (iface_type);
+                                                       icache.AddInterface (iface_type);
                                                }
                                        }
                                }
index 8ada0a97071a327cd2c8433a09eb654d904633bd..27b7f586932c7d01e355f5bc2745512d5cb07a1a 100644 (file)
@@ -406,8 +406,13 @@ namespace Mono.CSharp {
 
                public static MemberSpec FindMember (TypeSpec container, MemberFilter filter, BindingRestriction restrictions)
                {
+                       if (filter.Kind == MemberKind.Method && container.Kind == MemberKind.TypeParameter && filter.Parameters == null)
+                               throw new NotSupportedException ("type parameters methods cannot be lookup up due to two stage setup");
+
+                       IList<MemberSpec> applicable;
+                       var top_container = container;
+
                        do {
-                               IList<MemberSpec> applicable;
                                if (container.MemberCache.member_hash.TryGetValue (filter.Name, out applicable)) {
                                        // Start from the end because interface members are in reverse order
                                        for (int i = applicable.Count - 1; i >= 0; i--) {
@@ -438,6 +443,26 @@ namespace Mono.CSharp {
                                container = container.BaseType;
                        } while (container != null);
 
+                       var tps = top_container as TypeParameterSpec;
+                       if (tps != null && tps.InterfaceCache != null) {
+                               if (tps.InterfaceCache.member_hash.TryGetValue (filter.Name, out applicable)) {
+                                       for (int i = applicable.Count - 1; i >= 0; i--) {
+                                               var entry = applicable [i];
+
+                                               if ((restrictions & BindingRestriction.NoAccessors) != 0 && entry.IsAccessor)
+                                                       continue;
+
+                                               if ((restrictions & BindingRestriction.OverrideOnly) != 0 && (entry.Modifiers & Modifiers.OVERRIDE) == 0)
+                                                       continue;
+
+                                               if (!filter.Equals (entry))
+                                                       continue;
+
+                                               return entry;
+                                       }
+                               }
+                       }
+
                        return null;
                }
 
@@ -450,9 +475,9 @@ namespace Mono.CSharp {
                //
                public static IList<MemberSpec> FindMembers (TypeSpec container, string name, bool declaredOnlyClass)
                {
-                       IList<MemberSpec> applicable;
-
                        do {
+                               IList<MemberSpec> applicable;
+                               
                                if (container.MemberCache.member_hash.TryGetValue (name, out applicable) || declaredOnlyClass)
                                        return applicable;
 
@@ -462,6 +487,17 @@ namespace Mono.CSharp {
                        return null;
                }
 
+               public static IList<MemberSpec> FindInterfaceMembers (TypeParameterSpec typeParameter, string name)
+               {
+                       if (typeParameter.InterfaceCache != null) {
+                               IList<MemberSpec> applicable;
+                               typeParameter.InterfaceCache.member_hash.TryGetValue (name, out applicable);
+                               return applicable;
+                       }
+
+                       return null;
+               }
+
                //
                // Finds the nested type in container
                //
diff --git a/mcs/tests/gtest-639.cs b/mcs/tests/gtest-639.cs
new file mode 100644 (file)
index 0000000..afc0b20
--- /dev/null
@@ -0,0 +1,47 @@
+class A<T> where T : CB, IA
+{
+       void Foo (T t)
+       {
+               t.Prop = 3;
+               long l = t.Prop2;
+               t["1"] = "2";
+       }
+}
+
+class A2<T, U> 
+       where T : CB, U
+       where U : IA
+{
+       void Foo (T t)
+       {
+               t.Prop = 3;
+               long l = t.Prop2;
+               t["1"] = "2";
+       }
+}
+
+class CB : CA
+{
+}
+
+class CA
+{
+       public int Prop { get; set; }
+
+       public string this [byte b] { get { return ""; } }
+}
+
+interface IA
+{
+       string Prop { get; set; }
+       long Prop2 { get; }
+
+       string this [string b] { get; set; }
+}
+
+class X
+{
+       public static void Main ()
+       {
+       }
+}
\ No newline at end of file
index 5d65a14e47979efc27d2accaa0b8f36367c611c2..2ef20e19608c53fa53918153e1d01fa2feacff8b 100644 (file)
       </method>
     </type>
   </test>
+  <test name="gtest-639.cs">
+    <type name="A`1[T]">
+      <method name="Void Foo(T)" attrs="129">
+        <size>53</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+    <type name="CB">
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+    <type name="CA">
+      <method name="Int32 get_Prop()" attrs="2182">
+        <size>14</size>
+      </method>
+      <method name="Void set_Prop(Int32)" attrs="2182">
+        <size>8</size>
+      </method>
+      <method name="System.String get_Item(Byte)" attrs="2182">
+        <size>14</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+    <type name="X">
+      <method name="Void Main()" attrs="150">
+        <size>2</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+    <type name="A2`2[T,U]">
+      <method name="Void Foo(T)" attrs="129">
+        <size>53</size>
+      </method>
+      <method name="Void .ctor()" attrs="6278">
+        <size>7</size>
+      </method>
+    </type>
+  </test>
   <test name="gtest-anontype-01.cs">
     <type name="Test">
       <method name="Int32 Main()" attrs="150">