Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Resolver/CSharpConversions.cs @ 12222

Last change on this file since 12222 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 47.1 KB
Line 
1// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19using System;
20using System.Collections.Concurrent;
21using System.Collections.Generic;
22using System.Diagnostics;
23using System.Linq;
24using System.Threading;
25using ICSharpCode.NRefactory.Semantics;
26using ICSharpCode.NRefactory.TypeSystem;
27using ICSharpCode.NRefactory.Utils;
28
29namespace ICSharpCode.NRefactory.CSharp.Resolver
30{
31  /// <summary>
32  /// Contains logic that determines whether an implicit conversion exists between two types.
33  /// </summary>
34  /// <remarks>
35  /// This class is thread-safe.
36  /// </remarks>
37  public sealed class CSharpConversions
38  {
39    readonly ConcurrentDictionary<TypePair, Conversion> implicitConversionCache = new ConcurrentDictionary<TypePair, Conversion>();
40    readonly ICompilation compilation;
41    readonly IType objectType;
42   
43    public CSharpConversions(ICompilation compilation)
44    {
45      if (compilation == null)
46        throw new ArgumentNullException("compilation");
47      this.compilation = compilation;
48      this.objectType = compilation.FindType(KnownTypeCode.Object);
49      this.dynamicErasure = new DynamicErasure(this);
50    }
51   
52    /// <summary>
53    /// Gets the Conversions instance for the specified <see cref="ICompilation"/>.
54    /// This will make use of the context's cache manager to reuse the Conversions instance.
55    /// </summary>
56    public static CSharpConversions Get(ICompilation compilation)
57    {
58      if (compilation == null)
59        throw new ArgumentNullException("compilation");
60      CacheManager cache = compilation.CacheManager;
61      CSharpConversions operators = (CSharpConversions)cache.GetShared(typeof(CSharpConversions));
62      if (operators == null) {
63        operators = (CSharpConversions)cache.GetOrAddShared(typeof(CSharpConversions), new CSharpConversions(compilation));
64      }
65      return operators;
66    }
67   
68    #region TypePair (for caching)
69    struct TypePair : IEquatable<TypePair>
70    {
71      public readonly IType FromType;
72      public readonly IType ToType;
73     
74      public TypePair(IType fromType, IType toType)
75      {
76        Debug.Assert(fromType != null && toType != null);
77        this.FromType = fromType;
78        this.ToType = toType;
79      }
80     
81      public override bool Equals(object obj)
82      {
83        return (obj is TypePair) && Equals((TypePair)obj);
84      }
85     
86      public bool Equals(TypePair other)
87      {
88        return object.Equals(this.FromType, other.FromType) && object.Equals(this.ToType, other.ToType);
89      }
90     
91      public override int GetHashCode()
92      {
93        unchecked {
94          return 1000000007 * FromType.GetHashCode() + 1000000009 * ToType.GetHashCode();
95        }
96      }
97    }
98    #endregion
99   
100    #region ImplicitConversion
101    private Conversion ImplicitConversion(ResolveResult resolveResult, IType toType, bool allowUserDefined)
102    {
103      Conversion c;
104      if (resolveResult.IsCompileTimeConstant) {
105        c = ImplicitEnumerationConversion(resolveResult, toType);
106        if (c.IsValid) return c;
107        if (ImplicitConstantExpressionConversion(resolveResult, toType))
108          return Conversion.ImplicitConstantExpressionConversion;
109        c = StandardImplicitConversion(resolveResult.Type, toType);
110        if (c != Conversion.None) return c;
111        if (allowUserDefined) {
112          c = UserDefinedImplicitConversion(resolveResult, resolveResult.Type, toType);
113          if (c != Conversion.None) return c;
114        }
115      } else {
116        c = ImplicitConversion(resolveResult.Type, toType, allowUserDefined);
117        if (c != Conversion.None) return c;
118      }
119      if (resolveResult.Type.Kind == TypeKind.Dynamic)
120        return Conversion.ImplicitDynamicConversion;
121      c = AnonymousFunctionConversion(resolveResult, toType);
122      if (c != Conversion.None) return c;
123      c = MethodGroupConversion(resolveResult, toType);
124      return c;
125    }
126   
127    private Conversion ImplicitConversion(IType fromType, IType toType, bool allowUserDefined)
128    {
129      // C# 4.0 spec: §6.1
130      var c = StandardImplicitConversion(fromType, toType);
131      if (c == Conversion.None && allowUserDefined) {
132        c = UserDefinedImplicitConversion(null, fromType, toType);
133      }
134      return c;
135    }
136
137    public Conversion ImplicitConversion(ResolveResult resolveResult, IType toType)
138    {
139      if (resolveResult == null)
140        throw new ArgumentNullException("resolveResult");
141      return ImplicitConversion(resolveResult, toType, allowUserDefined: true);
142    }
143
144    public Conversion ImplicitConversion(IType fromType, IType toType)
145    {
146      if (fromType == null)
147        throw new ArgumentNullException("fromType");
148      if (toType == null)
149        throw new ArgumentNullException("toType");
150     
151      TypePair pair = new TypePair(fromType, toType);
152      Conversion c;
153      if (implicitConversionCache.TryGetValue(pair, out c))
154        return c;
155
156      c = ImplicitConversion(fromType, toType, allowUserDefined: true);
157
158      implicitConversionCache[pair] = c;
159      return c;
160    }
161   
162    public Conversion StandardImplicitConversion(IType fromType, IType toType)
163    {
164      if (fromType == null)
165        throw new ArgumentNullException("fromType");
166      if (toType == null)
167        throw new ArgumentNullException("toType");
168      // C# 4.0 spec: §6.3.1
169      if (IdentityConversion(fromType, toType))
170        return Conversion.IdentityConversion;
171      if (ImplicitNumericConversion(fromType, toType))
172        return Conversion.ImplicitNumericConversion;
173      Conversion c = ImplicitNullableConversion(fromType, toType);
174      if (c != Conversion.None)
175        return c;
176      if (NullLiteralConversion(fromType, toType))
177        return Conversion.NullLiteralConversion;
178      if (ImplicitReferenceConversion(fromType, toType, 0))
179        return Conversion.ImplicitReferenceConversion;
180      if (IsBoxingConversion(fromType, toType))
181        return Conversion.BoxingConversion;
182      if (ImplicitTypeParameterConversion(fromType, toType)) {
183        // Implicit type parameter conversions that aren't also
184        // reference conversions are considered to be boxing conversions
185        return Conversion.BoxingConversion;
186      }
187      if (ImplicitPointerConversion(fromType, toType))
188        return Conversion.ImplicitPointerConversion;
189      return Conversion.None;
190    }
191   
192    /// <summary>
193    /// Gets whether the type 'fromType' is convertible to 'toType'
194    /// using one of the conversions allowed when satisying constraints (§4.4.4)
195    /// </summary>
196    public bool IsConstraintConvertible(IType fromType, IType toType)
197    {
198      if (fromType == null)
199        throw new ArgumentNullException("fromType");
200      if (toType == null)
201        throw new ArgumentNullException("toType");
202     
203      if (IdentityConversion(fromType, toType))
204        return true;
205      if (ImplicitReferenceConversion(fromType, toType, 0))
206        return true;
207      if (NullableType.IsNullable(fromType)) {
208        // An 'object' constraint still allows nullable value types
209        // (object constraints don't exist in C#, but are inserted by DefaultResolvedTypeParameter.DirectBaseTypes)
210        if (toType.IsKnownType(KnownTypeCode.Object))
211          return true;
212      } else {
213        if (IsBoxingConversion(fromType, toType))
214          return true;
215      }
216      if (ImplicitTypeParameterConversion(fromType, toType))
217        return true;
218      return false;
219    }
220    #endregion
221   
222    #region ExplicitConversion
223    public Conversion ExplicitConversion(ResolveResult resolveResult, IType toType)
224    {
225      if (resolveResult == null)
226        throw new ArgumentNullException("resolveResult");
227      if (toType == null)
228        throw new ArgumentNullException("toType");
229     
230      if (resolveResult.Type.Kind == TypeKind.Dynamic)
231        return Conversion.ExplicitDynamicConversion;
232      Conversion c = ImplicitConversion(resolveResult, toType, allowUserDefined: false);
233      if (c != Conversion.None)
234        return c;
235      c = ExplicitConversionImpl(resolveResult.Type, toType);
236      if (c != Conversion.None)
237        return c;
238      return UserDefinedExplicitConversion(resolveResult, resolveResult.Type, toType);
239    }
240   
241    public Conversion ExplicitConversion(IType fromType, IType toType)
242    {
243      if (fromType == null)
244        throw new ArgumentNullException("fromType");
245      if (toType == null)
246        throw new ArgumentNullException("toType");
247     
248      Conversion c = ImplicitConversion(fromType, toType, allowUserDefined: false);
249      if (c != Conversion.None)
250        return c;
251      c = ExplicitConversionImpl(fromType, toType);
252      if (c != Conversion.None)
253        return c;
254      return UserDefinedExplicitConversion(null, fromType, toType);
255    }
256   
257    Conversion ExplicitConversionImpl(IType fromType, IType toType)
258    {
259      // This method is called after we already checked for implicit conversions,
260      // so any remaining conversions must be explicit.
261      if (AnyNumericConversion(fromType, toType))
262        return Conversion.ExplicitNumericConversion;
263      if (ExplicitEnumerationConversion(fromType, toType))
264        return Conversion.EnumerationConversion(false, false);
265      Conversion c = ExplicitNullableConversion(fromType, toType);
266      if (c != Conversion.None)
267        return c;
268      if (ExplicitReferenceConversion(fromType, toType))
269        return Conversion.ExplicitReferenceConversion;
270      if (UnboxingConversion(fromType, toType))
271        return Conversion.UnboxingConversion;
272      c = ExplicitTypeParameterConversion(fromType, toType);
273      if (c != Conversion.None)
274        return c;
275      if (ExplicitPointerConversion(fromType, toType))
276        return Conversion.ExplicitPointerConversion;
277      return Conversion.None;
278    }
279    #endregion
280   
281    #region Identity Conversion
282    /// <summary>
283    /// Gets whether there is an identity conversion from <paramref name="fromType"/> to <paramref name="toType"/>
284    /// </summary>
285    public bool IdentityConversion(IType fromType, IType toType)
286    {
287      // C# 4.0 spec: §6.1.1
288      return fromType.AcceptVisitor(dynamicErasure).Equals(toType.AcceptVisitor(dynamicErasure));
289    }
290   
291    readonly DynamicErasure dynamicErasure;
292   
293    sealed class DynamicErasure : TypeVisitor
294    {
295      readonly IType objectType;
296     
297      public DynamicErasure(CSharpConversions conversions)
298      {
299        this.objectType = conversions.objectType;
300      }
301     
302      public override IType VisitOtherType(IType type)
303      {
304        if (type.Kind == TypeKind.Dynamic)
305          return objectType;
306        else
307          return base.VisitOtherType(type);
308      }
309    }
310    #endregion
311   
312    #region Numeric Conversions
313    static readonly bool[,] implicitNumericConversionLookup = {
314      //       to:   short  ushort  int   uint   long   ulong
315      // from:
316      /* char   */ { false, true , true , true , true , true  },
317      /* sbyte  */ { true , false, true , false, true , false },
318      /* byte   */ { true , true , true , true , true , true  },
319      /* short  */ { false, false, true , false, true , false },
320      /* ushort */ { false, false, true , true , true , true  },
321      /* int    */ { false, false, false, false, true , false },
322      /* uint   */ { false, false, false, false, true , true  },
323    };
324   
325    bool ImplicitNumericConversion(IType fromType, IType toType)
326    {
327      // C# 4.0 spec: §6.1.2
328     
329      TypeCode from = ReflectionHelper.GetTypeCode(fromType);
330      TypeCode to = ReflectionHelper.GetTypeCode(toType);
331      if (to >= TypeCode.Single && to <= TypeCode.Decimal) {
332        // Conversions to float/double/decimal exist from all integral types,
333        // and there's a conversion from float to double.
334        return from >= TypeCode.Char && from <= TypeCode.UInt64
335          || from == TypeCode.Single && to == TypeCode.Double;
336      } else {
337        // Conversions to integral types: look at the table
338        return from >= TypeCode.Char && from <= TypeCode.UInt32
339          && to >= TypeCode.Int16 && to <= TypeCode.UInt64
340          && implicitNumericConversionLookup[from - TypeCode.Char, to - TypeCode.Int16];
341      }
342    }
343   
344    bool IsNumericType(IType type)
345    {
346      TypeCode c = ReflectionHelper.GetTypeCode(type);
347      return c >= TypeCode.Char && c <= TypeCode.Decimal;
348    }
349   
350    bool AnyNumericConversion(IType fromType, IType toType)
351    {
352      // C# 4.0 spec: §6.1.2 + §6.2.1
353      return IsNumericType(fromType) && IsNumericType(toType);
354    }
355    #endregion
356   
357    #region Enumeration Conversions
358    Conversion ImplicitEnumerationConversion(ResolveResult rr, IType toType)
359    {
360      // C# 4.0 spec: §6.1.3
361      Debug.Assert(rr.IsCompileTimeConstant);
362      TypeCode constantType = ReflectionHelper.GetTypeCode(rr.Type);
363      if (constantType >= TypeCode.SByte && constantType <= TypeCode.Decimal && Convert.ToDouble(rr.ConstantValue) == 0) {
364        if (NullableType.GetUnderlyingType(toType).Kind == TypeKind.Enum) {
365          return Conversion.EnumerationConversion(true, NullableType.IsNullable(toType));
366        }
367      }
368      return Conversion.None;
369    }
370   
371    bool ExplicitEnumerationConversion(IType fromType, IType toType)
372    {
373      // C# 4.0 spec: §6.2.2
374      if (fromType.Kind == TypeKind.Enum) {
375        return toType.Kind == TypeKind.Enum || IsNumericType(toType);
376      } else if (IsNumericType(fromType)) {
377        return toType.Kind == TypeKind.Enum;
378      }
379      return false;
380    }
381    #endregion
382   
383    #region Nullable Conversions
384    Conversion ImplicitNullableConversion(IType fromType, IType toType)
385    {
386      // C# 4.0 spec: §6.1.4
387      if (NullableType.IsNullable(toType)) {
388        IType t = NullableType.GetUnderlyingType(toType);
389        IType s = NullableType.GetUnderlyingType(fromType); // might or might not be nullable
390        if (IdentityConversion(s, t))
391          return Conversion.ImplicitNullableConversion;
392        if (ImplicitNumericConversion(s, t))
393          return Conversion.ImplicitLiftedNumericConversion;
394      }
395      return Conversion.None;
396    }
397   
398    Conversion ExplicitNullableConversion(IType fromType, IType toType)
399    {
400      // C# 4.0 spec: §6.1.4
401      if (NullableType.IsNullable(toType) || NullableType.IsNullable(fromType)) {
402        IType t = NullableType.GetUnderlyingType(toType);
403        IType s = NullableType.GetUnderlyingType(fromType);
404        if (IdentityConversion(s, t))
405          return Conversion.ExplicitNullableConversion;
406        if (AnyNumericConversion(s, t))
407          return Conversion.ExplicitLiftedNumericConversion;
408        if (ExplicitEnumerationConversion(s, t))
409          return Conversion.EnumerationConversion(false, true);
410      }
411      return Conversion.None;
412    }
413    #endregion
414   
415    #region Null Literal Conversion
416    bool NullLiteralConversion(IType fromType, IType toType)
417    {
418      // C# 4.0 spec: §6.1.5
419      if (fromType.Kind == TypeKind.Null) {
420        return NullableType.IsNullable(toType) || toType.IsReferenceType == true;
421      } else {
422        return false;
423      }
424    }
425    #endregion
426   
427    #region Implicit Reference Conversion
428    public bool IsImplicitReferenceConversion(IType fromType, IType toType)
429    {
430      return ImplicitReferenceConversion(fromType, toType, 0);
431    }
432   
433    bool ImplicitReferenceConversion(IType fromType, IType toType, int subtypeCheckNestingDepth)
434    {
435      // C# 4.0 spec: §6.1.6
436     
437      // reference conversions are possible:
438      // - if both types are known to be reference types
439      // - if both types are type parameters and fromType has a class constraint
440      //     (ImplicitTypeParameterConversionWithClassConstraintOnlyOnT)
441      if (!(fromType.IsReferenceType == true && toType.IsReferenceType != false))
442        return false;
443     
444      ArrayType fromArray = fromType as ArrayType;
445      if (fromArray != null) {
446        ArrayType toArray = toType as ArrayType;
447        if (toArray != null) {
448          // array covariance (the broken kind)
449          return fromArray.Dimensions == toArray.Dimensions
450            && ImplicitReferenceConversion(fromArray.ElementType, toArray.ElementType, subtypeCheckNestingDepth);
451        }
452        // conversion from single-dimensional array S[] to IList<T>:
453        IType toTypeArgument = UnpackGenericArrayInterface(toType);
454        if (fromArray.Dimensions == 1 && toTypeArgument != null) {
455          // array covariance plays a part here as well (string[] is IList<object>)
456          return IdentityConversion(fromArray.ElementType, toTypeArgument)
457            || ImplicitReferenceConversion(fromArray.ElementType, toTypeArgument, subtypeCheckNestingDepth);
458        }
459        // conversion from any array to System.Array and the interfaces it implements:
460        IType systemArray = compilation.FindType(KnownTypeCode.Array);
461        return ImplicitReferenceConversion(systemArray, toType, subtypeCheckNestingDepth);
462      }
463     
464      // now comes the hard part: traverse the inheritance chain and figure out generics+variance
465      return IsSubtypeOf(fromType, toType, subtypeCheckNestingDepth);
466    }
467   
468    /// <summary>
469    /// For IList{T}, ICollection{T}, IEnumerable{T} and IReadOnlyList{T}, returns T.
470    /// Otherwise, returns null.
471    /// </summary>
472    IType UnpackGenericArrayInterface(IType interfaceType)
473    {
474      ParameterizedType pt = interfaceType as ParameterizedType;
475      if (pt != null) {
476        KnownTypeCode tc = pt.GetDefinition().KnownTypeCode;
477        if (tc == KnownTypeCode.IListOfT || tc == KnownTypeCode.ICollectionOfT || tc == KnownTypeCode.IEnumerableOfT || tc == KnownTypeCode.IReadOnlyListOfT) {
478          return pt.GetTypeArgument(0);
479        }
480      }
481      return null;
482    }
483   
484    // Determines whether s is a subtype of t.
485    // Helper method used for ImplicitReferenceConversion, BoxingConversion and ImplicitTypeParameterConversion
486   
487    bool IsSubtypeOf(IType s, IType t, int subtypeCheckNestingDepth)
488    {
489      // conversion to dynamic + object are always possible
490      if (t.Kind == TypeKind.Dynamic || t.Equals(objectType))
491        return true;
492      if (subtypeCheckNestingDepth > 10) {
493        // Subtyping in C# is undecidable
494        // (see "On Decidability of Nominal Subtyping with Variance" by Andrew J. Kennedy and Benjamin C. Pierce),
495        // so we'll prevent infinite recursions by putting a limit on the nesting depth of variance conversions.
496       
497        // No real C# code should use generics nested more than 10 levels deep, and even if they do, most of
498        // those nestings should not involve variance.
499        return false;
500      }
501      // let GetAllBaseTypes do the work for us
502      foreach (IType baseType in s.GetAllBaseTypes()) {
503        if (IdentityOrVarianceConversion(baseType, t, subtypeCheckNestingDepth + 1))
504          return true;
505      }
506      return false;
507    }
508   
509    bool IdentityOrVarianceConversion(IType s, IType t, int subtypeCheckNestingDepth)
510    {
511      ITypeDefinition def = s.GetDefinition();
512      if (def != null) {
513        if (!def.Equals(t.GetDefinition()))
514          return false;
515        ParameterizedType ps = s as ParameterizedType;
516        ParameterizedType pt = t as ParameterizedType;
517        if (ps != null && pt != null) {
518          // C# 4.0 spec: §13.1.3.2 Variance Conversion
519          for (int i = 0; i < def.TypeParameters.Count; i++) {
520            IType si = ps.GetTypeArgument(i);
521            IType ti = pt.GetTypeArgument(i);
522            if (IdentityConversion(si, ti))
523              continue;
524            ITypeParameter xi = def.TypeParameters[i];
525            switch (xi.Variance) {
526              case VarianceModifier.Covariant:
527                if (!ImplicitReferenceConversion(si, ti, subtypeCheckNestingDepth))
528                  return false;
529                break;
530              case VarianceModifier.Contravariant:
531                if (!ImplicitReferenceConversion(ti, si, subtypeCheckNestingDepth))
532                  return false;
533                break;
534              default:
535                return false;
536            }
537          }
538        } else if (ps != null || pt != null) {
539          return false; // only of of them is parameterized, or counts don't match? -> not valid conversion
540        }
541        return true;
542      } else {
543        // not type definitions? we still need to check for equal types (e.g. s and t might be type parameters)
544        return s.Equals(t);
545      }
546    }
547    #endregion
548   
549    #region Explicit Reference Conversion
550    bool ExplicitReferenceConversion(IType fromType, IType toType)
551    {
552      // C# 4.0 spec: §6.2.4
553     
554      // test that the types are reference types:
555      if (toType.IsReferenceType != true)
556        return false;
557      if (fromType.IsReferenceType != true) {
558        // special case:
559        // converting from F to T is a reference conversion where T : class, F
560        // (because F actually must be a reference type as well, even though C# doesn't treat it as one)
561        if (fromType.Kind == TypeKind.TypeParameter)
562          return IsSubtypeOf(toType, fromType, 0);
563        return false;
564      }
565     
566      if (toType.Kind == TypeKind.Array) {
567        ArrayType toArray = (ArrayType)toType;
568        if (fromType.Kind == TypeKind.Array) {
569          // Array covariance
570          ArrayType fromArray = (ArrayType)fromType;
571          if (fromArray.Dimensions != toArray.Dimensions)
572            return false;
573          return ExplicitReferenceConversion(fromArray.ElementType, toArray.ElementType);
574        }
575        IType fromTypeArgument = UnpackGenericArrayInterface(fromType);
576        if (fromTypeArgument != null && toArray.Dimensions == 1) {
577          return ExplicitReferenceConversion(fromTypeArgument, toArray.ElementType)
578            || IdentityConversion(fromTypeArgument, toArray.ElementType);
579        }
580        // Otherwise treat the array like a sealed class - require implicit conversion in the opposite direction
581        return IsImplicitReferenceConversion(toType, fromType);
582      } else if (fromType.Kind == TypeKind.Array) {
583        ArrayType fromArray = (ArrayType)fromType;
584        IType toTypeArgument = UnpackGenericArrayInterface(toType);
585        if (toTypeArgument != null && fromArray.Dimensions == 1) {
586          return ExplicitReferenceConversion(fromArray.ElementType, toTypeArgument);
587        }
588        // Otherwise treat the array like a sealed class
589        return IsImplicitReferenceConversion(fromType, toType);
590      } else if (fromType.Kind == TypeKind.Delegate && toType.Kind == TypeKind.Delegate) {
591        ITypeDefinition def = fromType.GetDefinition();
592        if (def == null || !def.Equals(toType.GetDefinition()))
593          return false;
594        ParameterizedType ps = fromType as ParameterizedType;
595        ParameterizedType pt = toType as ParameterizedType;
596        if (ps == null || pt == null) {
597          // non-generic delegate - return true for the identity conversion
598          return ps == null && pt == null;
599        }
600        for (int i = 0; i < def.TypeParameters.Count; i++) {
601          IType si = ps.GetTypeArgument(i);
602          IType ti = pt.GetTypeArgument(i);
603          if (IdentityConversion(si, ti))
604            continue;
605          ITypeParameter xi = def.TypeParameters[i];
606          switch (xi.Variance) {
607            case VarianceModifier.Covariant:
608              if (!ExplicitReferenceConversion(si, ti))
609                return false;
610              break;
611            case VarianceModifier.Contravariant:
612              if (!(si.IsReferenceType == true && ti.IsReferenceType == true))
613                return false;
614              break;
615            default:
616              return false;
617          }
618        }
619        return true;
620      } else if (IsSealedReferenceType(fromType)) {
621        // If the source type is sealed, explicit conversions can't do anything more than implicit ones
622        return IsImplicitReferenceConversion(fromType, toType);
623      } else if (IsSealedReferenceType(toType)) {
624        // The the target type is sealed, there must be an implicit conversion in the opposite direction
625        return IsImplicitReferenceConversion(toType, fromType);
626      } else {
627        if (fromType.Kind == TypeKind.Interface || toType.Kind == TypeKind.Interface)
628          return true;
629        else
630          return IsImplicitReferenceConversion(toType, fromType)
631            || IsImplicitReferenceConversion(fromType, toType);
632      }
633    }
634   
635    bool IsSealedReferenceType(IType type)
636    {
637      TypeKind kind = type.Kind;
638      return kind == TypeKind.Class && type.GetDefinition().IsSealed
639        || kind == TypeKind.Delegate || kind == TypeKind.Anonymous;
640    }
641    #endregion
642   
643    #region Boxing Conversions
644    public bool IsBoxingConversion(IType fromType, IType toType)
645    {
646      // C# 4.0 spec: §6.1.7
647      fromType = NullableType.GetUnderlyingType(fromType);
648      if (fromType.IsReferenceType == false && toType.IsReferenceType == true)
649        return IsSubtypeOf(fromType, toType, 0);
650      else
651        return false;
652    }
653   
654    bool UnboxingConversion(IType fromType, IType toType)
655    {
656      // C# 4.0 spec: §6.2.5
657      toType = NullableType.GetUnderlyingType(toType);
658      if (fromType.IsReferenceType == true && toType.IsReferenceType == false)
659        return IsSubtypeOf(toType, fromType, 0);
660      else
661        return false;
662    }
663    #endregion
664   
665    #region Implicit Constant-Expression Conversion
666    bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType)
667    {
668      if (rr == null || !rr.IsCompileTimeConstant)
669        return false;
670      // C# 4.0 spec: §6.1.9
671      TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type);
672      TypeCode toTypeCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(toType));
673      if (fromTypeCode == TypeCode.Int64) {
674        long val = (long)rr.ConstantValue;
675        return val >= 0 && toTypeCode == TypeCode.UInt64;
676      } else if (fromTypeCode == TypeCode.Int32) {
677        object cv = rr.ConstantValue;
678        if (cv == null)
679          return false;
680        int val = (int)cv;
681        switch (toTypeCode) {
682          case TypeCode.SByte:
683            return val >= SByte.MinValue && val <= SByte.MaxValue;
684          case TypeCode.Byte:
685            return val >= Byte.MinValue && val <= Byte.MaxValue;
686          case TypeCode.Int16:
687            return val >= Int16.MinValue && val <= Int16.MaxValue;
688          case TypeCode.UInt16:
689            return val >= UInt16.MinValue && val <= UInt16.MaxValue;
690          case TypeCode.UInt32:
691            return val >= 0;
692          case TypeCode.UInt64:
693            return val >= 0;
694        }
695      }
696      return false;
697    }
698    #endregion
699   
700    #region Conversions involving type parameters
701    /// <summary>
702    /// Implicit conversions involving type parameters.
703    /// </summary>
704    bool ImplicitTypeParameterConversion(IType fromType, IType toType)
705    {
706      if (fromType.Kind != TypeKind.TypeParameter)
707        return false; // not a type parameter
708      if (fromType.IsReferenceType == true)
709        return false; // already handled by ImplicitReferenceConversion
710      return IsSubtypeOf(fromType, toType, 0);
711    }
712   
713    Conversion ExplicitTypeParameterConversion(IType fromType, IType toType)
714    {
715      if (toType.Kind == TypeKind.TypeParameter) {
716        // Explicit type parameter conversions that aren't also
717        // reference conversions are considered to be unboxing conversions
718        if (fromType.Kind == TypeKind.Interface || IsSubtypeOf(toType, fromType, 0))
719          return Conversion.UnboxingConversion;
720      } else {
721        if (fromType.Kind == TypeKind.TypeParameter && toType.Kind == TypeKind.Interface)
722          return Conversion.BoxingConversion;
723      }
724      return Conversion.None;
725    }
726    #endregion
727   
728    #region Pointer Conversions
729    bool ImplicitPointerConversion(IType fromType, IType toType)
730    {
731      // C# 4.0 spec: §18.4 Pointer conversions
732      if (fromType is PointerType && toType is PointerType && toType.ReflectionName == "System.Void*")
733        return true;
734      if (fromType.Kind == TypeKind.Null && toType is PointerType)
735        return true;
736      return false;
737    }
738   
739    bool ExplicitPointerConversion(IType fromType, IType toType)
740    {
741      // C# 4.0 spec: §18.4 Pointer conversions
742      if (fromType.Kind == TypeKind.Pointer) {
743        return toType.Kind == TypeKind.Pointer || IsIntegerType(toType);
744      } else {
745        return toType.Kind == TypeKind.Pointer && IsIntegerType(fromType);
746      }
747    }
748   
749    bool IsIntegerType(IType type)
750    {
751      TypeCode c = ReflectionHelper.GetTypeCode(type);
752      return c >= TypeCode.SByte && c <= TypeCode.UInt64;
753    }
754    #endregion
755   
756    #region User-Defined Conversions
757    /// <summary>
758    /// Gets whether type A is encompassed by type B.
759    /// </summary>
760    bool IsEncompassedBy(IType a, IType b)
761    {
762      return a.Kind != TypeKind.Interface && b.Kind != TypeKind.Interface && StandardImplicitConversion(a, b).IsValid;
763    }
764   
765    bool IsEncompassingOrEncompassedBy(IType a, IType b)
766    {
767      return a.Kind != TypeKind.Interface && b.Kind != TypeKind.Interface
768        && (StandardImplicitConversion(a, b).IsValid || StandardImplicitConversion(b, a).IsValid);
769    }
770
771    IType FindMostEncompassedType(IEnumerable<IType> candidates)
772    {
773      IType best = null;
774      foreach (var current in candidates) {
775        if (best == null || IsEncompassedBy(current, best))
776          best = current;
777        else if (!IsEncompassedBy(best, current))
778          return null;  // Ambiguous
779      }
780      return best;
781    }
782
783    IType FindMostEncompassingType(IEnumerable<IType> candidates)
784    {
785      IType best = null;
786      foreach (var current in candidates) {
787        if (best == null || IsEncompassedBy(best, current))
788          best = current;
789        else if (!IsEncompassedBy(current, best))
790          return null;  // Ambiguous
791      }
792      return best;
793    }
794
795    Conversion SelectOperator(IType mostSpecificSource, IType mostSpecificTarget, IList<OperatorInfo> operators, bool isImplicit, IType source, IType target)
796    {
797      var selected = operators.Where(op => op.SourceType.Equals(mostSpecificSource) && op.TargetType.Equals(mostSpecificTarget)).ToList();
798      if (selected.Count == 0)
799        return Conversion.None;
800
801      if (selected.Count == 1)
802        return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit, conversionBeforeUserDefinedOperator: ExplicitConversion(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversion(mostSpecificTarget, target));
803
804      int nNonLifted = selected.Count(s => !s.IsLifted);
805      if (nNonLifted == 1) {
806        var op = selected.First(s => !s.IsLifted);
807        return Conversion.UserDefinedConversion(op.Method, isLifted: op.IsLifted, isImplicit: isImplicit, conversionBeforeUserDefinedOperator: ExplicitConversion(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversion(mostSpecificTarget, target));
808      }
809     
810      return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit, isAmbiguous: true, conversionBeforeUserDefinedOperator: ExplicitConversion(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversion(mostSpecificTarget, target));
811    }
812
813    Conversion UserDefinedImplicitConversion(ResolveResult fromResult, IType fromType, IType toType)
814    {
815      // C# 4.0 spec §6.4.4 User-defined implicit conversions
816      var operators = GetApplicableConversionOperators(fromResult, fromType, toType, false);
817
818      if (operators.Count > 0) {
819        var mostSpecificSource = operators.Any(op => op.SourceType.Equals(fromType)) ? fromType : FindMostEncompassedType(operators.Select(op => op.SourceType));
820        if (mostSpecificSource == null)
821          return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: true, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
822        var mostSpecificTarget = operators.Any(op => op.TargetType.Equals(toType)) ? toType : FindMostEncompassingType(operators.Select(op => op.TargetType));
823        if (mostSpecificTarget == null) {
824          if (NullableType.IsNullable(toType))
825            return UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
826          else
827            return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: true, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
828        }
829
830        var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators, true, fromType, toType);
831        if (selected != Conversion.None) {
832          if (selected.IsLifted && NullableType.IsNullable(toType)) {
833            // Prefer A -> B -> B? over A -> A? -> B?
834            var other = UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
835            if (other != Conversion.None)
836              return other;
837          }
838          return selected;
839        }
840        else if (NullableType.IsNullable(toType))
841          return UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
842        else
843          return Conversion.None;
844      }
845      else {
846        return Conversion.None;
847      }
848    }
849   
850    Conversion UserDefinedExplicitConversion(ResolveResult fromResult, IType fromType, IType toType)
851    {
852      // C# 4.0 spec §6.4.5 User-defined implicit conversions
853      var operators = GetApplicableConversionOperators(fromResult, fromType, toType, true);
854      if (operators.Count > 0) {
855        IType mostSpecificSource;
856        if (operators.Any(op => op.SourceType.Equals(fromType))) {
857          mostSpecificSource = fromType;
858        } else {
859          var operatorsWithSourceEncompassingFromType = operators.Where(op => IsEncompassedBy(fromType, op.SourceType) || ImplicitConstantExpressionConversion(fromResult, NullableType.GetUnderlyingType(op.SourceType))).ToList();
860          if (operatorsWithSourceEncompassingFromType.Any())
861            mostSpecificSource = FindMostEncompassedType(operatorsWithSourceEncompassingFromType.Select(op => op.SourceType));
862          else
863            mostSpecificSource = FindMostEncompassingType(operators.Select(op => op.SourceType));
864        }
865        if (mostSpecificSource == null)
866          return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: false, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
867
868        IType mostSpecificTarget;
869        if (operators.Any(op => op.TargetType.Equals(toType)))
870          mostSpecificTarget = toType;
871        else if (operators.Any(op => IsEncompassedBy(op.TargetType, toType)))
872          mostSpecificTarget = FindMostEncompassingType(operators.Where(op => IsEncompassedBy(op.TargetType, toType)).Select(op => op.TargetType));
873        else
874          mostSpecificTarget = FindMostEncompassedType(operators.Select(op => op.TargetType));
875        if (mostSpecificTarget == null) {
876          if (NullableType.IsNullable(toType))
877            return UserDefinedExplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
878          else
879            return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: false, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
880        }
881
882        var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators, false, fromType, toType);
883        if (selected != Conversion.None) {
884          if (selected.IsLifted && NullableType.IsNullable(toType)) {
885            // Prefer A -> B -> B? over A -> A? -> B?
886            var other = UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
887            if (other != Conversion.None)
888              return other;
889          }
890          return selected;
891        }
892        else if (NullableType.IsNullable(toType))
893          return UserDefinedExplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
894        else if (NullableType.IsNullable(fromType))
895          return UserDefinedExplicitConversion(null, NullableType.GetUnderlyingType(fromType), toType); // A? -> A -> B
896        else
897          return Conversion.None;
898      }
899      else {
900        return Conversion.None;
901      }
902    }
903   
904    class OperatorInfo
905    {
906      public readonly IMethod Method;
907      public readonly IType SourceType;
908      public readonly IType TargetType;
909      public readonly bool IsLifted;
910     
911      public OperatorInfo(IMethod method, IType sourceType, IType targetType, bool isLifted)
912      {
913        this.Method = method;
914        this.SourceType = sourceType;
915        this.TargetType = targetType;
916        this.IsLifted = isLifted;
917      }
918    }
919   
920    List<OperatorInfo> GetApplicableConversionOperators(ResolveResult fromResult, IType fromType, IType toType, bool isExplicit)
921    {
922      // Find the candidate operators:
923      Predicate<IUnresolvedMethod> opFilter;
924      if (isExplicit)
925        opFilter = m => m.IsStatic && m.IsOperator && (m.Name == "op_Explicit" || m.Name == "op_Implicit") && m.Parameters.Count == 1;
926      else
927        opFilter = m => m.IsStatic && m.IsOperator && m.Name == "op_Implicit" && m.Parameters.Count == 1;
928     
929      var operators = NullableType.GetUnderlyingType(fromType).GetMethods(opFilter)
930        .Concat(NullableType.GetUnderlyingType(toType).GetMethods(opFilter)).Distinct();
931      // Determine whether one of them is applicable:
932      List<OperatorInfo> result = new List<OperatorInfo>();
933      foreach (IMethod op in operators) {
934        IType sourceType = op.Parameters[0].Type;
935        IType targetType = op.ReturnType;
936        // Try if the operator is applicable:
937        bool isApplicable;
938        if (isExplicit) {
939          isApplicable = (IsEncompassingOrEncompassedBy(fromType, sourceType) || ImplicitConstantExpressionConversion(fromResult, sourceType))
940            && IsEncompassingOrEncompassedBy(targetType, toType);
941        } else {
942          isApplicable = (IsEncompassedBy(fromType, sourceType) || ImplicitConstantExpressionConversion(fromResult, sourceType))
943            && IsEncompassedBy(targetType, toType);
944        }
945        // Try if the operator is applicable in lifted form:
946        if (isApplicable) {
947          result.Add(new OperatorInfo(op, sourceType, targetType, false));
948        }
949        if (NullableType.IsNonNullableValueType(sourceType)) {
950          // An operator can be applicable in both lifted and non-lifted form in case of explicit conversions
951          IType liftedSourceType = NullableType.Create(compilation, sourceType);
952          IType liftedTargetType = NullableType.IsNonNullableValueType(targetType) ? NullableType.Create(compilation, targetType) : targetType;
953          if (isExplicit) {
954            isApplicable = IsEncompassingOrEncompassedBy(fromType, liftedSourceType)
955              && IsEncompassingOrEncompassedBy(liftedTargetType, toType);
956          } else {
957            isApplicable = IsEncompassedBy(fromType, liftedSourceType) && IsEncompassedBy(liftedTargetType, toType);
958          }
959
960          if (isApplicable) {
961            result.Add(new OperatorInfo(op, liftedSourceType, liftedTargetType, true));
962          }
963        }
964      }
965      return result;
966    }
967    #endregion
968   
969    #region AnonymousFunctionConversion
970    Conversion AnonymousFunctionConversion(ResolveResult resolveResult, IType toType)
971    {
972      // C# 5.0 spec §6.5 Anonymous function conversions
973      LambdaResolveResult f = resolveResult as LambdaResolveResult;
974      if (f == null)
975        return Conversion.None;
976      if (!f.IsAnonymousMethod) {
977        // It's a lambda, so conversions to expression trees exist
978        // (even if the conversion leads to a compile-time error, e.g. for statement lambdas)
979        toType = UnpackExpressionTreeType(toType);
980      }
981      IMethod d = toType.GetDelegateInvokeMethod();
982      if (d == null)
983        return Conversion.None;
984     
985      IType[] dParamTypes = new IType[d.Parameters.Count];
986      for (int i = 0; i < dParamTypes.Length; i++) {
987        dParamTypes[i] = d.Parameters[i].Type;
988      }
989      IType dReturnType = d.ReturnType;
990     
991      if (f.HasParameterList) {
992        // If F contains an anonymous-function-signature, then D and F have the same number of parameters.
993        if (d.Parameters.Count != f.Parameters.Count)
994          return Conversion.None;
995       
996        if (f.IsImplicitlyTyped) {
997          // If F has an implicitly typed parameter list, D has no ref or out parameters.
998          foreach (IParameter p in d.Parameters) {
999            if (p.IsOut || p.IsRef)
1000              return Conversion.None;
1001          }
1002        } else {
1003          // If F has an explicitly typed parameter list, each parameter in D has the same type
1004          // and modifiers as the corresponding parameter in F.
1005          for (int i = 0; i < f.Parameters.Count; i++) {
1006            IParameter pD = d.Parameters[i];
1007            IParameter pF = f.Parameters[i];
1008            if (pD.IsRef != pF.IsRef || pD.IsOut != pF.IsOut)
1009              return Conversion.None;
1010            if (!IdentityConversion(dParamTypes[i], pF.Type))
1011              return Conversion.None;
1012          }
1013        }
1014      } else {
1015        // If F does not contain an anonymous-function-signature, then D may have zero or more parameters of any
1016        // type, as long as no parameter of D has the out parameter modifier.
1017        foreach (IParameter p in d.Parameters) {
1018          if (p.IsOut)
1019            return Conversion.None;
1020        }
1021      }
1022     
1023      return f.IsValid(dParamTypes, dReturnType, this);
1024    }
1025
1026    static IType UnpackExpressionTreeType(IType type)
1027    {
1028      ParameterizedType pt = type as ParameterizedType;
1029      if (pt != null && pt.TypeParameterCount == 1 && pt.Name == "Expression" && pt.Namespace == "System.Linq.Expressions") {
1030        return pt.GetTypeArgument(0);
1031      } else {
1032        return type;
1033      }
1034    }
1035    #endregion
1036   
1037    #region MethodGroupConversion
1038    Conversion MethodGroupConversion(ResolveResult resolveResult, IType toType)
1039    {
1040      // C# 4.0 spec §6.6 Method group conversions
1041      MethodGroupResolveResult rr = resolveResult as MethodGroupResolveResult;
1042      if (rr == null)
1043        return Conversion.None;
1044      IMethod invoke = toType.GetDelegateInvokeMethod();
1045      if (invoke == null)
1046        return Conversion.None;
1047     
1048      ResolveResult[] args = new ResolveResult[invoke.Parameters.Count];
1049      for (int i = 0; i < args.Length; i++) {
1050        IParameter param = invoke.Parameters[i];
1051        IType parameterType = param.Type;
1052        if ((param.IsRef || param.IsOut) && parameterType.Kind == TypeKind.ByReference) {
1053          parameterType = ((ByReferenceType)parameterType).ElementType;
1054          args[i] = new ByReferenceResolveResult(parameterType, param.IsOut);
1055        } else {
1056          args[i] = new ResolveResult(parameterType);
1057        }
1058      }
1059      var or = rr.PerformOverloadResolution(compilation, args, allowExpandingParams: false, allowOptionalParameters: false, conversions: this);
1060      if (or.FoundApplicableCandidate) {
1061        IMethod method = (IMethod)or.GetBestCandidateWithSubstitutedTypeArguments();
1062        var thisRR = rr.TargetResult as ThisResolveResult;
1063        bool isVirtual = method.IsOverridable && !(thisRR != null && thisRR.CausesNonVirtualInvocation);
1064        bool isValid = !or.IsAmbiguous && IsDelegateCompatible(method, invoke, or.IsExtensionMethodInvocation);
1065        bool delegateCapturesFirstArgument = or.IsExtensionMethodInvocation || !method.IsStatic;
1066        if (isValid)
1067          return Conversion.MethodGroupConversion(method, isVirtual, delegateCapturesFirstArgument);
1068        else
1069          return Conversion.InvalidMethodGroupConversion(method, isVirtual, delegateCapturesFirstArgument);
1070      } else {
1071        return Conversion.None;
1072      }
1073    }
1074   
1075    /// <summary>
1076    /// Gets whether a <paramref name="method"/> is compatible with a delegate type.
1077    /// §15.2 Delegate compatibility
1078    /// </summary>
1079    /// <param name="method">The method to test for compatibility</param>
1080    /// <param name="delegateType">The delegate type</param>
1081    public bool IsDelegateCompatible(IMethod method, IType delegateType)
1082    {
1083      if (method == null)
1084        throw new ArgumentNullException("method");
1085      if (delegateType == null)
1086        throw new ArgumentNullException("delegateType");
1087      IMethod invoke = delegateType.GetDelegateInvokeMethod();
1088      if (invoke == null)
1089        return false;
1090      return IsDelegateCompatible(method, invoke, false);
1091    }
1092   
1093    /// <summary>
1094    /// Gets whether a method <paramref name="m"/> is compatible with a delegate type.
1095    /// §15.2 Delegate compatibility
1096    /// </summary>
1097    /// <param name="m">The method to test for compatibility</param>
1098    /// <param name="invoke">The invoke method of the delegate</param>
1099    /// <param name="isExtensionMethodInvocation">Gets whether m is accessed using extension method syntax.
1100    /// If this parameter is true, the first parameter of <paramref name="m"/> will be ignored.</param>
1101    bool IsDelegateCompatible(IMethod m, IMethod invoke, bool isExtensionMethodInvocation)
1102    {
1103      if (m == null)
1104        throw new ArgumentNullException("m");
1105      if (invoke == null)
1106        throw new ArgumentNullException("invoke");
1107      int firstParameterInM = isExtensionMethodInvocation ? 1 : 0;
1108      if (m.Parameters.Count - firstParameterInM != invoke.Parameters.Count)
1109        return false;
1110      for (int i = 0; i < invoke.Parameters.Count; i++) {
1111        var pm = m.Parameters[firstParameterInM + i];
1112        var pd = invoke.Parameters[i];
1113        // ret/out must match
1114        if (pm.IsRef != pd.IsRef || pm.IsOut != pd.IsOut)
1115          return false;
1116        if (pm.IsRef || pm.IsOut) {
1117          // ref/out parameters must have same types
1118          if (!pm.Type.Equals(pd.Type))
1119            return false;
1120        } else {
1121          // non-ref/out parameters must have an identity or reference conversion from pd to pm
1122          if (!IdentityConversion(pd.Type, pm.Type) && !IsImplicitReferenceConversion(pd.Type, pm.Type))
1123            return false;
1124        }
1125      }
1126      // check return type compatibility
1127      return IdentityConversion(m.ReturnType, invoke.ReturnType)
1128        || IsImplicitReferenceConversion(m.ReturnType, invoke.ReturnType);
1129    }
1130    #endregion
1131   
1132    #region BetterConversion
1133    /// <summary>
1134    /// Gets the better conversion (C# 4.0 spec, §7.5.3.3)
1135    /// </summary>
1136    /// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
1137    public int BetterConversion(ResolveResult resolveResult, IType t1, IType t2)
1138    {
1139      LambdaResolveResult lambda = resolveResult as LambdaResolveResult;
1140      if (lambda != null) {
1141        if (!lambda.IsAnonymousMethod) {
1142          t1 = UnpackExpressionTreeType(t1);
1143          t2 = UnpackExpressionTreeType(t2);
1144        }
1145        IMethod m1 = t1.GetDelegateInvokeMethod();
1146        IMethod m2 = t2.GetDelegateInvokeMethod();
1147        if (m1 == null || m2 == null)
1148          return 0;
1149        if (m1.Parameters.Count != m2.Parameters.Count)
1150          return 0;
1151        IType[] parameterTypes = new IType[m1.Parameters.Count];
1152        for (int i = 0; i < parameterTypes.Length; i++) {
1153          parameterTypes[i] = m1.Parameters[i].Type;
1154          if (!parameterTypes[i].Equals(m2.Parameters[i].Type))
1155            return 0;
1156        }
1157        if (lambda.HasParameterList && parameterTypes.Length != lambda.Parameters.Count)
1158          return 0;
1159       
1160        IType ret1 = m1.ReturnType;
1161        IType ret2 = m2.ReturnType;
1162        if (ret1.Kind == TypeKind.Void && ret2.Kind != TypeKind.Void)
1163          return 2;
1164        if (ret1.Kind != TypeKind.Void && ret2.Kind == TypeKind.Void)
1165          return 1;
1166       
1167        IType inferredRet = lambda.GetInferredReturnType(parameterTypes);
1168        int r = BetterConversion(inferredRet, ret1, ret2);
1169        if (r == 0 && lambda.IsAsync) {
1170          ret1 = UnpackTask(ret1);
1171          ret2 = UnpackTask(ret2);
1172          inferredRet = UnpackTask(inferredRet);
1173          if (ret1 != null && ret2 != null && inferredRet != null)
1174            r = BetterConversion(inferredRet, ret1, ret2);
1175        }
1176        return r;
1177      } else {
1178        return BetterConversion(resolveResult.Type, t1, t2);
1179      }
1180    }
1181   
1182    /// <summary>
1183    /// Unpacks the generic Task[T]. Returns null if the input is not Task[T].
1184    /// </summary>
1185    static IType UnpackTask(IType type)
1186    {
1187      ParameterizedType pt = type as ParameterizedType;
1188      if (pt != null && pt.TypeParameterCount == 1 && pt.Name == "Task" && pt.Namespace == "System.Threading.Tasks") {
1189        return pt.GetTypeArgument(0);
1190      }
1191      return null;
1192    }
1193   
1194    /// <summary>
1195    /// Gets the better conversion (C# 4.0 spec, §7.5.3.4)
1196    /// </summary>
1197    /// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
1198    public int BetterConversion(IType s, IType t1, IType t2)
1199    {
1200      bool ident1 = IdentityConversion(s, t1);
1201      bool ident2 = IdentityConversion(s, t2);
1202      if (ident1 && !ident2)
1203        return 1;
1204      if (ident2 && !ident1)
1205        return 2;
1206      return BetterConversionTarget(t1, t2);
1207    }
1208   
1209    /// <summary>
1210    /// Gets the better conversion target (C# 4.0 spec, §7.5.3.5)
1211    /// </summary>
1212    /// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
1213    int BetterConversionTarget(IType t1, IType t2)
1214    {
1215      bool t1To2 = ImplicitConversion(t1, t2).IsValid;
1216      bool t2To1 = ImplicitConversion(t2, t1).IsValid;
1217      if (t1To2 && !t2To1)
1218        return 1;
1219      if (t2To1 && !t1To2)
1220        return 2;
1221      TypeCode t1Code = ReflectionHelper.GetTypeCode(t1);
1222      TypeCode t2Code = ReflectionHelper.GetTypeCode(t2);
1223      if (IsBetterIntegralType(t1Code, t2Code))
1224        return 1;
1225      if (IsBetterIntegralType(t2Code, t1Code))
1226        return 2;
1227      return 0;
1228    }
1229   
1230    bool IsBetterIntegralType(TypeCode t1, TypeCode t2)
1231    {
1232      // signed types are better than unsigned types
1233      switch (t1) {
1234        case TypeCode.SByte:
1235          return t2 == TypeCode.Byte || t2 == TypeCode.UInt16 || t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64;
1236        case TypeCode.Int16:
1237          return t2 == TypeCode.UInt16 || t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64;
1238        case TypeCode.Int32:
1239          return t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64;
1240        case TypeCode.Int64:
1241          return t2 == TypeCode.UInt64;
1242        default:
1243          return false;
1244      }
1245    }
1246    #endregion
1247  }
1248}
Note: See TracBrowser for help on using the repository browser.