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.
15 using System.Collections.Generic;
19 using IKVM.Reflection;
20 using IKVM.Reflection.Emit;
22 using System.Reflection;
23 using System.Reflection.Emit;
26 namespace Mono.CSharp {
28 struct TypeAndMethods {
30 public IList<MethodSpec> methods;
33 // Whether it is optional, this is used to allow the explicit/implicit
34 // implementation when a base class already implements an interface.
38 // class X : IA { } class Y : X, IA { IA.Explicit (); }
43 // This flag on the method says `We found a match, but
44 // because it was private, we could not use the match
46 public MethodData [] found;
48 // If a method is defined here, then we always need to
49 // create a proxy for it. This is used when implementing
50 // an interface's indexer with a different IndexerName.
51 public MethodSpec [] need_proxy;
54 struct ProxyMethodContext : IMemberContext
56 readonly TypeContainer container;
58 public ProxyMethodContext (TypeContainer container)
60 this.container = container;
63 public TypeSpec CurrentType {
65 throw new NotImplementedException ();
69 public TypeParameter[] CurrentTypeParameters {
71 throw new NotImplementedException ();
75 public MemberCore CurrentMemberDefinition {
77 throw new NotImplementedException ();
81 public bool IsObsolete {
87 public bool IsUnsafe {
89 throw new NotImplementedException ();
93 public bool IsStatic {
99 public ModuleContainer Module {
101 return container.Module;
105 public string GetSignatureForError ()
107 throw new NotImplementedException ();
110 public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity)
112 throw new NotImplementedException ();
115 public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
117 throw new NotImplementedException ();
120 public FullNamedExpression LookupNamespaceAlias (string name)
122 throw new NotImplementedException ();
126 public class PendingImplementation
129 /// The container for this PendingImplementation
131 readonly TypeContainer container;
134 /// This is the array of TypeAndMethods that describes the pending implementations
135 /// (both interfaces and abstract methods in base class)
137 TypeAndMethods [] pending_implementations;
139 PendingImplementation (TypeContainer container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total)
141 var type_builder = container.Definition;
143 this.container = container;
144 pending_implementations = new TypeAndMethods [total];
147 if (abstract_methods != null) {
148 int count = abstract_methods.Length;
149 pending_implementations [i].methods = new MethodSpec [count];
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 (TypeContainer container)
194 // Notice that Interfaces will only return the interfaces that the Type
195 // is supposed to implement, not all the interfaces that the type implements.
197 var impl = container.Definition.Interfaces;
199 if (impl == null || impl.Count == 0)
200 return EmptyMissingInterfacesInfo;
202 MissingInterfacesInfo[] ret = new MissingInterfacesInfo[impl.Count];
204 for (int i = 0; i < impl.Count; 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 (TypeContainer 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 const Parameter.Modifier ref_out = Parameter.Modifier.REF | Parameter.Modifier.OUT;
308 if ((cp[pi].ModFlags & ref_out) == (tp[pi].ModFlags & ref_out))
311 if ((cp[pi].ModFlags & tp[pi].ModFlags & Parameter.Modifier.ISBYREF) != 0) {
312 ref_only_difference = true;
320 if (!exact_match || !ref_only_difference)
323 pending.Report.SymbolRelatedToPreviousError (compared_method);
324 pending.Report.SymbolRelatedToPreviousError (tested_method);
325 pending.Report.Error (767, container.Location,
326 "Cannot implement interface `{0}' with the specified type parameters because it causes method `{1}' to differ on parameter modifiers only",
327 p.type.GetDefinition().GetSignatureForError (), compared_method.GetSignatureForError ());
337 public enum Operation {
339 // If you change this, review the whole InterfaceMethod routine as there
340 // are a couple of assumptions on these three states
342 Lookup, ClearOne, ClearAll
346 /// Whether the specified method is an interface method implementation
348 public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method, out MethodSpec ambiguousCandidate, ref bool optional)
350 return InterfaceMethod (name, ifaceType, method, Operation.Lookup, out ambiguousCandidate, ref optional);
353 public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one, out MethodSpec ambiguousCandidate, ref bool optional)
355 InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll, out ambiguousCandidate, ref optional);
359 /// If a method in Type `t' (or null to look in all interfaces
360 /// and the base abstract class) with name `Name', return type `ret_type' and
361 /// arguments `args' implements an interface, this method will
362 /// return the MethodInfo that this method implements.
364 /// If `name' is null, we operate solely on the method's signature. This is for
365 /// instance used when implementing indexers.
367 /// The `Operation op' controls whether to lookup, clear the pending bit, or clear
368 /// all the methods with the given signature.
370 /// The `MethodInfo need_proxy' is used when we're implementing an interface's
371 /// indexer in a class. If the new indexer's IndexerName does not match the one
372 /// that was used in the interface, then we always need to create a proxy for it.
375 public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op, out MethodSpec ambiguousCandidate, ref bool optional)
377 ambiguousCandidate = null;
379 if (pending_implementations == null)
382 TypeSpec ret_type = method.method.ReturnType;
383 ParametersCompiled args = method.method.ParameterInfo;
384 bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod;
387 foreach (TypeAndMethods tm in pending_implementations){
388 if (!(iType == null || tm.type == iType))
391 int method_count = tm.methods.Count;
392 for (int i = 0; i < method_count; i++){
399 if (!m.IsAccessor || m.Parameters.IsEmpty)
402 if (name.Name != m.Name)
405 if (m.Arity != name.Arity)
409 if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args))
412 if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) {
413 tm.found[i] = method;
418 // `need_proxy' is not null when we're implementing an
419 // interface indexer and this is Clear(One/All) operation.
421 // If `name' is null, then we do a match solely based on the
422 // signature and not on the name (this is done in the Lookup
423 // for an interface indexer).
425 if (op != Operation.Lookup) {
426 if (m.IsAccessor != method.method.IsAccessor)
429 // If `t != null', then this is an explicitly interface
430 // implementation and we can always clear the method.
431 // `need_proxy' is not null if we're implementing an
432 // interface indexer. In this case, we need to create
433 // a proxy if the implementation's IndexerName doesn't
434 // match the IndexerName in the interface.
435 if (m.DeclaringType.IsInterface && iType == null && name.Name != m.Name) { // TODO: This is very expensive comparison
436 tm.need_proxy[i] = method.method.Spec;
438 tm.methods[i] = null;
441 tm.found [i] = method;
442 optional = tm.optional;
445 if (op == Operation.Lookup && name.Left != null && ambiguousCandidate == null) {
446 ambiguousCandidate = m;
451 // Lookups and ClearOne return
453 if (op != Operation.ClearAll)
457 // If a specific type was requested, we can stop now.
458 if (tm.type == iType)
462 m = ambiguousCandidate;
463 ambiguousCandidate = null;
468 /// C# allows this kind of scenarios:
469 /// interface I { void M (); }
470 /// class X { public void M (); }
471 /// class Y : X, I { }
473 /// For that case, we create an explicit implementation function
476 void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method)
478 // TODO: Handle nested iface names
480 var ns = iface.MemberDefinition.Namespace;
481 if (string.IsNullOrEmpty (ns))
482 proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name;
484 proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name;
486 var param = iface_method.Parameters;
488 MethodBuilder proxy = container.TypeBuilder.DefineMethod (
490 MethodAttributes.Private |
491 MethodAttributes.HideBySig |
492 MethodAttributes.NewSlot |
493 MethodAttributes.CheckAccessOnOverride |
494 MethodAttributes.Virtual | MethodAttributes.Final,
495 CallingConventions.Standard | CallingConventions.HasThis,
496 base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
498 if (iface_method.IsGeneric) {
499 var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();
500 proxy.DefineGenericParameters (gnames);
503 for (int i = 0; i < param.Count; i++) {
504 string name = param.FixedParameters [i].Name;
505 ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags);
506 proxy.DefineParameter (i + 1, attr, name);
509 int top = param.Count;
510 var ec = new EmitContext (new ProxyMethodContext (container), proxy.GetILGenerator (), null);
512 // TODO: GetAllParametersArguments
513 for (int i = 0; i < top; i++)
514 ec.EmitArgumentLoad (i);
516 ec.Emit (OpCodes.Call, base_method);
517 ec.Emit (OpCodes.Ret);
519 container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ());
523 /// This function tells whether one of our base classes implements
524 /// the given method (which turns out, it is valid to have an interface
525 /// implementation in a base
527 bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
530 var base_type = container.BaseType;
533 // Setup filter with no return type to give better error message
534 // about mismatch at return type when the check bellow rejects them
536 var parameters = mi.Parameters;
538 var candidates = MemberCache.FindMembers (base_type, mi.Name, false);
539 if (candidates == null)
542 MethodSpec similar_candidate = null;
543 foreach (var candidate in candidates) {
544 if (candidate.Kind != MemberKind.Method)
547 if (candidate.Arity != mi.Arity)
550 var candidate_param = ((MethodSpec) candidate).Parameters;
551 if (!TypeSpecComparer.Override.IsSame (parameters.Types, candidate_param.Types))
554 bool modifiers_match = true;
555 for (int i = 0; i < parameters.Count; ++i) {
557 // First check exact ref/out match
559 const Parameter.Modifier ref_out = Parameter.Modifier.REF | Parameter.Modifier.OUT;
560 if ((parameters.FixedParameters[i].ModFlags & ref_out) == (candidate_param.FixedParameters[i].ModFlags & ref_out))
563 modifiers_match = false;
566 // Different in ref/out only
568 if ((parameters.FixedParameters[i].ModFlags & candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.ISBYREF) != 0) {
569 if (similar_candidate == null) {
570 if (!candidate.IsPublic)
573 if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, ((MethodSpec) candidate).ReturnType))
576 // It's used for ref/out ambiguity overload check
577 similar_candidate = (MethodSpec) candidate;
583 similar_candidate = null;
587 if (!modifiers_match)
591 // From this point on the candidate is used for detailed error reporting
592 // because it's very close match to what we are looking for
594 base_method = (MethodSpec) candidate;
596 if (!candidate.IsPublic)
599 if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, base_method.ReturnType))
602 if (mi.IsGeneric && !Method.CheckImplementingMethodConstraints (container, base_method, mi)) {
607 if (base_method != null) {
608 if (similar_candidate != null) {
609 Report.SymbolRelatedToPreviousError (similar_candidate);
610 Report.SymbolRelatedToPreviousError (mi);
611 Report.SymbolRelatedToPreviousError (container);
612 Report.Warning (1956, 1, ((MemberCore) base_method.MemberDefinition).Location,
613 "The interface method `{0}' implementation is ambiguous between following methods: `{1}' and `{2}' in type `{3}'",
614 mi.GetSignatureForError (), base_method.GetSignatureForError (), similar_candidate.GetSignatureForError (), container.GetSignatureForError ());
620 base_type = candidates[0].DeclaringType.BaseType;
621 if (base_type == null)
625 if (!base_method.IsVirtual) {
627 var base_builder = base_method.GetMetaInfo () as MethodBuilder;
628 if (base_builder != null) {
630 // We can avoid creating a proxy if base_method can be marked 'final virtual'. This can
631 // be done for all methods from compiled assembly
633 base_builder.__SetAttributes (base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot);
637 DefineProxy (iface_type, base_method, mi);
644 /// Verifies that any pending abstract methods or interface methods
645 /// were implemented.
647 public bool VerifyPendingMethods ()
649 int top = pending_implementations.Length;
653 for (i = 0; i < top; i++){
654 TypeSpec type = pending_implementations [i].type;
656 bool base_implements_type = type.IsInterface &&
657 container.BaseType != null &&
658 container.BaseType.ImplementsInterface (type, false);
660 for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
661 var mi = pending_implementations[i].methods[j];
665 if (type.IsInterface){
667 pending_implementations [i].need_proxy [j];
669 if (need_proxy != null) {
670 DefineProxy (type, need_proxy, mi);
674 if (pending_implementations [i].optional)
677 MethodSpec candidate = null;
678 if (base_implements_type || BaseImplements (type, mi, out candidate))
681 if (candidate == null) {
682 MethodData md = pending_implementations [i].found [j];
684 candidate = md.method.Spec;
687 Report.SymbolRelatedToPreviousError (mi);
688 if (candidate != null) {
689 Report.SymbolRelatedToPreviousError (candidate);
690 if (candidate.IsStatic) {
691 Report.Error (736, container.Location,
692 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static",
693 container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
694 } else if ((candidate.Modifiers & Modifiers.PUBLIC) == 0) {
695 Report.Error (737, container.Location,
696 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' in not public",
697 container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
699 Report.Error (738, container.Location,
700 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
701 container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError (),
702 candidate.ReturnType.GetSignatureForError (), mi.ReturnType.GetSignatureForError ());
705 Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
706 container.GetSignatureForError (), mi.GetSignatureForError ());
709 Report.SymbolRelatedToPreviousError (mi);
710 Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
711 container.GetSignatureForError (), mi.GetSignatureForError ());