Don't emit reaonly. prefix for reference loads
[mono.git] / mcs / mcs / pending.cs
index dac273f6ca935cf0765ba6ec78eba71886f68bbc..696797aa01f3ff4346a6d6159c1a3cec68ded53e 100644 (file)
 
 using System;
 using System.Collections.Generic;
+using System.Linq;
+
+#if STATIC
+using IKVM.Reflection;
+using IKVM.Reflection.Emit;
+#else
 using System.Reflection;
 using System.Reflection.Emit;
-using System.Linq;
+#endif
 
 namespace Mono.CSharp {
 
@@ -50,7 +56,7 @@ namespace Mono.CSharp {
                /// <summary>
                ///   The container for this PendingImplementation
                /// </summary>
-               TypeContainer container;
+               readonly TypeContainer container;
                
                /// <summary>
                ///   This is the array of TypeAndMethods that describes the pending implementations
@@ -91,6 +97,12 @@ namespace Mono.CSharp {
                        }
                }
 
+               Report Report {
+                       get {
+                               return container.Module.Compiler.Report;
+                       }
+               }
+
                struct MissingInterfacesInfo {
                        public TypeSpec Type;
                        public bool Optional;
@@ -102,7 +114,7 @@ namespace Mono.CSharp {
                        }
                }
 
-               static MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
+               static readonly MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
                
                static MissingInterfacesInfo [] GetMissingInterfaces (TypeContainer container)
                {
@@ -180,7 +192,74 @@ namespace Mono.CSharp {
                        if (total == 0)
                                return null;
 
-                       return new PendingImplementation (container, missing_interfaces, abstract_methods, total);
+                       var pending = new PendingImplementation (container, missing_interfaces, abstract_methods, total);
+
+                       //
+                       // check for inherited conflicting methods
+                       //
+                       foreach (var p in pending.pending_implementations) {
+                               //
+                               // It can happen for generic interfaces only
+                               //
+                               if (!p.type.IsGeneric)
+                                       continue;
+
+                               //
+                               // CLR does not distinguishes between ref and out
+                               //
+                               for (int i = 0; i < p.methods.Count; ++i) {
+                                       MethodSpec compared_method = p.methods[i];
+                                       if (compared_method.Parameters.IsEmpty)
+                                               continue;
+
+                                       for (int ii = i + 1; ii < p.methods.Count; ++ii) {
+                                               MethodSpec tested_method = p.methods[ii];
+                                               if (compared_method.Name != tested_method.Name)
+                                                       continue;
+
+                                               if (p.type != tested_method.DeclaringType)
+                                                       continue;
+
+                                               if (!TypeSpecComparer.Override.IsSame (compared_method.Parameters.Types, tested_method.Parameters.Types))
+                                                       continue;
+
+                                               bool exact_match = true;
+                                               bool ref_only_difference = false;
+                                               var cp = compared_method.Parameters.FixedParameters;
+                                               var tp = tested_method.Parameters.FixedParameters;
+
+                                               for (int pi = 0; pi < cp.Length; ++pi) {
+                                                       //
+                                                       // First check exact modifiers match
+                                                       //
+                                                       const Parameter.Modifier ref_out = Parameter.Modifier.REF | Parameter.Modifier.OUT;
+                                                       if ((cp[pi].ModFlags & ref_out) == (tp[pi].ModFlags & ref_out))
+                                                               continue;
+
+                                                       if ((cp[pi].ModFlags & tp[pi].ModFlags & Parameter.Modifier.ISBYREF) != 0) {
+                                                               ref_only_difference = true;
+                                                               continue;
+                                                       }
+
+                                                       exact_match = false;
+                                                       break;
+                                               }
+
+                                               if (!exact_match || !ref_only_difference)
+                                                       continue;
+
+                                               pending.Report.SymbolRelatedToPreviousError (compared_method);
+                                               pending.Report.SymbolRelatedToPreviousError (tested_method);
+                                               pending.Report.Error (767, container.Location,
+                                                       "Cannot implement interface `{0}' with the specified type parameters because it causes method `{1}' to differ on parameter modifiers only",
+                                                       p.type.GetDefinition().GetSignatureForError (), compared_method.GetSignatureForError ());
+
+                                               break;
+                                       }
+                               }
+                       }
+
+                       return pending;
                }
 
                public enum Operation {
@@ -194,14 +273,14 @@ namespace Mono.CSharp {
                /// <summary>
                ///   Whether the specified method is an interface method implementation
                /// </summary>
-               public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method)
+               public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method, out MethodSpec ambiguousCandidate)
                {
-                       return InterfaceMethod (name, ifaceType, method, Operation.Lookup);
+                       return InterfaceMethod (name, ifaceType, method, Operation.Lookup, out ambiguousCandidate);
                }
 
-               public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one
+               public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one, out MethodSpec ambiguousCandidate)
                {
-                       InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll);
+                       InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll, out ambiguousCandidate);
                }
 
                /// <remarks>
@@ -221,21 +300,23 @@ namespace Mono.CSharp {
                ///   that was used in the interface, then we always need to create a proxy for it.
                ///
                /// </remarks>
-               public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op)
+               public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op, out MethodSpec ambiguousCandidate)
                {
+                       ambiguousCandidate = null;
+
                        if (pending_implementations == null)
                                return null;
 
                        TypeSpec ret_type = method.method.ReturnType;
                        ParametersCompiled args = method.method.ParameterInfo;
                        bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod;
+                       MethodSpec m;
 
                        foreach (TypeAndMethods tm in pending_implementations){
                                if (!(iType == null || tm.type == iType))
                                        continue;
 
                                int method_count = tm.methods.Count;
-                               MethodSpec m;
                                for (int i = 0; i < method_count; i++){
                                        m = tm.methods [i];
 
@@ -270,6 +351,9 @@ namespace Mono.CSharp {
                                        // for an interface indexer).
                                        //
                                        if (op != Operation.Lookup) {
+                                               if (m.IsAccessor != method.method.IsAccessor)
+                                                       continue;
+
                                                // If `t != null', then this is an explicitly interface
                                                // implementation and we can always clear the method.
                                                // `need_proxy' is not null if we're implementing an
@@ -285,6 +369,11 @@ namespace Mono.CSharp {
                                                tm.found [i] = method;
                                        }
 
+                                       if (op == Operation.Lookup && name.Left != null && ambiguousCandidate == null) {
+                                               ambiguousCandidate = m;
+                                               continue;
+                                       }
+
                                        //
                                        // Lookups and ClearOne return
                                        //
@@ -294,9 +383,12 @@ namespace Mono.CSharp {
 
                                // If a specific type was requested, we can stop now.
                                if (tm.type == iType)
-                                       return null;
+                                       break;
                        }
-                       return null;
+
+                       m = ambiguousCandidate;
+                       ambiguousCandidate = null;
+                       return m;
                }
 
                /// <summary>
@@ -322,10 +414,11 @@ namespace Mono.CSharp {
 
                        MethodBuilder proxy = container.TypeBuilder.DefineMethod (
                                proxy_name,
+                               MethodAttributes.Private |
                                MethodAttributes.HideBySig |
                                MethodAttributes.NewSlot |
                                MethodAttributes.CheckAccessOnOverride |
-                               MethodAttributes.Virtual,
+                               MethodAttributes.Virtual | MethodAttributes.Final,
                                CallingConventions.Standard | CallingConventions.HasThis,
                                base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
 
@@ -341,7 +434,7 @@ namespace Mono.CSharp {
                        }
 
                        int top = param.Count;
-                       var ec = new EmitContext (null, proxy.GetILGenerator (), null);\r
+                       var ec = new EmitContext (null, proxy.GetILGenerator (), null);
                        // TODO: GetAllParametersArguments
                        for (int i = 0; i <= top; i++)
                                ParameterReference.EmitLdArg (ec, i);
@@ -359,24 +452,112 @@ namespace Mono.CSharp {
                /// </summary>
                bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
                {
+                       base_method = null;
                        var base_type = container.BaseType;
-                       base_method = (MethodSpec) MemberCache.FindMember (base_type, new MemberFilter (mi), BindingRestriction.None);
 
-                       if (base_method == null || (base_method.Modifiers & Modifiers.PUBLIC) == 0)
-                               return false;
+                       //
+                       // Setup filter with no return type to give better error message
+                       // about mismatch at return type when the check bellow rejects them
+                       //
+                       var parameters = mi.Parameters;
+                       while (true) {
+                               var candidates = MemberCache.FindMembers (base_type, mi.Name, false);
+                               if (candidates == null)
+                                       return false;
+
+                               MethodSpec similar_candidate = null;
+                               foreach (var candidate in candidates) {
+                                       if (candidate.Kind != MemberKind.Method)
+                                               continue;
+
+                                       if (candidate.Arity != mi.Arity)
+                                               continue;
+
+                                       var candidate_param = ((MethodSpec) candidate).Parameters;
+                                       if (!TypeSpecComparer.Override.IsSame (parameters.Types, candidate_param.Types))
+                                               continue;
+
+                                       bool modifiers_match = true;
+                                       for (int i = 0; i < parameters.Count; ++i) {
+                                               //
+                                               // First check exact ref/out match
+                                               //
+                                               const Parameter.Modifier ref_out = Parameter.Modifier.REF | Parameter.Modifier.OUT;
+                                               if ((parameters.FixedParameters[i].ModFlags & ref_out) == (candidate_param.FixedParameters[i].ModFlags & ref_out))
+                                                       continue;
 
-                       if (base_method.DeclaringType.IsInterface)
-                               return false;
+                                               modifiers_match = false;
 
-                       // Why was it here ????
-                       //if (TypeManager.ImplementsInterface (base_type, iface_type)) {
-                       //      return true;
-                       //}
+                                               //
+                                               // Different in ref/out only
+                                               //
+                                               if ((parameters.FixedParameters[i].ModFlags & candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.ISBYREF) != 0) {
+                                                       if (similar_candidate == null) {
+                                                               if (!candidate.IsPublic)
+                                                                       break;
 
-                       if (!base_method.IsAbstract && !base_method.IsVirtual)
-                               // FIXME: We can avoid creating a proxy if base_method can be marked 'final virtual' instead.
-                               //        However, it's too late now, the MethodBuilder has already been created (see bug 377519)
+                                                               if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, ((MethodSpec) candidate).ReturnType))
+                                                                       break;
+
+                                                               // It's used for ref/out ambiguity overload check
+                                                               similar_candidate = (MethodSpec) candidate;
+                                                       }
+
+                                                       continue;
+                                               }
+
+                                               similar_candidate = null;
+                                               break;
+                                       }
+
+                                       if (!modifiers_match)
+                                               continue;
+
+                                       //
+                                       // From this point on the candidate is used for detailed error reporting
+                                       // because it's very close match to what we are looking for
+                                       //
+                                       base_method = (MethodSpec) candidate;
+
+                                       if (!candidate.IsPublic)
+                                               return false;
+
+                                       if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, base_method.ReturnType))
+                                               return false;
+                               }
+
+                               if (base_method != null) {
+                                       if (similar_candidate != null) {
+                                               Report.SymbolRelatedToPreviousError (similar_candidate);
+                                               Report.SymbolRelatedToPreviousError (mi);
+                                               Report.SymbolRelatedToPreviousError (container);
+                                               Report.Warning (1956, 1, ((MemberCore) base_method.MemberDefinition).Location,
+                                                       "The interface method `{0}' implementation is ambiguous between following methods: `{1}' and `{2}' in type `{3}'",
+                                                       mi.GetSignatureForError (), base_method.GetSignatureForError (), similar_candidate.GetSignatureForError (), container.GetSignatureForError ());
+                                       }
+
+                                       break;
+                               }
+
+                               base_type = candidates[0].DeclaringType.BaseType;
+                               if (base_type == null)
+                                       return false;
+                       }
+
+                       if (!base_method.IsVirtual) {
+#if STATIC
+                               var base_builder = base_method.GetMetaInfo () as MethodBuilder;
+                               if (base_builder != null) {
+                                       //
+                                       // We can avoid creating a proxy if base_method can be marked 'final virtual'. This can
+                                       // be done for all methods from compiled assembly
+                                       //
+                                       base_builder.__SetAttributes (base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot);
+                                       return true;
+                               }
+#endif
                                DefineProxy (iface_type, base_method, mi);
+                       }
 
                        return true;
                }
@@ -385,7 +566,7 @@ namespace Mono.CSharp {
                ///   Verifies that any pending abstract methods or interface methods
                ///   were implemented.
                /// </summary>
-               public bool VerifyPendingMethods (Report Report)
+               public bool VerifyPendingMethods ()
                {
                        int top = pending_implementations.Length;
                        bool errors = false;
@@ -396,7 +577,7 @@ namespace Mono.CSharp {
 
                                bool base_implements_type = type.IsInterface &&
                                        container.BaseType != null &&
-                                       container.BaseType.ImplementsInterface (type);
+                                       container.BaseType.ImplementsInterface (type, false);
 
                                for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
                                        var mi = pending_implementations[i].methods[j];