2 // pending.cs: Pending method implementation
5 // Miguel de Icaza (miguel@gnu.org)
6 // Marek Safar (marek.safar@gmail.com)
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
10 // Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2003-2008 Novell, Inc.
12 // Copyright 2011 Xamarin Inc
16 using System.Collections.Generic;
20 using IKVM.Reflection;
21 using IKVM.Reflection.Emit;
23 using System.Reflection;
24 using System.Reflection.Emit;
27 namespace Mono.CSharp {
29 struct TypeAndMethods {
31 public IList<MethodSpec> methods;
34 // Whether it is optional, this is used to allow the explicit/implicit
35 // implementation when a base class already implements an interface.
39 // class X : IA { } class Y : X, IA { IA.Explicit (); }
44 // This flag on the method says `We found a match, but
45 // because it was private, we could not use the match
47 public MethodData [] found;
49 // If a method is defined here, then we always need to
50 // create a proxy for it. This is used when implementing
51 // an interface's indexer with a different IndexerName.
52 public MethodSpec [] need_proxy;
55 struct ProxyMethodContext : IMemberContext
57 readonly TypeContainer container;
59 public ProxyMethodContext (TypeContainer container)
61 this.container = container;
64 public TypeSpec CurrentType {
66 throw new NotImplementedException ();
70 public TypeParameters CurrentTypeParameters {
72 throw new NotImplementedException ();
76 public MemberCore CurrentMemberDefinition {
78 throw new NotImplementedException ();
82 public bool IsObsolete {
88 public bool IsUnsafe {
90 throw new NotImplementedException ();
94 public bool IsStatic {
100 public ModuleContainer Module {
102 return container.Module;
106 public string GetSignatureForError ()
108 throw new NotImplementedException ();
111 public ExtensionMethodCandidates LookupExtensionMethod (string name, int arity)
113 throw new NotImplementedException ();
116 public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
118 throw new NotImplementedException ();
121 public FullNamedExpression LookupNamespaceAlias (string name)
123 throw new NotImplementedException ();
127 public class PendingImplementation
130 /// The container for this PendingImplementation
132 readonly TypeDefinition container;
135 /// This is the array of TypeAndMethods that describes the pending implementations
136 /// (both interfaces and abstract methods in base class)
138 TypeAndMethods [] pending_implementations;
140 PendingImplementation (TypeDefinition container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total)
142 var type_builder = container.Definition;
144 this.container = container;
145 pending_implementations = new TypeAndMethods [total];
148 if (abstract_methods != null) {
149 int count = abstract_methods.Length;
150 pending_implementations [i].need_proxy = new MethodSpec [count];
152 pending_implementations [i].methods = abstract_methods;
153 pending_implementations [i].found = new MethodData [count];
154 pending_implementations [i].type = type_builder;
158 foreach (MissingInterfacesInfo missing in missing_ifaces) {
159 var iface = missing.Type;
160 var mi = MemberCache.GetInterfaceMethods (iface);
162 int count = mi.Count;
163 pending_implementations [i].type = iface;
164 pending_implementations [i].optional = missing.Optional;
165 pending_implementations [i].methods = mi;
166 pending_implementations [i].found = new MethodData [count];
167 pending_implementations [i].need_proxy = new MethodSpec [count];
174 return container.Module.Compiler.Report;
178 struct MissingInterfacesInfo {
179 public TypeSpec Type;
180 public bool Optional;
182 public MissingInterfacesInfo (TypeSpec t)
189 static readonly MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
191 static MissingInterfacesInfo [] GetMissingInterfaces (TypeDefinition container)
194 // Interfaces will return all interfaces that the container
195 // implements including any inherited interfaces
197 var impl = container.Definition.Interfaces;
199 if (impl == null || impl.Count == 0)
200 return EmptyMissingInterfacesInfo;
202 var ret = new MissingInterfacesInfo[impl.Count];
204 for (int i = 0; i < ret.Length; i++)
205 ret [i] = new MissingInterfacesInfo (impl [i]);
207 // we really should not get here because Object doesnt implement any
208 // interfaces. But it could implement something internal, so we have
209 // to handle that case.
210 if (container.BaseType == null)
213 var base_impls = container.BaseType.Interfaces;
214 if (base_impls != null) {
215 foreach (TypeSpec t in base_impls) {
216 for (int i = 0; i < ret.Length; i++) {
217 if (t == ret[i].Type) {
218 ret[i].Optional = true;
229 // Factory method: if there are pending implementation methods, we return a PendingImplementation
230 // object, otherwise we return null.
232 // Register method implementations are either abstract methods
233 // flagged as such on the base class or interface methods
235 static public PendingImplementation GetPendingImplementations (TypeDefinition container)
237 TypeSpec b = container.BaseType;
239 var missing_interfaces = GetMissingInterfaces (container);
242 // If we are implementing an abstract class, and we are not
243 // ourselves abstract, and there are abstract methods (C# allows
244 // abstract classes that have no abstract methods), then allocate
247 // We also pre-compute the methods.
249 bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0);
250 MethodSpec[] abstract_methods = null;
252 if (implementing_abstract){
253 var am = MemberCache.GetNotImplementedAbstractMethods (b);
256 implementing_abstract = false;
258 abstract_methods = new MethodSpec[am.Count];
259 am.CopyTo (abstract_methods, 0);
263 int total = missing_interfaces.Length + (implementing_abstract ? 1 : 0);
267 var pending = new PendingImplementation (container, missing_interfaces, abstract_methods, total);
270 // check for inherited conflicting methods
272 foreach (var p in pending.pending_implementations) {
274 // It can happen for generic interfaces only
276 if (!p.type.IsGeneric)
280 // CLR does not distinguishes between ref and out
282 for (int i = 0; i < p.methods.Count; ++i) {
283 MethodSpec compared_method = p.methods[i];
284 if (compared_method.Parameters.IsEmpty)
287 for (int ii = i + 1; ii < p.methods.Count; ++ii) {
288 MethodSpec tested_method = p.methods[ii];
289 if (compared_method.Name != tested_method.Name)
292 if (p.type != tested_method.DeclaringType)
295 if (!TypeSpecComparer.Override.IsSame (compared_method.Parameters.Types, tested_method.Parameters.Types))
298 bool exact_match = true;
299 bool ref_only_difference = false;
300 var cp = compared_method.Parameters.FixedParameters;
301 var tp = tested_method.Parameters.FixedParameters;
303 for (int pi = 0; pi < cp.Length; ++pi) {
305 // First check exact modifiers match
307 if ((cp[pi].ModFlags & Parameter.Modifier.RefOutMask) == (tp[pi].ModFlags & Parameter.Modifier.RefOutMask))
310 if (((cp[pi].ModFlags | tp[pi].ModFlags) & Parameter.Modifier.RefOutMask) == Parameter.Modifier.RefOutMask) {
311 ref_only_difference = true;
319 if (!exact_match || !ref_only_difference)
322 pending.Report.SymbolRelatedToPreviousError (compared_method);
323 pending.Report.SymbolRelatedToPreviousError (tested_method);
324 pending.Report.Error (767, container.Location,
325 "Cannot implement interface `{0}' with the specified type parameters because it causes method `{1}' to differ on parameter modifiers only",
326 p.type.GetDefinition().GetSignatureForError (), compared_method.GetSignatureForError ());
336 public enum Operation {
338 // If you change this, review the whole InterfaceMethod routine as there
339 // are a couple of assumptions on these three states
341 Lookup, ClearOne, ClearAll
345 /// Whether the specified method is an interface method implementation
347 public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method, out MethodSpec ambiguousCandidate, ref bool optional)
349 return InterfaceMethod (name, ifaceType, method, Operation.Lookup, out ambiguousCandidate, ref optional);
352 public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one, out MethodSpec ambiguousCandidate, ref bool optional)
354 InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll, out ambiguousCandidate, ref optional);
358 /// If a method in Type `t' (or null to look in all interfaces
359 /// and the base abstract class) with name `Name', return type `ret_type' and
360 /// arguments `args' implements an interface, this method will
361 /// return the MethodInfo that this method implements.
363 /// If `name' is null, we operate solely on the method's signature. This is for
364 /// instance used when implementing indexers.
366 /// The `Operation op' controls whether to lookup, clear the pending bit, or clear
367 /// all the methods with the given signature.
369 /// The `MethodInfo need_proxy' is used when we're implementing an interface's
370 /// indexer in a class. If the new indexer's IndexerName does not match the one
371 /// that was used in the interface, then we always need to create a proxy for it.
374 public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op, out MethodSpec ambiguousCandidate, ref bool optional)
376 ambiguousCandidate = null;
378 if (pending_implementations == null)
381 TypeSpec ret_type = method.method.ReturnType;
382 ParametersCompiled args = method.method.ParameterInfo;
383 bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod;
386 foreach (TypeAndMethods tm in pending_implementations){
387 if (!(iType == null || tm.type == iType))
390 int method_count = tm.methods.Count;
391 for (int i = 0; i < method_count; i++){
398 if (!m.IsAccessor || m.Parameters.IsEmpty)
401 if (name.Name != m.Name)
404 if (m.Arity != name.Arity)
408 if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args))
411 if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) {
412 tm.found[i] = method;
417 // `need_proxy' is not null when we're implementing an
418 // interface indexer and this is Clear(One/All) operation.
420 // If `name' is null, then we do a match solely based on the
421 // signature and not on the name (this is done in the Lookup
422 // for an interface indexer).
424 if (op != Operation.Lookup) {
425 if (m.IsAccessor != method.method.IsAccessor)
428 // If `t != null', then this is an explicitly interface
429 // implementation and we can always clear the method.
430 // `need_proxy' is not null if we're implementing an
431 // interface indexer. In this case, we need to create
432 // a proxy if the implementation's IndexerName doesn't
433 // match the IndexerName in the interface.
434 if (m.DeclaringType.IsInterface && iType == null && name.Name != m.Name) { // TODO: This is very expensive comparison
435 tm.need_proxy[i] = method.method.Spec;
437 tm.methods[i] = null;
440 tm.found [i] = method;
441 optional = tm.optional;
444 if (op == Operation.Lookup && name.ExplicitInterface != null && ambiguousCandidate == null) {
445 ambiguousCandidate = m;
450 // Lookups and ClearOne return
452 if (op != Operation.ClearAll)
456 // If a specific type was requested, we can stop now.
457 if (tm.type == iType)
461 m = ambiguousCandidate;
462 ambiguousCandidate = null;
467 /// C# allows this kind of scenarios:
468 /// interface I { void M (); }
469 /// class X { public void M (); }
470 /// class Y : X, I { }
472 /// For that case, we create an explicit implementation function
475 void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method)
477 // TODO: Handle nested iface names
479 var ns = iface.MemberDefinition.Namespace;
480 if (string.IsNullOrEmpty (ns))
481 proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name;
483 proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name;
485 var param = iface_method.Parameters;
487 MethodBuilder proxy = container.TypeBuilder.DefineMethod (
489 MethodAttributes.Private |
490 MethodAttributes.HideBySig |
491 MethodAttributes.NewSlot |
492 MethodAttributes.CheckAccessOnOverride |
493 MethodAttributes.Virtual | MethodAttributes.Final,
494 CallingConventions.Standard | CallingConventions.HasThis,
495 base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
497 if (iface_method.IsGeneric) {
498 var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();
499 proxy.DefineGenericParameters (gnames);
502 for (int i = 0; i < param.Count; i++) {
503 string name = param.FixedParameters [i].Name;
504 ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags);
505 proxy.DefineParameter (i + 1, attr, name);
508 int top = param.Count;
509 var ec = new EmitContext (new ProxyMethodContext (container), proxy.GetILGenerator (), null, null);
511 // TODO: GetAllParametersArguments
512 for (int i = 0; i < top; i++)
513 ec.EmitArgumentLoad (i);
515 ec.Emit (OpCodes.Call, base_method);
516 ec.Emit (OpCodes.Ret);
518 container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ());
522 /// This function tells whether one of our base classes implements
523 /// the given method (which turns out, it is valid to have an interface
524 /// implementation in a base
526 bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
529 bool base_can_implement = true;
530 TypeSpec lookup_type;
533 // Special handling for properties/indexers which cannot have accessors
534 // implementing an interface found in different types (e.g. current and base)
536 if (mi.IsAccessor && container.Interfaces != null) {
538 bool new_implementation = false;
539 foreach (var iface in container.Interfaces) {
540 if (TypeSpecComparer.IsEqual (iface, iface_type)) {
541 new_implementation = true;
546 if (new_implementation) {
548 bool getter = mi.Name [0] == 'g';
549 if (mi.Parameters.Count > (getter ? 0 : 1)) {
550 var indexer_params = getter ? mi.Parameters : IndexerSpec.CreateParametersFromSetter (mi, mi.Parameters.Count - 1);
551 var ptype = getter ? mi.ReturnType : mi.Parameters.Types [mi.Parameters.Count - 1];
552 filter = new MemberFilter (MemberCache.IndexerNameAlias, 0, MemberKind.Indexer, indexer_params, ptype);
554 var pname = mi.Name.Substring (4);
555 var ptype = getter ? mi.ReturnType : mi.Parameters.Types [0];
556 filter = MemberFilter.Property (pname, ptype);
559 var prop = MemberCache.FindMember (container.CurrentType, filter, BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly);
560 if (prop != null && (prop.Modifiers & Modifiers.NEW) != 0)
561 base_can_implement = false;
565 if (base_can_implement) {
566 lookup_type = container.BaseType;
568 if (lookup_type.ImplementsInterface (iface_type, false))
571 lookup_type = container.CurrentType;
575 // Setup filter with no return type to give better error message
576 // about mismatch at return type when the check bellow rejects them
578 var parameters = mi.Parameters;
579 MethodSpec close_match = null;
582 var candidates = MemberCache.FindMembers (lookup_type, mi.Name, !base_can_implement);
583 if (candidates == null) {
584 base_method = close_match;
588 MethodSpec similar_candidate = null;
589 foreach (var candidate in candidates) {
590 if (candidate.Kind != MemberKind.Method)
593 if (candidate.Arity != mi.Arity)
596 var candidate_param = ((MethodSpec) candidate).Parameters;
597 if (!TypeSpecComparer.Override.IsEqual (parameters.Types, candidate_param.Types))
600 bool modifiers_match = true;
601 for (int i = 0; i < parameters.Count; ++i) {
603 // First check exact ref/out match
605 if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) == (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask))
608 modifiers_match = false;
611 // Different in ref/out only
613 if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) {
614 if (similar_candidate == null) {
615 if (!candidate.IsPublic)
618 if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, ((MethodSpec) candidate).ReturnType))
621 // It's used for ref/out ambiguity overload check
622 similar_candidate = (MethodSpec) candidate;
628 similar_candidate = null;
632 if (!modifiers_match)
636 // From this point the candidate is used for detailed error reporting
637 // because it's very close match to what we are looking for
639 var m = (MethodSpec) candidate;
642 if (close_match == null)
648 if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, m.ReturnType)) {
649 if (close_match == null)
657 if (mi.IsGeneric && !Method.CheckImplementingMethodConstraints (container, m, mi)) {
662 if (base_method != null) {
663 if (similar_candidate != null) {
664 Report.SymbolRelatedToPreviousError (similar_candidate);
665 Report.SymbolRelatedToPreviousError (mi);
666 Report.SymbolRelatedToPreviousError (container);
667 Report.Warning (1956, 1, ((MemberCore) base_method.MemberDefinition).Location,
668 "The interface method `{0}' implementation is ambiguous between following methods: `{1}' and `{2}' in type `{3}'",
669 mi.GetSignatureForError (), base_method.GetSignatureForError (), similar_candidate.GetSignatureForError (), container.GetSignatureForError ());
675 if (!base_can_implement)
678 lookup_type = candidates[0].DeclaringType.BaseType;
679 if (lookup_type == null) {
680 base_method = close_match;
685 if (!base_method.IsVirtual) {
687 var base_builder = base_method.GetMetaInfo () as MethodBuilder;
688 if (base_builder != null) {
690 // We can avoid creating a proxy if base_method can be marked 'final virtual'. This can
691 // be done for all methods from compiled assembly
693 base_builder.__SetAttributes (base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot);
697 DefineProxy (iface_type, base_method, mi);
704 /// Verifies that any pending abstract methods or interface methods
705 /// were implemented.
707 public bool VerifyPendingMethods ()
709 int top = pending_implementations.Length;
713 for (i = 0; i < top; i++){
714 TypeSpec type = pending_implementations [i].type;
716 for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
717 var mi = pending_implementations[i].methods[j];
721 if (type.IsInterface){
723 pending_implementations [i].need_proxy [j];
725 if (need_proxy != null) {
726 DefineProxy (type, need_proxy, mi);
730 MethodSpec candidate;
731 if (BaseImplements (type, mi, out candidate))
734 if (candidate == null) {
735 MethodData md = pending_implementations [i].found [j];
737 candidate = md.method.Spec;
740 Report.SymbolRelatedToPreviousError (mi);
741 if (candidate != null) {
742 Report.SymbolRelatedToPreviousError (candidate);
743 if (candidate.IsStatic) {
744 Report.Error (736, container.Location,
745 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static",
746 container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
747 } else if ((candidate.Modifiers & Modifiers.PUBLIC) == 0) {
748 Report.Error (737, container.Location,
749 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is not public",
750 container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
751 } else if (mi.ReturnType.Kind == MemberKind.ByRef) {
752 Report.Error (8152, container.Location,
753 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not return by reference",
754 container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError (),
755 candidate.ReturnType.GetSignatureForError ());
757 Report.Error (738, container.Location,
758 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
759 container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError (),
760 candidate.ReturnType.GetSignatureForError (), mi.ReturnType.GetSignatureForError ());
763 Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
764 container.GetSignatureForError (), mi.GetSignatureForError ());
767 Report.SymbolRelatedToPreviousError (mi);
768 Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
769 container.GetSignatureForError (), mi.GetSignatureForError ());