~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/nrefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
Import upstream version 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
19
using System;
 
20
using System.Collections.Concurrent;
 
21
using System.Collections.Generic;
 
22
using System.Diagnostics;
 
23
using System.Linq;
 
24
using System.Threading;
 
25
using ICSharpCode.NRefactory.Semantics;
 
26
using ICSharpCode.NRefactory.TypeSystem;
 
27
using ICSharpCode.NRefactory.Utils;
 
28
 
 
29
namespace 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 (!dParamTypes[i].Equals(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
                                if (isValid)
 
1066
                                        return Conversion.MethodGroupConversion(method, isVirtual);
 
1067
                                else
 
1068
                                        return Conversion.InvalidMethodGroupConversion(method, isVirtual);
 
1069
                        } else {
 
1070
                                return Conversion.None;
 
1071
                        }
 
1072
                }
 
1073
                
 
1074
                /// <summary>
 
1075
                /// Gets whether a method <paramref name="m"/> is compatible with a delegate type.
 
1076
                /// §15.2 Delegate compatibility
 
1077
                /// </summary>
 
1078
                /// <param name="m">The method to test for compatibility</param>
 
1079
                /// <param name="invoke">The invoke method of the delegate</param>
 
1080
                /// <param name="isExtensionMethodInvocation">Gets whether m is accessed using extension method syntax.
 
1081
                /// If this parameter is true, the first parameter of <paramref name="m"/> will be ignored.</param>
 
1082
                bool IsDelegateCompatible(IMethod m, IMethod invoke, bool isExtensionMethodInvocation)
 
1083
                {
 
1084
                        if (m == null)
 
1085
                                throw new ArgumentNullException("m");
 
1086
                        if (invoke == null)
 
1087
                                throw new ArgumentNullException("invoke");
 
1088
                        int firstParameterInM = isExtensionMethodInvocation ? 1 : 0;
 
1089
                        if (m.Parameters.Count - firstParameterInM != invoke.Parameters.Count)
 
1090
                                return false;
 
1091
                        for (int i = 0; i < invoke.Parameters.Count; i++) {
 
1092
                                var pm = m.Parameters[firstParameterInM + i];
 
1093
                                var pd = invoke.Parameters[i];
 
1094
                                // ret/out must match
 
1095
                                if (pm.IsRef != pd.IsRef || pm.IsOut != pd.IsOut)
 
1096
                                        return false;
 
1097
                                if (pm.IsRef || pm.IsOut) {
 
1098
                                        // ref/out parameters must have same types
 
1099
                                        if (!pm.Type.Equals(pd.Type))
 
1100
                                                return false;
 
1101
                                } else {
 
1102
                                        // non-ref/out parameters must have an identity or reference conversion from pd to pm
 
1103
                                        if (!IdentityConversion(pd.Type, pm.Type) && !IsImplicitReferenceConversion(pd.Type, pm.Type))
 
1104
                                                return false;
 
1105
                                }
 
1106
                        }
 
1107
                        // check return type compatibility
 
1108
                        return IdentityConversion(m.ReturnType, invoke.ReturnType)
 
1109
                                || IsImplicitReferenceConversion(m.ReturnType, invoke.ReturnType);
 
1110
                }
 
1111
                #endregion
 
1112
                
 
1113
                #region BetterConversion
 
1114
                /// <summary>
 
1115
                /// Gets the better conversion (C# 4.0 spec, §7.5.3.3)
 
1116
                /// </summary>
 
1117
                /// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
 
1118
                public int BetterConversion(ResolveResult resolveResult, IType t1, IType t2)
 
1119
                {
 
1120
                        LambdaResolveResult lambda = resolveResult as LambdaResolveResult;
 
1121
                        if (lambda != null) {
 
1122
                                if (!lambda.IsAnonymousMethod) {
 
1123
                                        t1 = UnpackExpressionTreeType(t1);
 
1124
                                        t2 = UnpackExpressionTreeType(t2);
 
1125
                                }
 
1126
                                IMethod m1 = t1.GetDelegateInvokeMethod();
 
1127
                                IMethod m2 = t2.GetDelegateInvokeMethod();
 
1128
                                if (m1 == null || m2 == null)
 
1129
                                        return 0;
 
1130
                                int r = BetterConversionTarget(t1, t2);
 
1131
                                if (r != 0)
 
1132
                                        return r;
 
1133
                                if (m1.Parameters.Count != m2.Parameters.Count)
 
1134
                                        return 0;
 
1135
                                IType[] parameterTypes = new IType[m1.Parameters.Count];
 
1136
                                for (int i = 0; i < parameterTypes.Length; i++) {
 
1137
                                        parameterTypes[i] = m1.Parameters[i].Type;
 
1138
                                        if (!parameterTypes[i].Equals(m2.Parameters[i].Type))
 
1139
                                                return 0;
 
1140
                                }
 
1141
                                if (lambda.HasParameterList && parameterTypes.Length != lambda.Parameters.Count)
 
1142
                                        return 0;
 
1143
                                
 
1144
                                IType ret1 = m1.ReturnType;
 
1145
                                IType ret2 = m2.ReturnType;
 
1146
                                if (ret1.Kind == TypeKind.Void && ret2.Kind != TypeKind.Void)
 
1147
                                        return 2;
 
1148
                                if (ret1.Kind != TypeKind.Void && ret2.Kind == TypeKind.Void)
 
1149
                                        return 1;
 
1150
                                
 
1151
                                IType inferredRet = lambda.GetInferredReturnType(parameterTypes);
 
1152
                                r = BetterConversion(inferredRet, ret1, ret2);
 
1153
                                if (r == 0 && lambda.IsAsync) {
 
1154
                                        ret1 = UnpackTask(ret1);
 
1155
                                        ret2 = UnpackTask(ret2);
 
1156
                                        inferredRet = UnpackTask(inferredRet);
 
1157
                                        if (ret1 != null && ret2 != null && inferredRet != null)
 
1158
                                                r = BetterConversion(inferredRet, ret1, ret2);
 
1159
                                }
 
1160
                                return r;
 
1161
                        } else {
 
1162
                                return BetterConversion(resolveResult.Type, t1, t2);
 
1163
                        }
 
1164
                }
 
1165
                
 
1166
                /// <summary>
 
1167
                /// Unpacks the generic Task[T]. Returns null if the input is not Task[T].
 
1168
                /// </summary>
 
1169
                static IType UnpackTask(IType type)
 
1170
                {
 
1171
                        ParameterizedType pt = type as ParameterizedType;
 
1172
                        if (pt != null && pt.TypeParameterCount == 1 && pt.Name == "Task" && pt.Namespace == "System.Threading.Tasks") {
 
1173
                                return pt.GetTypeArgument(0);
 
1174
                        }
 
1175
                        return null;
 
1176
                }
 
1177
                
 
1178
                /// <summary>
 
1179
                /// Gets the better conversion (C# 4.0 spec, §7.5.3.4)
 
1180
                /// </summary>
 
1181
                /// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
 
1182
                public int BetterConversion(IType s, IType t1, IType t2)
 
1183
                {
 
1184
                        bool ident1 = IdentityConversion(s, t1);
 
1185
                        bool ident2 = IdentityConversion(s, t2);
 
1186
                        if (ident1 && !ident2)
 
1187
                                return 1;
 
1188
                        if (ident2 && !ident1)
 
1189
                                return 2;
 
1190
                        return BetterConversionTarget(t1, t2);
 
1191
                }
 
1192
                
 
1193
                /// <summary>
 
1194
                /// Gets the better conversion target (C# 4.0 spec, §7.5.3.5)
 
1195
                /// </summary>
 
1196
                /// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
 
1197
                int BetterConversionTarget(IType t1, IType t2)
 
1198
                {
 
1199
                        bool t1To2 = ImplicitConversion(t1, t2).IsValid;
 
1200
                        bool t2To1 = ImplicitConversion(t2, t1).IsValid;
 
1201
                        if (t1To2 && !t2To1)
 
1202
                                return 1;
 
1203
                        if (t2To1 && !t1To2)
 
1204
                                return 2;
 
1205
                        TypeCode t1Code = ReflectionHelper.GetTypeCode(t1);
 
1206
                        TypeCode t2Code = ReflectionHelper.GetTypeCode(t2);
 
1207
                        if (IsBetterIntegralType(t1Code, t2Code))
 
1208
                                return 1;
 
1209
                        if (IsBetterIntegralType(t2Code, t1Code))
 
1210
                                return 2;
 
1211
                        return 0;
 
1212
                }
 
1213
                
 
1214
                bool IsBetterIntegralType(TypeCode t1, TypeCode t2)
 
1215
                {
 
1216
                        // signed types are better than unsigned types
 
1217
                        switch (t1) {
 
1218
                                case TypeCode.SByte:
 
1219
                                        return t2 == TypeCode.Byte || t2 == TypeCode.UInt16 || t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64;
 
1220
                                case TypeCode.Int16:
 
1221
                                        return t2 == TypeCode.UInt16 || t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64;
 
1222
                                case TypeCode.Int32:
 
1223
                                        return t2 == TypeCode.UInt32 || t2 == TypeCode.UInt64;
 
1224
                                case TypeCode.Int64:
 
1225
                                        return t2 == TypeCode.UInt64;
 
1226
                                default:
 
1227
                                        return false;
 
1228
                        }
 
1229
                }
 
1230
                #endregion
 
1231
        }
 
1232
}