Merge pull request #347 from JamesB7/master
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Ast / TypeUtils.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Apache License, Version 2.0, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Apache License, Version 2.0.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 #if !FEATURE_CORE_DLR
17 using Microsoft.Scripting.Ast;
18 #else
19 using System.Linq.Expressions;
20 #endif
21
22 using System.Collections.Generic;
23 using System.Diagnostics;
24 using System.Reflection;
25
26 namespace System.Dynamic.Utils {
27
28     internal static class TypeUtils {
29         private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
30         internal const MethodAttributes PublicStatic = MethodAttributes.Public | MethodAttributes.Static;
31
32         internal static Type GetNonNullableType(this Type type) {
33             if (IsNullableType(type)) {
34                 return type.GetGenericArguments()[0];
35             }
36             return type;
37         }
38
39         internal static Type GetNullableType(Type type) {
40             Debug.Assert(type != null, "type cannot be null");
41             if (type.IsValueType && !IsNullableType(type)) {
42                 return typeof(Nullable<>).MakeGenericType(type);
43             }
44             return type;
45         }
46
47         internal static bool IsNullableType(this Type type) {
48             return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
49         }
50
51         internal static bool IsBool(Type type) {
52             return GetNonNullableType(type) == typeof(bool);
53         }
54
55         internal static bool IsNumeric(Type type) {
56             type = GetNonNullableType(type);
57             if (!type.IsEnum) {
58                 switch (Type.GetTypeCode(type)) {
59                     case TypeCode.Char:
60                     case TypeCode.SByte:
61                     case TypeCode.Byte:
62                     case TypeCode.Int16:
63                     case TypeCode.Int32:
64                     case TypeCode.Int64:
65                     case TypeCode.Double:
66                     case TypeCode.Single:
67                     case TypeCode.UInt16:
68                     case TypeCode.UInt32:
69                     case TypeCode.UInt64:
70                         return true;
71                 }
72             }
73             return false;
74         }
75
76         internal static bool IsInteger(Type type) {
77             type = GetNonNullableType(type);
78             if (type.IsEnum) {
79                 return false;
80             }
81             switch (Type.GetTypeCode(type)) {
82                 case TypeCode.Byte:
83                 case TypeCode.SByte:
84                 case TypeCode.Int16:
85                 case TypeCode.Int32:
86                 case TypeCode.Int64:
87                 case TypeCode.UInt16:
88                 case TypeCode.UInt32:
89                 case TypeCode.UInt64:
90                     return true;
91                 default:
92                     return false;
93             }
94         }
95
96
97         internal static bool IsArithmetic(Type type) {
98             type = GetNonNullableType(type);
99             if (!type.IsEnum) {
100                 switch (Type.GetTypeCode(type)) {
101                     case TypeCode.Int16:
102                     case TypeCode.Int32:
103                     case TypeCode.Int64:
104                     case TypeCode.Double:
105                     case TypeCode.Single:
106                     case TypeCode.UInt16:
107                     case TypeCode.UInt32:
108                     case TypeCode.UInt64:
109                         return true;
110                 }
111             }
112             return false;
113         }
114
115         internal static bool IsUnsignedInt(Type type) {
116             type = GetNonNullableType(type);
117             if (!type.IsEnum) {
118                 switch (Type.GetTypeCode(type)) {
119                     case TypeCode.UInt16:
120                     case TypeCode.UInt32:
121                     case TypeCode.UInt64:
122                         return true;
123                 }
124             }
125             return false;
126         }
127
128         internal static bool IsIntegerOrBool(Type type) {
129             type = GetNonNullableType(type);
130             if (!type.IsEnum) {
131                 switch (Type.GetTypeCode(type)) {
132                     case TypeCode.Int64:
133                     case TypeCode.Int32:
134                     case TypeCode.Int16:
135                     case TypeCode.UInt64:
136                     case TypeCode.UInt32:
137                     case TypeCode.UInt16:
138                     case TypeCode.Boolean:
139                     case TypeCode.SByte:
140                     case TypeCode.Byte:
141                         return true;
142                 }
143             }
144             return false;
145         }
146
147         internal static bool AreEquivalent(Type t1, Type t2) {
148 #if FEATURE_TYPE_EQUIVALENCE
149             return t1 == t2 || t1.IsEquivalentTo(t2);
150 #else
151             return t1 == t2;
152 #endif
153         }
154
155         internal static bool AreReferenceAssignable(Type dest, Type src) {
156             // WARNING: This actually implements "Is this identity assignable and/or reference assignable?"
157             if (AreEquivalent(dest, src)) {
158                 return true;
159             }
160             if (!dest.IsValueType && !src.IsValueType && dest.IsAssignableFrom(src)) {
161                 return true;
162             }
163             return false;
164         }
165
166         // Checks if the type is a valid target for an instance call
167         internal static bool IsValidInstanceType(MemberInfo member, Type instanceType) {
168             Type targetType = member.DeclaringType;
169             if (AreReferenceAssignable(targetType, instanceType)) {
170                 return true;
171             }
172             if (instanceType.IsValueType) {
173                 if (AreReferenceAssignable(targetType, typeof(System.Object))) {
174                     return true;
175                 }
176                 if (AreReferenceAssignable(targetType, typeof(System.ValueType))) {
177                     return true;
178                 }
179                 if (instanceType.IsEnum && AreReferenceAssignable(targetType, typeof(System.Enum))) {
180                     return true;
181                 }
182                 // A call to an interface implemented by a struct is legal whether the struct has
183                 // been boxed or not.
184                 if (targetType.IsInterface) {
185                     foreach (Type interfaceType in instanceType.GetInterfaces()) {
186                         if (AreReferenceAssignable(targetType, interfaceType)) {
187                             return true;
188                         }
189                     }
190                 }
191             }
192             return false;
193         }
194
195         internal static bool HasIdentityPrimitiveOrNullableConversion(Type source, Type dest) {
196             Debug.Assert(source != null && dest != null);
197
198             // Identity conversion
199             if (AreEquivalent(source, dest)) {
200                 return true;
201             }
202
203             // Nullable conversions
204             if (IsNullableType(source) && AreEquivalent(dest, GetNonNullableType(source))) {
205                 return true;
206             }
207             if (IsNullableType(dest) && AreEquivalent(source, GetNonNullableType(dest))) {
208                 return true;
209             }
210             // Primitive runtime conversions
211             // All conversions amongst enum, bool, char, integer and float types
212             // (and their corresponding nullable types) are legal except for
213             // nonbool==>bool and nonbool==>bool?
214             // Since we have already covered bool==>bool, bool==>bool?, etc, above,
215             // we can just disallow having a bool or bool? destination type here.
216             if (IsConvertible(source) && IsConvertible(dest) && GetNonNullableType(dest) != typeof(bool)) {
217                 return true;
218             }
219             return false;
220         }
221
222         internal static bool HasReferenceConversion(Type source, Type dest) {
223             Debug.Assert(source != null && dest != null);
224
225             // void -> void conversion is handled elsewhere
226             // (it's an identity conversion)
227             // All other void conversions are disallowed.
228             if (source == typeof(void) || dest == typeof(void)) {
229                 return false;
230             }
231
232             Type nnSourceType = TypeUtils.GetNonNullableType(source);
233             Type nnDestType = TypeUtils.GetNonNullableType(dest);
234
235             // Down conversion
236             if (nnSourceType.IsAssignableFrom(nnDestType)) {
237                 return true;
238             }
239             // Up conversion
240             if (nnDestType.IsAssignableFrom(nnSourceType)) {
241                 return true;
242             }
243             // Interface conversion
244             if (source.IsInterface || dest.IsInterface) {
245                 return true;
246             }
247             // Variant delegate conversion
248             if (IsLegalExplicitVariantDelegateConversion(source, dest))
249                 return true;
250                 
251             // Object conversion
252             if (source == typeof(object) || dest == typeof(object)) {
253                 return true;
254             }
255             return false;
256         }
257
258         private static bool IsCovariant(Type t)
259         {
260             Debug.Assert(t != null);
261             return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Covariant);
262         }
263
264         private static bool IsContravariant(Type t)
265         {
266             Debug.Assert(t != null);
267             return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Contravariant);
268         }
269
270         private static bool IsInvariant(Type t)
271         {
272             Debug.Assert(t != null);
273             return 0 == (t.GenericParameterAttributes & GenericParameterAttributes.VarianceMask);
274         }
275
276         private static bool IsDelegate(Type t)
277         {
278             Debug.Assert(t != null);
279             return t.IsSubclassOf(typeof(System.MulticastDelegate));
280         }
281
282         internal static bool IsLegalExplicitVariantDelegateConversion(Type source, Type dest)
283         {
284             Debug.Assert(source != null && dest != null);
285
286             // There *might* be a legal conversion from a generic delegate type S to generic delegate type  T, 
287             // provided all of the follow are true:
288             //   o Both types are constructed generic types of the same generic delegate type, D<X1,... Xk>.
289             //     That is, S = D<S1...>, T = D<T1...>.
290             //   o If type parameter Xi is declared to be invariant then Si must be identical to Ti.
291             //   o If type parameter Xi is declared to be covariant ("out") then Si must be convertible
292             //     to Ti via an identify conversion,  implicit reference conversion, or explicit reference conversion.
293             //   o If type parameter Xi is declared to be contravariant ("in") then either Si must be identical to Ti, 
294             //     or Si and Ti must both be reference types.
295
296             if (!IsDelegate(source) || !IsDelegate(dest) || !source.IsGenericType || !dest.IsGenericType)
297                 return false;
298
299             Type genericDelegate = source.GetGenericTypeDefinition();
300
301             if (dest.GetGenericTypeDefinition() != genericDelegate)
302                 return false;
303
304             Type[] genericParameters = genericDelegate.GetGenericArguments();
305             Type[] sourceArguments = source.GetGenericArguments();
306             Type[] destArguments = dest.GetGenericArguments();
307
308             Debug.Assert(genericParameters != null);
309             Debug.Assert(sourceArguments != null);
310             Debug.Assert(destArguments != null);
311             Debug.Assert(genericParameters.Length == sourceArguments.Length);
312             Debug.Assert(genericParameters.Length == destArguments.Length);
313
314             for (int iParam = 0; iParam < genericParameters.Length; ++iParam)
315             {
316                 Type sourceArgument = sourceArguments[iParam];
317                 Type destArgument = destArguments[iParam];
318
319                 Debug.Assert(sourceArgument != null && destArgument != null);
320                
321                 // If the arguments are identical then this one is automatically good, so skip it.
322                 if (AreEquivalent(sourceArgument, destArgument))
323                 {
324                     continue;
325                 }
326                 
327                 Type genericParameter = genericParameters[iParam];
328
329                 Debug.Assert(genericParameter != null);
330
331                 if (IsInvariant(genericParameter))
332                 {
333                     return false;
334                 }
335         
336                 if (IsCovariant(genericParameter))
337                 {
338                     if (!HasReferenceConversion(sourceArgument, destArgument))
339                     {
340                         return false;
341                     }
342                 }
343                 else if (IsContravariant(genericParameter))
344                 {
345                     if (sourceArgument.IsValueType || destArgument.IsValueType)
346                     {
347                         return false;
348                     }
349                 }
350             }
351             return true;
352         }
353
354         internal static bool IsConvertible(Type type) {
355             type = GetNonNullableType(type);
356             if (type.IsEnum) {
357                 return true;
358             }
359             switch (Type.GetTypeCode(type)) {
360                 case TypeCode.Boolean:
361                 case TypeCode.Byte:
362                 case TypeCode.SByte:
363                 case TypeCode.Int16:
364                 case TypeCode.Int32:
365                 case TypeCode.Int64:
366                 case TypeCode.UInt16:
367                 case TypeCode.UInt32:
368                 case TypeCode.UInt64:
369                 case TypeCode.Single:
370                 case TypeCode.Double:
371                 case TypeCode.Char:
372                     return true;
373                 default:
374                     return false;
375             }
376         }
377
378         internal static bool HasReferenceEquality(Type left, Type right) {
379             if (left.IsValueType || right.IsValueType) {
380                 return false;
381             }
382
383             // If we have an interface and a reference type then we can do 
384             // reference equality.
385
386             // If we have two reference types and one is assignable to the
387             // other then we can do reference equality.
388
389             return left.IsInterface || right.IsInterface ||
390                 AreReferenceAssignable(left, right) ||
391                 AreReferenceAssignable(right, left);
392         }
393
394         internal static bool HasBuiltInEqualityOperator(Type left, Type right) {
395             // If we have an interface and a reference type then we can do 
396             // reference equality.
397             if (left.IsInterface && !right.IsValueType) {
398                 return true;
399             }
400             if (right.IsInterface && !left.IsValueType) {
401                 return true;
402             }
403             // If we have two reference types and one is assignable to the
404             // other then we can do reference equality.
405             if (!left.IsValueType && !right.IsValueType) {
406                 if (AreReferenceAssignable(left, right) || AreReferenceAssignable(right, left)) {
407                     return true;
408                 }
409             }
410             // Otherwise, if the types are not the same then we definitely 
411             // do not have a built-in equality operator.
412             if (!AreEquivalent(left, right)) {
413                 return false;
414             }
415             // We have two identical value types, modulo nullability.  (If they were both the 
416             // same reference type then we would have returned true earlier.)
417             Debug.Assert(left.IsValueType);
418             // Equality between struct types is only defined for numerics, bools, enums,
419             // and their nullable equivalents.
420             Type nnType = GetNonNullableType(left);
421             if (nnType == typeof(bool) || IsNumeric(nnType) || nnType.IsEnum) {
422                 return true;
423             }
424             return false;
425         }
426
427         internal static bool IsImplicitlyConvertible(Type source, Type destination) {
428             return AreEquivalent(source, destination) ||                // identity conversion
429                 IsImplicitNumericConversion(source, destination) ||
430                 IsImplicitReferenceConversion(source, destination) ||
431                 IsImplicitBoxingConversion(source, destination) ||
432                 IsImplicitNullableConversion(source, destination);
433         }
434
435
436         internal static MethodInfo GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType, bool implicitOnly) {
437             // check for implicit coercions first
438             Type nnExprType = TypeUtils.GetNonNullableType(convertFrom);
439             Type nnConvType = TypeUtils.GetNonNullableType(convertToType);
440             // try exact match on types
441             MethodInfo[] eMethods = nnExprType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
442             MethodInfo method = FindConversionOperator(eMethods, convertFrom, convertToType, implicitOnly);
443             if (method != null) {
444                 return method;
445             }
446             MethodInfo[] cMethods = nnConvType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
447             method = FindConversionOperator(cMethods, convertFrom, convertToType, implicitOnly);
448             if (method != null) {
449                 return method;
450             }
451             // try lifted conversion
452             if (!TypeUtils.AreEquivalent(nnExprType, convertFrom) ||
453                 !TypeUtils.AreEquivalent(nnConvType, convertToType)) {
454                 method = FindConversionOperator(eMethods, nnExprType, nnConvType, implicitOnly);
455                 if (method == null) {
456                     method = FindConversionOperator(cMethods, nnExprType, nnConvType, implicitOnly);
457                 }
458                 if (method != null) {
459                     return method;
460                 }
461             }
462             return null;
463         }
464
465         internal static MethodInfo FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo, bool implicitOnly) {
466             foreach (MethodInfo mi in methods) {
467                 if (mi.Name != "op_Implicit" && (implicitOnly || mi.Name != "op_Explicit")) {
468                     continue;
469                 }
470                 if (!TypeUtils.AreEquivalent(mi.ReturnType, typeTo)) {
471                     continue;
472                 }
473                 ParameterInfo[] pis = mi.GetParametersCached();
474                 if (!TypeUtils.AreEquivalent(pis[0].ParameterType, typeFrom)) {
475                     continue;
476                 }
477                 return mi;
478             }
479             return null;
480         }
481
482         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
483         private static bool IsImplicitNumericConversion(Type source, Type destination) {
484             TypeCode tcSource = Type.GetTypeCode(source);
485             TypeCode tcDest = Type.GetTypeCode(destination);
486
487             switch (tcSource) {
488                 case TypeCode.SByte:
489                     switch (tcDest) {
490                         case TypeCode.Int16:
491                         case TypeCode.Int32:
492                         case TypeCode.Int64:
493                         case TypeCode.Single:
494                         case TypeCode.Double:
495                         case TypeCode.Decimal:
496                             return true;
497                     }
498                     return false;
499                 case TypeCode.Byte:
500                     switch (tcDest) {
501                         case TypeCode.Int16:
502                         case TypeCode.UInt16:
503                         case TypeCode.Int32:
504                         case TypeCode.UInt32:
505                         case TypeCode.Int64:
506                         case TypeCode.UInt64:
507                         case TypeCode.Single:
508                         case TypeCode.Double:
509                         case TypeCode.Decimal:
510                             return true;
511                     }
512                     return false;
513                 case TypeCode.Int16:
514                     switch (tcDest) {
515                         case TypeCode.Int32:
516                         case TypeCode.Int64:
517                         case TypeCode.Single:
518                         case TypeCode.Double:
519                         case TypeCode.Decimal:
520                             return true;
521                     }
522                     return false;
523                 case TypeCode.UInt16:
524                     switch (tcDest) {
525                         case TypeCode.Int32:
526                         case TypeCode.UInt32:
527                         case TypeCode.Int64:
528                         case TypeCode.UInt64:
529                         case TypeCode.Single:
530                         case TypeCode.Double:
531                         case TypeCode.Decimal:
532                             return true;
533                     }
534                     return false;
535                 case TypeCode.Int32:
536                     switch (tcDest) {
537                         case TypeCode.Int64:
538                         case TypeCode.Single:
539                         case TypeCode.Double:
540                         case TypeCode.Decimal:
541                             return true;
542                     }
543                     return false;
544                 case TypeCode.UInt32:
545                     switch (tcDest) {
546                         case TypeCode.UInt32:
547                         case TypeCode.UInt64:
548                         case TypeCode.Single:
549                         case TypeCode.Double:
550                         case TypeCode.Decimal:
551                             return true;
552                     }
553                     return false;
554                 case TypeCode.Int64:
555                 case TypeCode.UInt64:
556                     switch (tcDest) {
557                         case TypeCode.Single:
558                         case TypeCode.Double:
559                         case TypeCode.Decimal:
560                             return true;
561                     }
562                     return false;
563                 case TypeCode.Char:
564                     switch (tcDest) {
565                         case TypeCode.UInt16:
566                         case TypeCode.Int32:
567                         case TypeCode.UInt32:
568                         case TypeCode.Int64:
569                         case TypeCode.UInt64:
570                         case TypeCode.Single:
571                         case TypeCode.Double:
572                         case TypeCode.Decimal:
573                             return true;
574                     }
575                     return false;
576                 case TypeCode.Single:
577                     return (tcDest == TypeCode.Double);
578             }
579             return false;
580         }
581
582         private static bool IsImplicitReferenceConversion(Type source, Type destination) {
583             return destination.IsAssignableFrom(source);
584         }
585
586         private static bool IsImplicitBoxingConversion(Type source, Type destination) {
587             if (source.IsValueType && (destination == typeof(object) || destination == typeof(System.ValueType)))
588                 return true;
589             if (source.IsEnum && destination == typeof(System.Enum))
590                 return true;
591             return false;
592         }
593
594         private static bool IsImplicitNullableConversion(Type source, Type destination) {
595             if (IsNullableType(destination))
596                 return IsImplicitlyConvertible(GetNonNullableType(source), GetNonNullableType(destination));
597             return false;
598         }
599
600         internal static bool IsSameOrSubclass(Type type, Type subType) {
601             return AreEquivalent(type, subType) || subType.IsSubclassOf(type);
602         }
603
604         internal static void ValidateType(Type type) {
605             if (type.IsGenericTypeDefinition) {
606                 throw Error.TypeIsGeneric(type);
607             }
608             if (type.ContainsGenericParameters) {
609                 throw Error.TypeContainsGenericParameters(type);
610             }
611         }
612
613         //from TypeHelper
614         internal static Type FindGenericType(Type definition, Type type) {
615             while (type != null && type != typeof(object)) {
616                 if (type.IsGenericType && AreEquivalent(type.GetGenericTypeDefinition(), definition)) {
617                     return type;
618                 }
619                 if (definition.IsInterface) {
620                     foreach (Type itype in type.GetInterfaces()) {
621                         Type found = FindGenericType(definition, itype);
622                         if (found != null)
623                             return found;
624                     }
625                 }
626                 type = type.BaseType;
627             }
628             return null;
629         }
630
631         internal static bool IsUnsigned(Type type) {
632             type = GetNonNullableType(type);
633             switch (Type.GetTypeCode(type)) {
634                 case TypeCode.Byte:
635                 case TypeCode.UInt16:
636                 case TypeCode.Char:
637                 case TypeCode.UInt32:
638                 case TypeCode.UInt64:
639                     return true;
640                 default:
641                     return false;
642             }
643         }
644
645         internal static bool IsFloatingPoint(Type type) {
646             type = GetNonNullableType(type);
647             switch (Type.GetTypeCode(type)) {
648                 case TypeCode.Single:
649                 case TypeCode.Double:
650                     return true;
651                 default:
652                     return false;
653             }
654         }
655
656         /// <summary>
657         /// Searches for an operator method on the type. The method must have
658         /// the specified signature, no generic arguments, and have the
659         /// SpecialName bit set. Also searches inherited operator methods.
660         /// 
661         /// NOTE: This was designed to satisfy the needs of op_True and
662         /// op_False, because we have to do runtime lookup for those. It may
663         /// not work right for unary operators in general.
664         /// </summary>
665         internal static MethodInfo GetBooleanOperator(Type type, string name) {
666             do {
667                 MethodInfo result = type.GetMethodValidated(name, AnyStatic, null, new Type[] { type }, null);
668                 if (result != null && result.IsSpecialName && !result.ContainsGenericParameters) {
669                     return result;
670                 }
671                 type = type.BaseType;
672             } while (type != null);
673             return null;
674         }
675
676         internal static Type GetNonRefType(this Type type) {
677             return type.IsByRef ? type.GetElementType() : type;
678         }
679
680         private static readonly Assembly _mscorlib = typeof(object).Assembly;
681         private static readonly Assembly _systemCore = typeof(Expression).Assembly;
682
683         /// <summary>
684         /// We can cache references to types, as long as they aren't in
685         /// collectable assemblies. Unfortunately, we can't really distinguish
686         /// between different flavors of assemblies. But, we can at least
687         /// create a whitelist for types in mscorlib (so we get the primitives)
688         /// and System.Core (so we find Func/Action overloads, etc).
689         /// </summary>
690         internal static bool CanCache(this Type t) {
691             // Note: we don't have to scan base or declaring types here.
692             // There's no way for a type in mscorlib to derive from or be
693             // contained in a type from another assembly. The only thing we
694             // need to look at is the generic arguments, which are the thing
695             // that allows mscorlib types to be specialized by types in other
696             // assemblies.
697
698             var asm = t.Assembly;
699             if (asm != _mscorlib && asm != _systemCore) {
700                 // Not in mscorlib or our assembly
701                 return false;
702             }
703
704             if (t.IsGenericType) {
705                 foreach (Type g in t.GetGenericArguments()) {
706                     if (!CanCache(g)) {
707                         return false;
708                     }
709                 }
710             }
711
712             return true;
713         }
714     }
715 }