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 (TypeSpec extensionType, 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].methods = new MethodSpec [count];
151 pending_implementations [i].need_proxy = new MethodSpec [count];
153 pending_implementations [i].methods = abstract_methods;
154 pending_implementations [i].found = new MethodData [count];
155 pending_implementations [i].type = type_builder;
159 foreach (MissingInterfacesInfo missing in missing_ifaces) {
160 var iface = missing.Type;
161 var mi = MemberCache.GetInterfaceMethods (iface);
163 int count = mi.Count;
164 pending_implementations [i].type = iface;
165 pending_implementations [i].optional = missing.Optional;
166 pending_implementations [i].methods = mi;
167 pending_implementations [i].found = new MethodData [count];
168 pending_implementations [i].need_proxy = new MethodSpec [count];
175 return container.Module.Compiler.Report;
179 struct MissingInterfacesInfo {
180 public TypeSpec Type;
181 public bool Optional;
183 public MissingInterfacesInfo (TypeSpec t)
190 static readonly MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
192 static MissingInterfacesInfo [] GetMissingInterfaces (TypeDefinition container)
195 // Notice that Interfaces will only return the interfaces that the Type
196 // is supposed to implement, not all the interfaces that the type implements.
198 var impl = container.Definition.Interfaces;
200 if (impl == null || impl.Count == 0)
201 return EmptyMissingInterfacesInfo;
203 MissingInterfacesInfo[] ret = new MissingInterfacesInfo[impl.Count];
205 for (int i = 0; i < impl.Count; i++)
206 ret [i] = new MissingInterfacesInfo (impl [i]);
208 // we really should not get here because Object doesnt implement any
209 // interfaces. But it could implement something internal, so we have
210 // to handle that case.
211 if (container.BaseType == null)
214 var base_impls = container.BaseType.Interfaces;
215 if (base_impls != null) {
216 foreach (TypeSpec t in base_impls) {
217 for (int i = 0; i < ret.Length; i++) {
218 if (t == ret[i].Type) {
219 ret[i].Optional = true;
230 // Factory method: if there are pending implementation methods, we return a PendingImplementation
231 // object, otherwise we return null.
233 // Register method implementations are either abstract methods
234 // flagged as such on the base class or interface methods
236 static public PendingImplementation GetPendingImplementations (TypeDefinition container)
238 TypeSpec b = container.BaseType;
240 var missing_interfaces = GetMissingInterfaces (container);
243 // If we are implementing an abstract class, and we are not
244 // ourselves abstract, and there are abstract methods (C# allows
245 // abstract classes that have no abstract methods), then allocate
248 // We also pre-compute the methods.
250 bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0);
251 MethodSpec[] abstract_methods = null;
253 if (implementing_abstract){
254 var am = MemberCache.GetNotImplementedAbstractMethods (b);
257 implementing_abstract = false;
259 abstract_methods = new MethodSpec[am.Count];
260 am.CopyTo (abstract_methods, 0);
264 int total = missing_interfaces.Length + (implementing_abstract ? 1 : 0);
268 var pending = new PendingImplementation (container, missing_interfaces, abstract_methods, total);
271 // check for inherited conflicting methods
273 foreach (var p in pending.pending_implementations) {
275 // It can happen for generic interfaces only
277 if (!p.type.IsGeneric)
281 // CLR does not distinguishes between ref and out
283 for (int i = 0; i < p.methods.Count; ++i) {
284 MethodSpec compared_method = p.methods[i];
285 if (compared_method.Parameters.IsEmpty)
288 for (int ii = i + 1; ii < p.methods.Count; ++ii) {
289 MethodSpec tested_method = p.methods[ii];
290 if (compared_method.Name != tested_method.Name)
293 if (p.type != tested_method.DeclaringType)
296 if (!TypeSpecComparer.Override.IsSame (compared_method.Parameters.Types, tested_method.Parameters.Types))
299 bool exact_match = true;
300 bool ref_only_difference = false;
301 var cp = compared_method.Parameters.FixedParameters;
302 var tp = tested_method.Parameters.FixedParameters;
304 for (int pi = 0; pi < cp.Length; ++pi) {
306 // First check exact modifiers match
308 const Parameter.Modifier ref_out = Parameter.Modifier.REF | Parameter.Modifier.OUT;
309 if ((cp[pi].ModFlags & ref_out) == (tp[pi].ModFlags & ref_out))
312 if ((cp[pi].ModFlags & tp[pi].ModFlags & Parameter.Modifier.ISBYREF) != 0) {
313 ref_only_difference = true;
321 if (!exact_match || !ref_only_difference)
324 pending.Report.SymbolRelatedToPreviousError (compared_method);
325 pending.Report.SymbolRelatedToPreviousError (tested_method);
326 pending.Report.Error (767, container.Location,
327 "Cannot implement interface `{0}' with the specified type parameters because it causes method `{1}' to differ on parameter modifiers only",
328 p.type.GetDefinition().GetSignatureForError (), compared_method.GetSignatureForError ());
338 public enum Operation {
340 // If you change this, review the whole InterfaceMethod routine as there
341 // are a couple of assumptions on these three states
343 Lookup, ClearOne, ClearAll
347 /// Whether the specified method is an interface method implementation
349 public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method, out MethodSpec ambiguousCandidate, ref bool optional)
351 return InterfaceMethod (name, ifaceType, method, Operation.Lookup, out ambiguousCandidate, ref optional);
354 public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one, out MethodSpec ambiguousCandidate, ref bool optional)
356 InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll, out ambiguousCandidate, ref optional);
360 /// If a method in Type `t' (or null to look in all interfaces
361 /// and the base abstract class) with name `Name', return type `ret_type' and
362 /// arguments `args' implements an interface, this method will
363 /// return the MethodInfo that this method implements.
365 /// If `name' is null, we operate solely on the method's signature. This is for
366 /// instance used when implementing indexers.
368 /// The `Operation op' controls whether to lookup, clear the pending bit, or clear
369 /// all the methods with the given signature.
371 /// The `MethodInfo need_proxy' is used when we're implementing an interface's
372 /// indexer in a class. If the new indexer's IndexerName does not match the one
373 /// that was used in the interface, then we always need to create a proxy for it.
376 public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op, out MethodSpec ambiguousCandidate, ref bool optional)
378 ambiguousCandidate = null;
380 if (pending_implementations == null)
383 TypeSpec ret_type = method.method.ReturnType;
384 ParametersCompiled args = method.method.ParameterInfo;
385 bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod;
388 foreach (TypeAndMethods tm in pending_implementations){
389 if (!(iType == null || tm.type == iType))
392 int method_count = tm.methods.Count;
393 for (int i = 0; i < method_count; i++){
400 if (!m.IsAccessor || m.Parameters.IsEmpty)
403 if (name.Name != m.Name)
406 if (m.Arity != name.Arity)
410 if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args))
413 if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) {
414 tm.found[i] = method;
419 // `need_proxy' is not null when we're implementing an
420 // interface indexer and this is Clear(One/All) operation.
422 // If `name' is null, then we do a match solely based on the
423 // signature and not on the name (this is done in the Lookup
424 // for an interface indexer).
426 if (op != Operation.Lookup) {
427 if (m.IsAccessor != method.method.IsAccessor)
430 // If `t != null', then this is an explicitly interface
431 // implementation and we can always clear the method.
432 // `need_proxy' is not null if we're implementing an
433 // interface indexer. In this case, we need to create
434 // a proxy if the implementation's IndexerName doesn't
435 // match the IndexerName in the interface.
436 if (m.DeclaringType.IsInterface && iType == null && name.Name != m.Name) { // TODO: This is very expensive comparison
437 tm.need_proxy[i] = method.method.Spec;
439 tm.methods[i] = null;
442 tm.found [i] = method;
443 optional = tm.optional;
446 if (op == Operation.Lookup && name.ExplicitInterface != null && ambiguousCandidate == null) {
447 ambiguousCandidate = m;
452 // Lookups and ClearOne return
454 if (op != Operation.ClearAll)
458 // If a specific type was requested, we can stop now.
459 if (tm.type == iType)
463 m = ambiguousCandidate;
464 ambiguousCandidate = null;
469 /// C# allows this kind of scenarios:
470 /// interface I { void M (); }
471 /// class X { public void M (); }
472 /// class Y : X, I { }
474 /// For that case, we create an explicit implementation function
477 void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method)
479 // TODO: Handle nested iface names
481 var ns = iface.MemberDefinition.Namespace;
482 if (string.IsNullOrEmpty (ns))
483 proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name;
485 proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name;
487 var param = iface_method.Parameters;
489 MethodBuilder proxy = container.TypeBuilder.DefineMethod (
491 MethodAttributes.Private |
492 MethodAttributes.HideBySig |
493 MethodAttributes.NewSlot |
494 MethodAttributes.CheckAccessOnOverride |
495 MethodAttributes.Virtual | MethodAttributes.Final,
496 CallingConventions.Standard | CallingConventions.HasThis,
497 base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
499 if (iface_method.IsGeneric) {
500 var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();
501 proxy.DefineGenericParameters (gnames);
504 for (int i = 0; i < param.Count; i++) {
505 string name = param.FixedParameters [i].Name;
506 ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags);
507 proxy.DefineParameter (i + 1, attr, name);
510 int top = param.Count;
511 var ec = new EmitContext (new ProxyMethodContext (container), proxy.GetILGenerator (), null);
513 // TODO: GetAllParametersArguments
514 for (int i = 0; i < top; i++)
515 ec.EmitArgumentLoad (i);
517 ec.Emit (OpCodes.Call, base_method);
518 ec.Emit (OpCodes.Ret);
520 container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ());
524 /// This function tells whether one of our base classes implements
525 /// the given method (which turns out, it is valid to have an interface
526 /// implementation in a base
528 bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
531 var base_type = container.BaseType;
534 // Setup filter with no return type to give better error message
535 // about mismatch at return type when the check bellow rejects them
537 var parameters = mi.Parameters;
539 var candidates = MemberCache.FindMembers (base_type, mi.Name, false);
540 if (candidates == null)
543 MethodSpec similar_candidate = null;
544 foreach (var candidate in candidates) {
545 if (candidate.Kind != MemberKind.Method)
548 if (candidate.Arity != mi.Arity)
551 var candidate_param = ((MethodSpec) candidate).Parameters;
552 if (!TypeSpecComparer.Override.IsSame (parameters.Types, candidate_param.Types))
555 bool modifiers_match = true;
556 for (int i = 0; i < parameters.Count; ++i) {
558 // First check exact ref/out match
560 const Parameter.Modifier ref_out = Parameter.Modifier.REF | Parameter.Modifier.OUT;
561 if ((parameters.FixedParameters[i].ModFlags & ref_out) == (candidate_param.FixedParameters[i].ModFlags & ref_out))
564 modifiers_match = false;
567 // Different in ref/out only
569 if ((parameters.FixedParameters[i].ModFlags & candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.ISBYREF) != 0) {
570 if (similar_candidate == null) {
571 if (!candidate.IsPublic)
574 if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, ((MethodSpec) candidate).ReturnType))
577 // It's used for ref/out ambiguity overload check
578 similar_candidate = (MethodSpec) candidate;
584 similar_candidate = null;
588 if (!modifiers_match)
592 // From this point on the candidate is used for detailed error reporting
593 // because it's very close match to what we are looking for
595 base_method = (MethodSpec) candidate;
597 if (!candidate.IsPublic)
600 if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, base_method.ReturnType))
603 if (mi.IsGeneric && !Method.CheckImplementingMethodConstraints (container, base_method, mi)) {
608 if (base_method != null) {
609 if (similar_candidate != null) {
610 Report.SymbolRelatedToPreviousError (similar_candidate);
611 Report.SymbolRelatedToPreviousError (mi);
612 Report.SymbolRelatedToPreviousError (container);
613 Report.Warning (1956, 1, ((MemberCore) base_method.MemberDefinition).Location,
614 "The interface method `{0}' implementation is ambiguous between following methods: `{1}' and `{2}' in type `{3}'",
615 mi.GetSignatureForError (), base_method.GetSignatureForError (), similar_candidate.GetSignatureForError (), container.GetSignatureForError ());
621 base_type = candidates[0].DeclaringType.BaseType;
622 if (base_type == null)
626 if (!base_method.IsVirtual) {
628 var base_builder = base_method.GetMetaInfo () as MethodBuilder;
629 if (base_builder != null) {
631 // We can avoid creating a proxy if base_method can be marked 'final virtual'. This can
632 // be done for all methods from compiled assembly
634 base_builder.__SetAttributes (base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot);
638 DefineProxy (iface_type, base_method, mi);
645 /// Verifies that any pending abstract methods or interface methods
646 /// were implemented.
648 public bool VerifyPendingMethods ()
650 int top = pending_implementations.Length;
654 for (i = 0; i < top; i++){
655 TypeSpec type = pending_implementations [i].type;
657 bool base_implements_type = type.IsInterface &&
658 container.BaseType != null &&
659 container.BaseType.ImplementsInterface (type, false);
661 for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
662 var mi = pending_implementations[i].methods[j];
666 if (type.IsInterface){
668 pending_implementations [i].need_proxy [j];
670 if (need_proxy != null) {
671 DefineProxy (type, need_proxy, mi);
675 if (pending_implementations [i].optional)
678 MethodSpec candidate = null;
679 if (base_implements_type || BaseImplements (type, mi, out candidate))
682 if (candidate == null) {
683 MethodData md = pending_implementations [i].found [j];
685 candidate = md.method.Spec;
688 Report.SymbolRelatedToPreviousError (mi);
689 if (candidate != null) {
690 Report.SymbolRelatedToPreviousError (candidate);
691 if (candidate.IsStatic) {
692 Report.Error (736, container.Location,
693 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static",
694 container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
695 } else if ((candidate.Modifiers & Modifiers.PUBLIC) == 0) {
696 Report.Error (737, container.Location,
697 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' in not public",
698 container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
700 Report.Error (738, container.Location,
701 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
702 container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError (),
703 candidate.ReturnType.GetSignatureForError (), mi.ReturnType.GetSignatureForError ());
706 Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
707 container.GetSignatureForError (), mi.GetSignatureForError ());
710 Report.SymbolRelatedToPreviousError (mi);
711 Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
712 container.GetSignatureForError (), mi.GetSignatureForError ());