Fix effective base class for value types
[mono.git] / mcs / mcs / pending.cs
1 //
2 // pending.cs: Pending method implementation
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@gnu.org)
6 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 //
10 // Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2003-2008 Novell, Inc.
12 //
13
14 using System.Collections.Generic;
15 using System.Linq;
16
17 #if STATIC
18 using IKVM.Reflection;
19 using IKVM.Reflection.Emit;
20 #else
21 using System.Reflection;
22 using System.Reflection.Emit;
23 #endif
24
25 namespace Mono.CSharp {
26
27         struct TypeAndMethods {
28                 public TypeSpec          type;
29                 public IList<MethodSpec> methods;
30
31                 // 
32                 // Whether it is optional, this is used to allow the explicit/implicit
33                 // implementation when a base class already implements an interface. 
34                 //
35                 // For example:
36                 //
37                 // class X : IA { }  class Y : X, IA { IA.Explicit (); }
38                 //
39                 public bool          optional;
40                                 
41                 //
42                 // This flag on the method says `We found a match, but
43                 // because it was private, we could not use the match
44                 //
45                 public MethodData [] found;
46
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;
51         }
52
53         public class PendingImplementation
54         {
55                 /// <summary>
56                 ///   The container for this PendingImplementation
57                 /// </summary>
58                 readonly TypeContainer container;
59                 
60                 /// <summary>
61                 ///   This is the array of TypeAndMethods that describes the pending implementations
62                 ///   (both interfaces and abstract methods in base class)
63                 /// </summary>
64                 TypeAndMethods [] pending_implementations;
65
66                 PendingImplementation (TypeContainer container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total)
67                 {
68                         var type_builder = container.Definition;
69                         
70                         this.container = container;
71                         pending_implementations = new TypeAndMethods [total];
72
73                         int i = 0;
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];
78
79                                 pending_implementations [i].methods = abstract_methods;
80                                 pending_implementations [i].found = new MethodData [count];
81                                 pending_implementations [i].type = type_builder;
82                                 ++i;
83                         }
84
85                         foreach (MissingInterfacesInfo missing in missing_ifaces) {
86                                 var iface = missing.Type;
87                                 var mi = MemberCache.GetInterfaceMethods (iface);
88
89                                 int count = mi.Count;
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];
95                                 i++;
96                         }
97                 }
98
99                 Report Report {
100                         get {
101                                 return container.Module.Compiler.Report;
102                         }
103                 }
104
105                 struct MissingInterfacesInfo {
106                         public TypeSpec Type;
107                         public bool Optional;
108
109                         public MissingInterfacesInfo (TypeSpec t)
110                         {
111                                 Type = t;
112                                 Optional = false;
113                         }
114                 }
115
116                 static readonly MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
117                 
118                 static MissingInterfacesInfo [] GetMissingInterfaces (TypeContainer container)
119                 {
120                         //
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.
123                         //
124                         var impl = container.Definition.Interfaces;
125
126                         if (impl == null || impl.Count == 0)
127                                 return EmptyMissingInterfacesInfo;
128
129                         MissingInterfacesInfo[] ret = new MissingInterfacesInfo[impl.Count];
130
131                         for (int i = 0; i < impl.Count; i++)
132                                 ret [i] = new MissingInterfacesInfo (impl [i]);
133
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)
138                                 return ret;
139                         
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;
146                                                         break;
147                                                 }
148                                         }
149                                 }
150                         }
151
152                         return ret;
153                 }
154                 
155                 //
156                 // Factory method: if there are pending implementation methods, we return a PendingImplementation
157                 // object, otherwise we return null.
158                 //
159                 // Register method implementations are either abstract methods
160                 // flagged as such on the base class or interface methods
161                 //
162                 static public PendingImplementation GetPendingImplementations (TypeContainer container)
163                 {
164                         TypeSpec b = container.BaseType;
165
166                         var missing_interfaces = GetMissingInterfaces (container);
167
168                         //
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
172                         // one slot.
173                         //
174                         // We also pre-compute the methods.
175                         //
176                         bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0);
177                         MethodSpec[] abstract_methods = null;
178
179                         if (implementing_abstract){
180                                 var am = MemberCache.GetNotImplementedAbstractMethods (b);
181
182                                 if (am == null) {
183                                         implementing_abstract = false;
184                                 } else {
185                                         abstract_methods = new MethodSpec[am.Count];
186                                         am.CopyTo (abstract_methods, 0);
187                                 }
188                         }
189                         
190                         int total = missing_interfaces.Length +  (implementing_abstract ? 1 : 0);
191                         if (total == 0)
192                                 return null;
193
194                         var pending = new PendingImplementation (container, missing_interfaces, abstract_methods, total);
195
196                         //
197                         // check for inherited conflicting methods
198                         //
199                         foreach (var p in pending.pending_implementations) {
200                                 //
201                                 // It can happen for generic interfaces only
202                                 //
203                                 if (!p.type.IsGeneric)
204                                         continue;
205
206                                 //
207                                 // CLR does not distinguishes between ref and out
208                                 //
209                                 for (int i = 0; i < p.methods.Count; ++i) {
210                                         MethodSpec compared_method = p.methods[i];
211                                         if (compared_method.Parameters.IsEmpty)
212                                                 continue;
213
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)
217                                                         continue;
218
219                                                 if (p.type != tested_method.DeclaringType)
220                                                         continue;
221
222                                                 if (!TypeSpecComparer.Override.IsSame (compared_method.Parameters.Types, tested_method.Parameters.Types))
223                                                         continue;
224
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) {
230                                                         //
231                                                         // First check exact modifiers match
232                                                         //
233                                                         const Parameter.Modifier ref_out = Parameter.Modifier.REF | Parameter.Modifier.OUT;
234                                                         if ((cp[i].ModFlags & ref_out) == (tp[i].ModFlags & ref_out))
235                                                                 continue;
236
237                                                         if ((cp[i].ModFlags & tp[i].ModFlags & Parameter.Modifier.ISBYREF) != 0) {
238                                                                 ref_only_difference = true;
239                                                                 continue;
240                                                         }
241
242                                                         exact_match = false;
243                                                         break;
244                                                 }
245
246                                                 if (!exact_match || !ref_only_difference)
247                                                         continue;
248
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 ());
254
255                                                 break;
256                                         }
257                                 }
258                         }
259
260                         return pending;
261                 }
262
263                 public enum Operation {
264                         //
265                         // If you change this, review the whole InterfaceMethod routine as there
266                         // are a couple of assumptions on these three states
267                         //
268                         Lookup, ClearOne, ClearAll
269                 }
270
271                 /// <summary>
272                 ///   Whether the specified method is an interface method implementation
273                 /// </summary>
274                 public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method, out MethodSpec ambiguousCandidate)
275                 {
276                         return InterfaceMethod (name, ifaceType, method, Operation.Lookup, out ambiguousCandidate);
277                 }
278
279                 public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one, out MethodSpec ambiguousCandidate)
280                 {
281                         InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll, out ambiguousCandidate);
282                 }
283
284                 /// <remarks>
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.
289                 ///
290                 ///   If `name' is null, we operate solely on the method's signature.  This is for
291                 ///   instance used when implementing indexers.
292                 ///
293                 ///   The `Operation op' controls whether to lookup, clear the pending bit, or clear
294                 ///   all the methods with the given signature.
295                 ///
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.
299                 ///
300                 /// </remarks>
301                 public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op, out MethodSpec ambiguousCandidate)
302                 {
303                         ambiguousCandidate = null;
304
305                         if (pending_implementations == null)
306                                 return null;
307
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;
311                         MethodSpec m;
312
313                         foreach (TypeAndMethods tm in pending_implementations){
314                                 if (!(iType == null || tm.type == iType))
315                                         continue;
316
317                                 int method_count = tm.methods.Count;
318                                 for (int i = 0; i < method_count; i++){
319                                         m = tm.methods [i];
320
321                                         if (m == null)
322                                                 continue;
323
324                                         if (is_indexer) {
325                                                 if (!m.IsAccessor || m.Parameters.IsEmpty)
326                                                         continue;
327                                         } else {
328                                                 if (name.Name != m.Name)
329                                                         continue;
330
331                                                 if (m.Arity != name.Arity)
332                                                         continue;
333                                         }
334
335                                         if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args))
336                                                 continue;
337
338                                         if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) {
339                                                 tm.found[i] = method;
340                                                 continue;
341                                         }
342
343                                         //
344                                         // `need_proxy' is not null when we're implementing an
345                                         // interface indexer and this is Clear(One/All) operation.
346                                         //
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).
350                                         //
351                                         if (op != Operation.Lookup) {
352                                                 if (m.IsAccessor != method.method.IsAccessor)
353                                                         continue;
354
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;
363                                                 } else {
364                                                         tm.methods[i] = null;
365                                                 }
366                                         } else {
367                                                 tm.found [i] = method;
368                                         }
369
370                                         if (op == Operation.Lookup && name.Left != null && ambiguousCandidate == null) {
371                                                 ambiguousCandidate = m;
372                                                 continue;
373                                         }
374
375                                         //
376                                         // Lookups and ClearOne return
377                                         //
378                                         if (op != Operation.ClearAll)
379                                                 return m;
380                                 }
381
382                                 // If a specific type was requested, we can stop now.
383                                 if (tm.type == iType)
384                                         break;
385                         }
386
387                         m = ambiguousCandidate;
388                         ambiguousCandidate = null;
389                         return m;
390                 }
391
392                 /// <summary>
393                 ///   C# allows this kind of scenarios:
394                 ///   interface I { void M (); }
395                 ///   class X { public void M (); }
396                 ///   class Y : X, I { }
397                 ///
398                 ///   For that case, we create an explicit implementation function
399                 ///   I.M in Y.
400                 /// </summary>
401                 void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method)
402                 {
403                         // TODO: Handle nested iface names
404                         string proxy_name;
405                         var ns = iface.MemberDefinition.Namespace;
406                         if (string.IsNullOrEmpty (ns))
407                                 proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name;
408                         else
409                                 proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name;
410
411                         var param = iface_method.Parameters;
412
413                         MethodBuilder proxy = container.TypeBuilder.DefineMethod (
414                                 proxy_name,
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 ());
422
423                         if (iface_method.IsGeneric) {
424                                 var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();
425                                 proxy.DefineGenericParameters (gnames);
426                         }
427
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);
432                         }
433
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);
439
440                         ec.Emit (OpCodes.Call, base_method);
441                         ec.Emit (OpCodes.Ret);
442
443                         container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ());
444                 }
445                 
446                 /// <summary>
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
450                 /// </summary>
451                 bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
452                 {
453                         base_method = null;
454                         var base_type = container.BaseType;
455
456                         //
457                         // Setup filter with no return type to give better error message
458                         // about mismatch at return type when the check bellow rejects them
459                         //
460                         var parameters = mi.Parameters;
461                         while (true) {
462                                 var candidates = MemberCache.FindMembers (base_type, mi.Name, false);
463                                 if (candidates == null)
464                                         return false;
465
466                                 MethodSpec similar_candidate = null;
467                                 foreach (var candidate in candidates) {
468                                         if (candidate.Kind != MemberKind.Method)
469                                                 continue;
470
471                                         if (candidate.Arity != mi.Arity)
472                                                 continue;
473
474                                         var candidate_param = ((MethodSpec) candidate).Parameters;
475                                         if (!TypeSpecComparer.Override.IsSame (parameters.Types, candidate_param.Types))
476                                                 continue;
477
478                                         bool modifiers_match = true;
479                                         for (int i = 0; i < parameters.Count; ++i) {
480                                                 //
481                                                 // First check exact ref/out match
482                                                 //
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))
485                                                         continue;
486
487                                                 modifiers_match = false;
488
489                                                 //
490                                                 // Different in ref/out only
491                                                 //
492                                                 if ((parameters.FixedParameters[i].ModFlags & candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.ISBYREF) != 0) {
493                                                         if (similar_candidate == null) {
494                                                                 if (!candidate.IsPublic)
495                                                                         break;
496
497                                                                 if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, ((MethodSpec) candidate).ReturnType))
498                                                                         break;
499
500                                                                 // It's used for ref/out ambiguity overload check
501                                                                 similar_candidate = (MethodSpec) candidate;
502                                                         }
503
504                                                         continue;
505                                                 }
506
507                                                 similar_candidate = null;
508                                                 break;
509                                         }
510
511                                         if (!modifiers_match)
512                                                 continue;
513
514                                         //
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
517                                         //
518                                         base_method = (MethodSpec) candidate;
519
520                                         if (!candidate.IsPublic)
521                                                 return false;
522
523                                         if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, base_method.ReturnType))
524                                                 return false;
525                                 }
526
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 ());
535                                         }
536
537                                         break;
538                                 }
539
540                                 base_type = candidates[0].DeclaringType.BaseType;
541                                 if (base_type == null)
542                                         return false;
543                         }
544
545                         if (!base_method.IsVirtual) {
546 #if STATIC
547                                 var base_builder = base_method.GetMetaInfo () as MethodBuilder;
548                                 if (base_builder != null) {
549                                         //
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
552                                         //
553                                         base_builder.__SetAttributes (base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot);
554                                         return true;
555                                 }
556 #endif
557                                 DefineProxy (iface_type, base_method, mi);
558                         }
559
560                         return true;
561                 }
562
563                 /// <summary>
564                 ///   Verifies that any pending abstract methods or interface methods
565                 ///   were implemented.
566                 /// </summary>
567                 public bool VerifyPendingMethods ()
568                 {
569                         int top = pending_implementations.Length;
570                         bool errors = false;
571                         int i;
572                         
573                         for (i = 0; i < top; i++){
574                                 TypeSpec type = pending_implementations [i].type;
575
576                                 bool base_implements_type = type.IsInterface &&
577                                         container.BaseType != null &&
578                                         container.BaseType.ImplementsInterface (type, false);
579
580                                 for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
581                                         var mi = pending_implementations[i].methods[j];
582                                         if (mi == null)
583                                                 continue;
584
585                                         if (type.IsInterface){
586                                                 var need_proxy =
587                                                         pending_implementations [i].need_proxy [j];
588
589                                                 if (need_proxy != null) {
590                                                         DefineProxy (type, need_proxy, mi);
591                                                         continue;
592                                                 }
593
594                                                 if (pending_implementations [i].optional)
595                                                         continue;
596
597                                                 MethodSpec candidate = null;
598                                                 if (base_implements_type || BaseImplements (type, mi, out candidate))
599                                                         continue;
600
601                                                 if (candidate == null) {
602                                                         MethodData md = pending_implementations [i].found [j];
603                                                         if (md != null)
604                                                                 candidate = md.method.Spec;
605                                                 }
606
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 ());
618                                                         } else {
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));
623                                                         }
624                                                 } else {
625                                                         Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
626                                                                 container.GetSignatureForError (), mi.GetSignatureForError ());
627                                                 }
628                                         } else {
629                                                 Report.SymbolRelatedToPreviousError (mi);
630                                                 Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
631                                                         container.GetSignatureForError (), mi.GetSignatureForError ());
632                                         }
633                                         errors = true;
634                                 }
635                         }
636                         return errors;
637                 }
638         }
639 }