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