1
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
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:
9
// The above copyright notice and this permission notice shall be included in all copies or
10
// substantial portions of the Software.
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.
20
using System.Collections.Concurrent;
21
using System.Collections.Generic;
22
using System.Diagnostics;
24
using System.Threading;
25
using ICSharpCode.NRefactory.Semantics;
26
using ICSharpCode.NRefactory.TypeSystem;
27
using ICSharpCode.NRefactory.Utils;
29
namespace ICSharpCode.NRefactory.CSharp.Resolver
32
/// Contains logic that determines whether an implicit conversion exists between two types.
35
/// This class is thread-safe.
37
public sealed class CSharpConversions
39
readonly ConcurrentDictionary<TypePair, Conversion> implicitConversionCache = new ConcurrentDictionary<TypePair, Conversion>();
40
readonly ICompilation compilation;
41
readonly IType objectType;
43
public CSharpConversions(ICompilation compilation)
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);
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.
56
public static CSharpConversions Get(ICompilation compilation)
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));
68
#region TypePair (for caching)
69
struct TypePair : IEquatable<TypePair>
71
public readonly IType FromType;
72
public readonly IType ToType;
74
public TypePair(IType fromType, IType toType)
76
Debug.Assert(fromType != null && toType != null);
77
this.FromType = fromType;
81
public override bool Equals(object obj)
83
return (obj is TypePair) && Equals((TypePair)obj);
86
public bool Equals(TypePair other)
88
return object.Equals(this.FromType, other.FromType) && object.Equals(this.ToType, other.ToType);
91
public override int GetHashCode()
94
return 1000000007 * FromType.GetHashCode() + 1000000009 * ToType.GetHashCode();
100
#region ImplicitConversion
101
private Conversion ImplicitConversion(ResolveResult resolveResult, IType toType, bool allowUserDefined)
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;
116
c = ImplicitConversion(resolveResult.Type, toType, allowUserDefined);
117
if (c != Conversion.None) return c;
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);
127
private Conversion ImplicitConversion(IType fromType, IType toType, bool allowUserDefined)
130
var c = StandardImplicitConversion(fromType, toType);
131
if (c == Conversion.None && allowUserDefined) {
132
c = UserDefinedImplicitConversion(null, fromType, toType);
137
public Conversion ImplicitConversion(ResolveResult resolveResult, IType toType)
139
if (resolveResult == null)
140
throw new ArgumentNullException("resolveResult");
141
return ImplicitConversion(resolveResult, toType, allowUserDefined: true);
144
public Conversion ImplicitConversion(IType fromType, IType toType)
146
if (fromType == null)
147
throw new ArgumentNullException("fromType");
149
throw new ArgumentNullException("toType");
151
TypePair pair = new TypePair(fromType, toType);
153
if (implicitConversionCache.TryGetValue(pair, out c))
156
c = ImplicitConversion(fromType, toType, allowUserDefined: true);
158
implicitConversionCache[pair] = c;
162
public Conversion StandardImplicitConversion(IType fromType, IType toType)
164
if (fromType == null)
165
throw new ArgumentNullException("fromType");
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)
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;
187
if (ImplicitPointerConversion(fromType, toType))
188
return Conversion.ImplicitPointerConversion;
189
return Conversion.None;
193
/// Gets whether the type 'fromType' is convertible to 'toType'
194
/// using one of the conversions allowed when satisying constraints (§4.4.4)
196
public bool IsConstraintConvertible(IType fromType, IType toType)
198
if (fromType == null)
199
throw new ArgumentNullException("fromType");
201
throw new ArgumentNullException("toType");
203
if (IdentityConversion(fromType, toType))
205
if (ImplicitReferenceConversion(fromType, toType, 0))
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))
213
if (IsBoxingConversion(fromType, toType))
216
if (ImplicitTypeParameterConversion(fromType, toType))
222
#region ExplicitConversion
223
public Conversion ExplicitConversion(ResolveResult resolveResult, IType toType)
225
if (resolveResult == null)
226
throw new ArgumentNullException("resolveResult");
228
throw new ArgumentNullException("toType");
230
if (resolveResult.Type.Kind == TypeKind.Dynamic)
231
return Conversion.ExplicitDynamicConversion;
232
Conversion c = ImplicitConversion(resolveResult, toType, allowUserDefined: false);
233
if (c != Conversion.None)
235
c = ExplicitConversionImpl(resolveResult.Type, toType);
236
if (c != Conversion.None)
238
return UserDefinedExplicitConversion(resolveResult, resolveResult.Type, toType);
241
public Conversion ExplicitConversion(IType fromType, IType toType)
243
if (fromType == null)
244
throw new ArgumentNullException("fromType");
246
throw new ArgumentNullException("toType");
248
Conversion c = ImplicitConversion(fromType, toType, allowUserDefined: false);
249
if (c != Conversion.None)
251
c = ExplicitConversionImpl(fromType, toType);
252
if (c != Conversion.None)
254
return UserDefinedExplicitConversion(null, fromType, toType);
257
Conversion ExplicitConversionImpl(IType fromType, IType toType)
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)
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)
275
if (ExplicitPointerConversion(fromType, toType))
276
return Conversion.ExplicitPointerConversion;
277
return Conversion.None;
281
#region Identity Conversion
283
/// Gets whether there is an identity conversion from <paramref name="fromType"/> to <paramref name="toType"/>
285
public bool IdentityConversion(IType fromType, IType toType)
287
// C# 4.0 spec: §6.1.1
288
return fromType.AcceptVisitor(dynamicErasure).Equals(toType.AcceptVisitor(dynamicErasure));
291
readonly DynamicErasure dynamicErasure;
293
sealed class DynamicErasure : TypeVisitor
295
readonly IType objectType;
297
public DynamicErasure(CSharpConversions conversions)
299
this.objectType = conversions.objectType;
302
public override IType VisitOtherType(IType type)
304
if (type.Kind == TypeKind.Dynamic)
307
return base.VisitOtherType(type);
312
#region Numeric Conversions
313
static readonly bool[,] implicitNumericConversionLookup = {
314
// to: short ushort int uint long ulong
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 },
325
bool ImplicitNumericConversion(IType fromType, IType toType)
327
// C# 4.0 spec: §6.1.2
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;
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];
344
bool IsNumericType(IType type)
346
TypeCode c = ReflectionHelper.GetTypeCode(type);
347
return c >= TypeCode.Char && c <= TypeCode.Decimal;
350
bool AnyNumericConversion(IType fromType, IType toType)
352
// C# 4.0 spec: §6.1.2 + §6.2.1
353
return IsNumericType(fromType) && IsNumericType(toType);
357
#region Enumeration Conversions
358
Conversion ImplicitEnumerationConversion(ResolveResult rr, IType toType)
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));
368
return Conversion.None;
371
bool ExplicitEnumerationConversion(IType fromType, IType toType)
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;
383
#region Nullable Conversions
384
Conversion ImplicitNullableConversion(IType fromType, IType toType)
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;
395
return Conversion.None;
398
Conversion ExplicitNullableConversion(IType fromType, IType toType)
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);
411
return Conversion.None;
415
#region Null Literal Conversion
416
bool NullLiteralConversion(IType fromType, IType toType)
418
// C# 4.0 spec: §6.1.5
419
if (fromType.Kind == TypeKind.Null) {
420
return NullableType.IsNullable(toType) || toType.IsReferenceType == true;
427
#region Implicit Reference Conversion
428
public bool IsImplicitReferenceConversion(IType fromType, IType toType)
430
return ImplicitReferenceConversion(fromType, toType, 0);
433
bool ImplicitReferenceConversion(IType fromType, IType toType, int subtypeCheckNestingDepth)
435
// C# 4.0 spec: §6.1.6
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))
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);
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);
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);
464
// now comes the hard part: traverse the inheritance chain and figure out generics+variance
465
return IsSubtypeOf(fromType, toType, subtypeCheckNestingDepth);
469
/// For IList{T}, ICollection{T}, IEnumerable{T} and IReadOnlyList{T}, returns T.
470
/// Otherwise, returns null.
472
IType UnpackGenericArrayInterface(IType interfaceType)
474
ParameterizedType pt = interfaceType as ParameterizedType;
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);
484
// Determines whether s is a subtype of t.
485
// Helper method used for ImplicitReferenceConversion, BoxingConversion and ImplicitTypeParameterConversion
487
bool IsSubtypeOf(IType s, IType t, int subtypeCheckNestingDepth)
489
// conversion to dynamic + object are always possible
490
if (t.Kind == TypeKind.Dynamic || t.Equals(objectType))
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.
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.
501
// let GetAllBaseTypes do the work for us
502
foreach (IType baseType in s.GetAllBaseTypes()) {
503
if (IdentityOrVarianceConversion(baseType, t, subtypeCheckNestingDepth + 1))
509
bool IdentityOrVarianceConversion(IType s, IType t, int subtypeCheckNestingDepth)
511
ITypeDefinition def = s.GetDefinition();
513
if (!def.Equals(t.GetDefinition()))
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))
524
ITypeParameter xi = def.TypeParameters[i];
525
switch (xi.Variance) {
526
case VarianceModifier.Covariant:
527
if (!ImplicitReferenceConversion(si, ti, subtypeCheckNestingDepth))
530
case VarianceModifier.Contravariant:
531
if (!ImplicitReferenceConversion(ti, si, subtypeCheckNestingDepth))
538
} else if (ps != null || pt != null) {
539
return false; // only of of them is parameterized, or counts don't match? -> not valid conversion
543
// not type definitions? we still need to check for equal types (e.g. s and t might be type parameters)
549
#region Explicit Reference Conversion
550
bool ExplicitReferenceConversion(IType fromType, IType toType)
552
// C# 4.0 spec: §6.2.4
554
// test that the types are reference types:
555
if (toType.IsReferenceType != true)
557
if (fromType.IsReferenceType != true) {
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);
566
if (toType.Kind == TypeKind.Array) {
567
ArrayType toArray = (ArrayType)toType;
568
if (fromType.Kind == TypeKind.Array) {
570
ArrayType fromArray = (ArrayType)fromType;
571
if (fromArray.Dimensions != toArray.Dimensions)
573
return ExplicitReferenceConversion(fromArray.ElementType, toArray.ElementType);
575
IType fromTypeArgument = UnpackGenericArrayInterface(fromType);
576
if (fromTypeArgument != null && toArray.Dimensions == 1) {
577
return ExplicitReferenceConversion(fromTypeArgument, toArray.ElementType)
578
|| IdentityConversion(fromTypeArgument, toArray.ElementType);
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);
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()))
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;
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))
605
ITypeParameter xi = def.TypeParameters[i];
606
switch (xi.Variance) {
607
case VarianceModifier.Covariant:
608
if (!ExplicitReferenceConversion(si, ti))
611
case VarianceModifier.Contravariant:
612
if (!(si.IsReferenceType == true && ti.IsReferenceType == 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);
627
if (fromType.Kind == TypeKind.Interface || toType.Kind == TypeKind.Interface)
630
return IsImplicitReferenceConversion(toType, fromType)
631
|| IsImplicitReferenceConversion(fromType, toType);
635
bool IsSealedReferenceType(IType type)
637
TypeKind kind = type.Kind;
638
return kind == TypeKind.Class && type.GetDefinition().IsSealed
639
|| kind == TypeKind.Delegate || kind == TypeKind.Anonymous;
643
#region Boxing Conversions
644
public bool IsBoxingConversion(IType fromType, IType toType)
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);
654
bool UnboxingConversion(IType fromType, IType toType)
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);
665
#region Implicit Constant-Expression Conversion
666
bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType)
668
if (rr == null || !rr.IsCompileTimeConstant)
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;
681
switch (toTypeCode) {
683
return val >= SByte.MinValue && val <= SByte.MaxValue;
685
return val >= Byte.MinValue && val <= Byte.MaxValue;
687
return val >= Int16.MinValue && val <= Int16.MaxValue;
688
case TypeCode.UInt16:
689
return val >= UInt16.MinValue && val <= UInt16.MaxValue;
690
case TypeCode.UInt32:
692
case TypeCode.UInt64:
700
#region Conversions involving type parameters
702
/// Implicit conversions involving type parameters.
704
bool ImplicitTypeParameterConversion(IType fromType, IType toType)
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);
713
Conversion ExplicitTypeParameterConversion(IType fromType, IType toType)
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;
721
if (fromType.Kind == TypeKind.TypeParameter && toType.Kind == TypeKind.Interface)
722
return Conversion.BoxingConversion;
724
return Conversion.None;
728
#region Pointer Conversions
729
bool ImplicitPointerConversion(IType fromType, IType toType)
731
// C# 4.0 spec: §18.4 Pointer conversions
732
if (fromType is PointerType && toType is PointerType && toType.ReflectionName == "System.Void*")
734
if (fromType.Kind == TypeKind.Null && toType is PointerType)
739
bool ExplicitPointerConversion(IType fromType, IType toType)
741
// C# 4.0 spec: §18.4 Pointer conversions
742
if (fromType.Kind == TypeKind.Pointer) {
743
return toType.Kind == TypeKind.Pointer || IsIntegerType(toType);
745
return toType.Kind == TypeKind.Pointer && IsIntegerType(fromType);
749
bool IsIntegerType(IType type)
751
TypeCode c = ReflectionHelper.GetTypeCode(type);
752
return c >= TypeCode.SByte && c <= TypeCode.UInt64;
756
#region User-Defined Conversions
758
/// Gets whether type A is encompassed by type B.
760
bool IsEncompassedBy(IType a, IType b)
762
return a.Kind != TypeKind.Interface && b.Kind != TypeKind.Interface && StandardImplicitConversion(a, b).IsValid;
765
bool IsEncompassingOrEncompassedBy(IType a, IType b)
767
return a.Kind != TypeKind.Interface && b.Kind != TypeKind.Interface
768
&& (StandardImplicitConversion(a, b).IsValid || StandardImplicitConversion(b, a).IsValid);
771
IType FindMostEncompassedType(IEnumerable<IType> candidates)
774
foreach (var current in candidates) {
775
if (best == null || IsEncompassedBy(current, best))
777
else if (!IsEncompassedBy(best, current))
778
return null; // Ambiguous
783
IType FindMostEncompassingType(IEnumerable<IType> candidates)
786
foreach (var current in candidates) {
787
if (best == null || IsEncompassedBy(best, current))
789
else if (!IsEncompassedBy(current, best))
790
return null; // Ambiguous
795
Conversion SelectOperator(IType mostSpecificSource, IType mostSpecificTarget, IList<OperatorInfo> operators, bool isImplicit, IType source, IType target)
797
var selected = operators.Where(op => op.SourceType.Equals(mostSpecificSource) && op.TargetType.Equals(mostSpecificTarget)).ToList();
798
if (selected.Count == 0)
799
return Conversion.None;
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));
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));
810
return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit, isAmbiguous: true, conversionBeforeUserDefinedOperator: ExplicitConversion(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversion(mostSpecificTarget, target));
813
Conversion UserDefinedImplicitConversion(ResolveResult fromResult, IType fromType, IType toType)
815
// C# 4.0 spec §6.4.4 User-defined implicit conversions
816
var operators = GetApplicableConversionOperators(fromResult, fromType, toType, false);
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));
827
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: true, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
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)
840
else if (NullableType.IsNullable(toType))
841
return UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
843
return Conversion.None;
846
return Conversion.None;
850
Conversion UserDefinedExplicitConversion(ResolveResult fromResult, IType fromType, IType toType)
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;
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));
863
mostSpecificSource = FindMostEncompassingType(operators.Select(op => op.SourceType));
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);
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));
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));
879
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: false, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
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)
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
897
return Conversion.None;
900
return Conversion.None;
906
public readonly IMethod Method;
907
public readonly IType SourceType;
908
public readonly IType TargetType;
909
public readonly bool IsLifted;
911
public OperatorInfo(IMethod method, IType sourceType, IType targetType, bool isLifted)
913
this.Method = method;
914
this.SourceType = sourceType;
915
this.TargetType = targetType;
916
this.IsLifted = isLifted;
920
List<OperatorInfo> GetApplicableConversionOperators(ResolveResult fromResult, IType fromType, IType toType, bool isExplicit)
922
// Find the candidate operators:
923
Predicate<IUnresolvedMethod> opFilter;
925
opFilter = m => m.IsStatic && m.IsOperator && (m.Name == "op_Explicit" || m.Name == "op_Implicit") && m.Parameters.Count == 1;
927
opFilter = m => m.IsStatic && m.IsOperator && m.Name == "op_Implicit" && m.Parameters.Count == 1;
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:
939
isApplicable = (IsEncompassingOrEncompassedBy(fromType, sourceType) || ImplicitConstantExpressionConversion(fromResult, sourceType))
940
&& IsEncompassingOrEncompassedBy(targetType, toType);
942
isApplicable = (IsEncompassedBy(fromType, sourceType) || ImplicitConstantExpressionConversion(fromResult, sourceType))
943
&& IsEncompassedBy(targetType, toType);
945
// Try if the operator is applicable in lifted form:
947
result.Add(new OperatorInfo(op, sourceType, targetType, false));
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;
954
isApplicable = IsEncompassingOrEncompassedBy(fromType, liftedSourceType)
955
&& IsEncompassingOrEncompassedBy(liftedTargetType, toType);
957
isApplicable = IsEncompassedBy(fromType, liftedSourceType) && IsEncompassedBy(liftedTargetType, toType);
961
result.Add(new OperatorInfo(op, liftedSourceType, liftedTargetType, true));
969
#region AnonymousFunctionConversion
970
Conversion AnonymousFunctionConversion(ResolveResult resolveResult, IType toType)
972
// C# 5.0 spec §6.5 Anonymous function conversions
973
LambdaResolveResult f = resolveResult as LambdaResolveResult;
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);
981
IMethod d = toType.GetDelegateInvokeMethod();
983
return Conversion.None;
985
IType[] dParamTypes = new IType[d.Parameters.Count];
986
for (int i = 0; i < dParamTypes.Length; i++) {
987
dParamTypes[i] = d.Parameters[i].Type;
989
IType dReturnType = d.ReturnType;
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;
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;
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;
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) {
1019
return Conversion.None;
1023
return f.IsValid(dParamTypes, dReturnType, this);
1026
static IType UnpackExpressionTreeType(IType type)
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);
1037
#region MethodGroupConversion
1038
Conversion MethodGroupConversion(ResolveResult resolveResult, IType toType)
1040
// C# 4.0 spec §6.6 Method group conversions
1041
MethodGroupResolveResult rr = resolveResult as MethodGroupResolveResult;
1043
return Conversion.None;
1044
IMethod invoke = toType.GetDelegateInvokeMethod();
1046
return Conversion.None;
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);
1056
args[i] = new ResolveResult(parameterType);
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);
1066
return Conversion.MethodGroupConversion(method, isVirtual);
1068
return Conversion.InvalidMethodGroupConversion(method, isVirtual);
1070
return Conversion.None;
1075
/// Gets whether a method <paramref name="m"/> is compatible with a delegate type.
1076
/// §15.2 Delegate compatibility
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)
1085
throw new ArgumentNullException("m");
1087
throw new ArgumentNullException("invoke");
1088
int firstParameterInM = isExtensionMethodInvocation ? 1 : 0;
1089
if (m.Parameters.Count - firstParameterInM != invoke.Parameters.Count)
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)
1097
if (pm.IsRef || pm.IsOut) {
1098
// ref/out parameters must have same types
1099
if (!pm.Type.Equals(pd.Type))
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))
1107
// check return type compatibility
1108
return IdentityConversion(m.ReturnType, invoke.ReturnType)
1109
|| IsImplicitReferenceConversion(m.ReturnType, invoke.ReturnType);
1113
#region BetterConversion
1115
/// Gets the better conversion (C# 4.0 spec, §7.5.3.3)
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)
1120
LambdaResolveResult lambda = resolveResult as LambdaResolveResult;
1121
if (lambda != null) {
1122
if (!lambda.IsAnonymousMethod) {
1123
t1 = UnpackExpressionTreeType(t1);
1124
t2 = UnpackExpressionTreeType(t2);
1126
IMethod m1 = t1.GetDelegateInvokeMethod();
1127
IMethod m2 = t2.GetDelegateInvokeMethod();
1128
if (m1 == null || m2 == null)
1130
int r = BetterConversionTarget(t1, t2);
1133
if (m1.Parameters.Count != m2.Parameters.Count)
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))
1141
if (lambda.HasParameterList && parameterTypes.Length != lambda.Parameters.Count)
1144
IType ret1 = m1.ReturnType;
1145
IType ret2 = m2.ReturnType;
1146
if (ret1.Kind == TypeKind.Void && ret2.Kind != TypeKind.Void)
1148
if (ret1.Kind != TypeKind.Void && ret2.Kind == TypeKind.Void)
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);
1162
return BetterConversion(resolveResult.Type, t1, t2);
1167
/// Unpacks the generic Task[T]. Returns null if the input is not Task[T].
1169
static IType UnpackTask(IType type)
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);
1179
/// Gets the better conversion (C# 4.0 spec, §7.5.3.4)
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)
1184
bool ident1 = IdentityConversion(s, t1);
1185
bool ident2 = IdentityConversion(s, t2);
1186
if (ident1 && !ident2)
1188
if (ident2 && !ident1)
1190
return BetterConversionTarget(t1, t2);
1194
/// Gets the better conversion target (C# 4.0 spec, §7.5.3.5)
1196
/// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
1197
int BetterConversionTarget(IType t1, IType t2)
1199
bool t1To2 = ImplicitConversion(t1, t2).IsValid;
1200
bool t2To1 = ImplicitConversion(t2, t1).IsValid;
1201
if (t1To2 && !t2To1)
1203
if (t2To1 && !t1To2)
1205
TypeCode t1Code = ReflectionHelper.GetTypeCode(t1);
1206
TypeCode t2Code = ReflectionHelper.GetTypeCode(t2);
1207
if (IsBetterIntegralType(t1Code, t2Code))
1209
if (IsBetterIntegralType(t2Code, t1Code))
1214
bool IsBetterIntegralType(TypeCode t1, TypeCode t2)
1216
// signed types are better than unsigned types
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;