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.
14 using System.Collections.Generic;
18 using IKVM.Reflection;
19 using IKVM.Reflection.Emit;
21 using System.Reflection;
22 using System.Reflection.Emit;
25 namespace Mono.CSharp {
27 struct TypeAndMethods {
29 public IList<MethodSpec> methods;
32 // Whether it is optional, this is used to allow the explicit/implicit
33 // implementation when a base class already implements an interface.
37 // class X : IA { } class Y : X, IA { IA.Explicit (); }
42 // This flag on the method says `We found a match, but
43 // because it was private, we could not use the match
45 public MethodData [] found;
47 // If a method is defined here, then we always need to
48 // create a proxy for it. This is used when implementing
49 // an interface's indexer with a different IndexerName.
50 public MethodSpec [] need_proxy;
53 public class PendingImplementation
56 /// The container for this PendingImplementation
58 readonly TypeContainer container;
61 /// This is the array of TypeAndMethods that describes the pending implementations
62 /// (both interfaces and abstract methods in base class)
64 TypeAndMethods [] pending_implementations;
66 PendingImplementation (TypeContainer container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total)
68 var type_builder = container.Definition;
70 this.container = container;
71 pending_implementations = new TypeAndMethods [total];
74 if (abstract_methods != null) {
75 int count = abstract_methods.Length;
76 pending_implementations [i].methods = new MethodSpec [count];
77 pending_implementations [i].need_proxy = new MethodSpec [count];
79 pending_implementations [i].methods = abstract_methods;
80 pending_implementations [i].found = new MethodData [count];
81 pending_implementations [i].type = type_builder;
85 foreach (MissingInterfacesInfo missing in missing_ifaces) {
86 var iface = missing.Type;
87 var mi = MemberCache.GetInterfaceMethods (iface);
90 pending_implementations [i].type = iface;
91 pending_implementations [i].optional = missing.Optional;
92 pending_implementations [i].methods = mi;
93 pending_implementations [i].found = new MethodData [count];
94 pending_implementations [i].need_proxy = new MethodSpec [count];
101 return container.Module.Compiler.Report;
105 struct MissingInterfacesInfo {
106 public TypeSpec Type;
107 public bool Optional;
109 public MissingInterfacesInfo (TypeSpec t)
116 static readonly MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
118 static MissingInterfacesInfo [] GetMissingInterfaces (TypeContainer container)
121 // Notice that Interfaces will only return the interfaces that the Type
122 // is supposed to implement, not all the interfaces that the type implements.
124 var impl = container.Definition.Interfaces;
126 if (impl == null || impl.Count == 0)
127 return EmptyMissingInterfacesInfo;
129 MissingInterfacesInfo[] ret = new MissingInterfacesInfo[impl.Count];
131 for (int i = 0; i < impl.Count; i++)
132 ret [i] = new MissingInterfacesInfo (impl [i]);
134 // we really should not get here because Object doesnt implement any
135 // interfaces. But it could implement something internal, so we have
136 // to handle that case.
137 if (container.BaseType == null)
140 var base_impls = container.BaseType.Interfaces;
141 if (base_impls != null) {
142 foreach (TypeSpec t in base_impls) {
143 for (int i = 0; i < ret.Length; i++) {
144 if (t == ret[i].Type) {
145 ret[i].Optional = true;
156 // Factory method: if there are pending implementation methods, we return a PendingImplementation
157 // object, otherwise we return null.
159 // Register method implementations are either abstract methods
160 // flagged as such on the base class or interface methods
162 static public PendingImplementation GetPendingImplementations (TypeContainer container)
164 TypeSpec b = container.BaseType;
166 var missing_interfaces = GetMissingInterfaces (container);
169 // If we are implementing an abstract class, and we are not
170 // ourselves abstract, and there are abstract methods (C# allows
171 // abstract classes that have no abstract methods), then allocate
174 // We also pre-compute the methods.
176 bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0);
177 MethodSpec[] abstract_methods = null;
179 if (implementing_abstract){
180 var am = MemberCache.GetNotImplementedAbstractMethods (b);
183 implementing_abstract = false;
185 abstract_methods = new MethodSpec[am.Count];
186 am.CopyTo (abstract_methods, 0);
190 int total = missing_interfaces.Length + (implementing_abstract ? 1 : 0);
194 var pending = new PendingImplementation (container, missing_interfaces, abstract_methods, total);
197 // check for inherited conflicting methods
199 foreach (var p in pending.pending_implementations) {
201 // It can happen for generic interfaces only
203 if (!p.type.IsGeneric)
207 // CLR does not distinguishes between ref and out
209 for (int i = 0; i < p.methods.Count; ++i) {
210 MethodSpec compared_method = p.methods[i];
211 if (compared_method.Parameters.IsEmpty)
214 for (int ii = i + 1; ii < p.methods.Count; ++ii) {
215 MethodSpec tested_method = p.methods[ii];
216 if (compared_method.Name != tested_method.Name)
219 if (p.type != tested_method.DeclaringType)
222 if (!TypeSpecComparer.Override.IsSame (compared_method.Parameters.Types, tested_method.Parameters.Types))
225 bool exact_match = true;
226 bool ref_only_difference = false;
227 var cp = compared_method.Parameters.FixedParameters;
228 var tp = tested_method.Parameters.FixedParameters;
229 for (int pi = 0; pi < compared_method.Parameters.Count; ++pi) {
231 // First check exact modifiers match
233 const Parameter.Modifier ref_out = Parameter.Modifier.REF | Parameter.Modifier.OUT;
234 if ((cp[i].ModFlags & ref_out) == (tp[i].ModFlags & ref_out))
237 if ((cp[i].ModFlags & tp[i].ModFlags & Parameter.Modifier.ISBYREF) != 0) {
238 ref_only_difference = true;
246 if (!exact_match || !ref_only_difference)
249 pending.Report.SymbolRelatedToPreviousError (compared_method);
250 pending.Report.SymbolRelatedToPreviousError (tested_method);
251 pending.Report.Error (767, container.Location,
252 "Cannot implement interface `{0}' with the specified type parameters because it causes method `{1}' to differ on parameter modifiers only",
253 p.type.GetDefinition().GetSignatureForError (), compared_method.GetSignatureForError ());
263 public enum Operation {
265 // If you change this, review the whole InterfaceMethod routine as there
266 // are a couple of assumptions on these three states
268 Lookup, ClearOne, ClearAll
272 /// Whether the specified method is an interface method implementation
274 public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method, out MethodSpec ambiguousCandidate)
276 return InterfaceMethod (name, ifaceType, method, Operation.Lookup, out ambiguousCandidate);
279 public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one, out MethodSpec ambiguousCandidate)
281 InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll, out ambiguousCandidate);
285 /// If a method in Type `t' (or null to look in all interfaces
286 /// and the base abstract class) with name `Name', return type `ret_type' and
287 /// arguments `args' implements an interface, this method will
288 /// return the MethodInfo that this method implements.
290 /// If `name' is null, we operate solely on the method's signature. This is for
291 /// instance used when implementing indexers.
293 /// The `Operation op' controls whether to lookup, clear the pending bit, or clear
294 /// all the methods with the given signature.
296 /// The `MethodInfo need_proxy' is used when we're implementing an interface's
297 /// indexer in a class. If the new indexer's IndexerName does not match the one
298 /// that was used in the interface, then we always need to create a proxy for it.
301 public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op, out MethodSpec ambiguousCandidate)
303 ambiguousCandidate = null;
305 if (pending_implementations == null)
308 TypeSpec ret_type = method.method.ReturnType;
309 ParametersCompiled args = method.method.ParameterInfo;
310 bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod;
313 foreach (TypeAndMethods tm in pending_implementations){
314 if (!(iType == null || tm.type == iType))
317 int method_count = tm.methods.Count;
318 for (int i = 0; i < method_count; i++){
325 if (!m.IsAccessor || m.Parameters.IsEmpty)
328 if (name.Name != m.Name)
331 if (m.Arity != name.Arity)
335 if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args))
338 if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) {
339 tm.found[i] = method;
344 // `need_proxy' is not null when we're implementing an
345 // interface indexer and this is Clear(One/All) operation.
347 // If `name' is null, then we do a match solely based on the
348 // signature and not on the name (this is done in the Lookup
349 // for an interface indexer).
351 if (op != Operation.Lookup) {
352 if (m.IsAccessor != method.method.IsAccessor)
355 // If `t != null', then this is an explicitly interface
356 // implementation and we can always clear the method.
357 // `need_proxy' is not null if we're implementing an
358 // interface indexer. In this case, we need to create
359 // a proxy if the implementation's IndexerName doesn't
360 // match the IndexerName in the interface.
361 if (m.DeclaringType.IsInterface && iType == null && name.Name != m.Name) { // TODO: This is very expensive comparison
362 tm.need_proxy[i] = method.method.Spec;
364 tm.methods[i] = null;
367 tm.found [i] = method;
370 if (op == Operation.Lookup && name.Left != null && ambiguousCandidate == null) {
371 ambiguousCandidate = m;
376 // Lookups and ClearOne return
378 if (op != Operation.ClearAll)
382 // If a specific type was requested, we can stop now.
383 if (tm.type == iType)
387 m = ambiguousCandidate;
388 ambiguousCandidate = null;
393 /// C# allows this kind of scenarios:
394 /// interface I { void M (); }
395 /// class X { public void M (); }
396 /// class Y : X, I { }
398 /// For that case, we create an explicit implementation function
401 void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method)
403 // TODO: Handle nested iface names
405 var ns = iface.MemberDefinition.Namespace;
406 if (string.IsNullOrEmpty (ns))
407 proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name;
409 proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name;
411 var param = iface_method.Parameters;
413 MethodBuilder proxy = container.TypeBuilder.DefineMethod (
415 MethodAttributes.Private |
416 MethodAttributes.HideBySig |
417 MethodAttributes.NewSlot |
418 MethodAttributes.CheckAccessOnOverride |
419 MethodAttributes.Virtual | MethodAttributes.Final,
420 CallingConventions.Standard | CallingConventions.HasThis,
421 base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
423 if (iface_method.IsGeneric) {
424 var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();
425 proxy.DefineGenericParameters (gnames);
428 for (int i = 0; i < param.Count; i++) {
429 string name = param.FixedParameters [i].Name;
430 ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags);
431 proxy.DefineParameter (i + 1, attr, name);
434 int top = param.Count;
435 var ec = new EmitContext (null, proxy.GetILGenerator (), null);
436 // TODO: GetAllParametersArguments
437 for (int i = 0; i <= top; i++)
438 ParameterReference.EmitLdArg (ec, i);
440 ec.Emit (OpCodes.Call, base_method);
441 ec.Emit (OpCodes.Ret);
443 container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ());
447 /// This function tells whether one of our base classes implements
448 /// the given method (which turns out, it is valid to have an interface
449 /// implementation in a base
451 bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
454 var base_type = container.BaseType;
457 // Setup filter with no return type to give better error message
458 // about mismatch at return type when the check bellow rejects them
460 var parameters = mi.Parameters;
462 var candidates = MemberCache.FindMembers (base_type, mi.Name, false);
463 if (candidates == null)
466 MethodSpec similar_candidate = null;
467 foreach (var candidate in candidates) {
468 if (candidate.Kind != MemberKind.Method)
471 if (candidate.Arity != mi.Arity)
474 var candidate_param = ((MethodSpec) candidate).Parameters;
475 if (!TypeSpecComparer.Override.IsSame (parameters.Types, candidate_param.Types))
478 bool modifiers_match = true;
479 for (int i = 0; i < parameters.Count; ++i) {
481 // First check exact ref/out match
483 const Parameter.Modifier ref_out = Parameter.Modifier.REF | Parameter.Modifier.OUT;
484 if ((parameters.FixedParameters[i].ModFlags & ref_out) == (candidate_param.FixedParameters[i].ModFlags & ref_out))
487 modifiers_match = false;
490 // Different in ref/out only
492 if ((parameters.FixedParameters[i].ModFlags & candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.ISBYREF) != 0) {
493 if (similar_candidate == null) {
494 if (!candidate.IsPublic)
497 if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, ((MethodSpec) candidate).ReturnType))
500 // It's used for ref/out ambiguity overload check
501 similar_candidate = (MethodSpec) candidate;
507 similar_candidate = null;
511 if (!modifiers_match)
515 // From this point on the candidate is used for detailed error reporting
516 // because it's very close match to what we are looking for
518 base_method = (MethodSpec) candidate;
520 if (!candidate.IsPublic)
523 if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, base_method.ReturnType))
527 if (base_method != null) {
528 if (similar_candidate != null) {
529 Report.SymbolRelatedToPreviousError (similar_candidate);
530 Report.SymbolRelatedToPreviousError (mi);
531 Report.SymbolRelatedToPreviousError (container);
532 Report.Warning (1956, 1, ((MemberCore) base_method.MemberDefinition).Location,
533 "The interface method `{0}' implementation is ambiguous between following methods: `{1}' and `{2}' in type `{3}'",
534 mi.GetSignatureForError (), base_method.GetSignatureForError (), similar_candidate.GetSignatureForError (), container.GetSignatureForError ());
540 base_type = candidates[0].DeclaringType.BaseType;
541 if (base_type == null)
545 if (!base_method.IsVirtual) {
547 var base_builder = base_method.GetMetaInfo () as MethodBuilder;
548 if (base_builder != null) {
550 // We can avoid creating a proxy if base_method can be marked 'final virtual'. This can
551 // be done for all methods from compiled assembly
553 base_builder.__SetAttributes (base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot);
557 DefineProxy (iface_type, base_method, mi);
564 /// Verifies that any pending abstract methods or interface methods
565 /// were implemented.
567 public bool VerifyPendingMethods ()
569 int top = pending_implementations.Length;
573 for (i = 0; i < top; i++){
574 TypeSpec type = pending_implementations [i].type;
576 bool base_implements_type = type.IsInterface &&
577 container.BaseType != null &&
578 container.BaseType.ImplementsInterface (type, false);
580 for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
581 var mi = pending_implementations[i].methods[j];
585 if (type.IsInterface){
587 pending_implementations [i].need_proxy [j];
589 if (need_proxy != null) {
590 DefineProxy (type, need_proxy, mi);
594 if (pending_implementations [i].optional)
597 MethodSpec candidate = null;
598 if (base_implements_type || BaseImplements (type, mi, out candidate))
601 if (candidate == null) {
602 MethodData md = pending_implementations [i].found [j];
604 candidate = md.method.Spec;
607 Report.SymbolRelatedToPreviousError (mi);
608 if (candidate != null) {
609 Report.SymbolRelatedToPreviousError (candidate);
610 if (candidate.IsStatic) {
611 Report.Error (736, container.Location,
612 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static",
613 container.GetSignatureForError (), mi.GetSignatureForError (), TypeManager.CSharpSignature (candidate));
614 } else if ((candidate.Modifiers & Modifiers.PUBLIC) == 0) {
615 Report.Error (737, container.Location,
616 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' in not public",
617 container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
619 Report.Error (738, container.Location,
620 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
621 container.GetSignatureForError (), mi.GetSignatureForError (), TypeManager.CSharpSignature (candidate),
622 TypeManager.CSharpName (candidate.ReturnType), TypeManager.CSharpName (mi.ReturnType));
625 Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
626 container.GetSignatureForError (), mi.GetSignatureForError ());
629 Report.SymbolRelatedToPreviousError (mi);
630 Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
631 container.GetSignatureForError (), mi.GetSignatureForError ());