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