1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
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.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using Microsoft.Scripting.Ast;
19 using System.Linq.Expressions;
25 using System.Collections.Generic;
26 using System.Diagnostics;
27 using System.Reflection;
29 namespace System.Dynamic.Utils {
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;
35 internal static Type GetNonNullableType(this Type type) {
36 if (IsNullableType(type)) {
37 return type.GetGenericArguments()[0];
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);
50 internal static bool IsNullableType(this Type type) {
51 return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
54 internal static bool IsBool(Type type) {
55 return GetNonNullableType(type) == typeof(bool);
58 internal static bool IsNumeric(Type type) {
59 type = GetNonNullableType(type);
61 switch (Type.GetTypeCode(type)) {
79 internal static bool IsInteger(Type type) {
80 type = GetNonNullableType(type);
84 switch (Type.GetTypeCode(type)) {
100 internal static bool IsArithmetic(Type type) {
101 type = GetNonNullableType(type);
103 switch (Type.GetTypeCode(type)) {
107 case TypeCode.Double:
108 case TypeCode.Single:
109 case TypeCode.UInt16:
110 case TypeCode.UInt32:
111 case TypeCode.UInt64:
118 internal static bool IsUnsignedInt(Type type) {
119 type = GetNonNullableType(type);
121 switch (Type.GetTypeCode(type)) {
122 case TypeCode.UInt16:
123 case TypeCode.UInt32:
124 case TypeCode.UInt64:
131 internal static bool IsIntegerOrBool(Type type) {
132 type = GetNonNullableType(type);
134 switch (Type.GetTypeCode(type)) {
138 case TypeCode.UInt64:
139 case TypeCode.UInt32:
140 case TypeCode.UInt16:
141 case TypeCode.Boolean:
150 internal static bool AreEquivalent(Type t1, Type t2)
152 #if CLR2 || SILVERLIGHT
155 return t1 == t2 || t1.IsEquivalentTo(t2);
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)) {
164 if (!dest.IsValueType && !src.IsValueType && dest.IsAssignableFrom(src)) {
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)) {
176 if (instanceType.IsValueType) {
177 if (AreReferenceAssignable(targetType, typeof(System.Object))) {
180 if (AreReferenceAssignable(targetType, typeof(System.ValueType))) {
183 if (instanceType.IsEnum && AreReferenceAssignable(targetType, typeof(System.Enum))) {
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)) {
199 internal static bool HasIdentityPrimitiveOrNullableConversion(Type source, Type dest) {
200 Debug.Assert(source != null && dest != null);
202 // Identity conversion
203 if (AreEquivalent(source, dest)) {
207 // Nullable conversions
208 if (IsNullableType(source) && AreEquivalent(dest, GetNonNullableType(source))) {
211 if (IsNullableType(dest) && AreEquivalent(source, GetNonNullableType(dest))) {
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)) {
226 internal static bool HasReferenceConversion(Type source, Type dest) {
227 Debug.Assert(source != null && dest != null);
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)) {
236 Type nnSourceType = TypeUtils.GetNonNullableType(source);
237 Type nnDestType = TypeUtils.GetNonNullableType(dest);
240 if (nnSourceType.IsAssignableFrom(nnDestType)) {
244 if (nnDestType.IsAssignableFrom(nnSourceType)) {
247 // Interface conversion
248 if (source.IsInterface || dest.IsInterface) {
251 // Variant delegate conversion
252 if (IsLegalExplicitVariantDelegateConversion(source, dest))
256 if (source == typeof(object) || dest == typeof(object)) {
262 private static bool IsCovariant(Type t)
264 Debug.Assert(t != null);
265 return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Covariant);
268 private static bool IsContravariant(Type t)
270 Debug.Assert(t != null);
271 return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Contravariant);
274 private static bool IsInvariant(Type t)
276 Debug.Assert(t != null);
277 return 0 == (t.GenericParameterAttributes & GenericParameterAttributes.VarianceMask);
280 private static bool IsDelegate(Type t)
282 Debug.Assert(t != null);
283 return t.IsSubclassOf(typeof(System.MulticastDelegate));
286 internal static bool IsLegalExplicitVariantDelegateConversion(Type source, Type dest)
288 Debug.Assert(source != null && dest != null);
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.
300 if (!IsDelegate(source) || !IsDelegate(dest) || !source.IsGenericType || !dest.IsGenericType)
303 Type genericDelegate = source.GetGenericTypeDefinition();
305 if (dest.GetGenericTypeDefinition() != genericDelegate)
308 Type[] genericParameters = genericDelegate.GetGenericArguments();
309 Type[] sourceArguments = source.GetGenericArguments();
310 Type[] destArguments = dest.GetGenericArguments();
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);
318 for (int iParam = 0; iParam < genericParameters.Length; ++iParam)
320 Type sourceArgument = sourceArguments[iParam];
321 Type destArgument = destArguments[iParam];
323 Debug.Assert(sourceArgument != null && destArgument != null);
325 // If the arguments are identical then this one is automatically good, so skip it.
326 if (AreEquivalent(sourceArgument, destArgument))
331 Type genericParameter = genericParameters[iParam];
333 Debug.Assert(genericParameter != null);
335 if (IsInvariant(genericParameter))
340 if (IsCovariant(genericParameter))
342 if (!HasReferenceConversion(sourceArgument, destArgument))
347 else if (IsContravariant(genericParameter))
349 if (sourceArgument.IsValueType || destArgument.IsValueType)
358 internal static bool IsConvertible(Type type) {
359 type = GetNonNullableType(type);
363 switch (Type.GetTypeCode(type)) {
364 case TypeCode.Boolean:
370 case TypeCode.UInt16:
371 case TypeCode.UInt32:
372 case TypeCode.UInt64:
373 case TypeCode.Single:
374 case TypeCode.Double:
382 internal static bool HasReferenceEquality(Type left, Type right) {
383 if (left.IsValueType || right.IsValueType) {
387 // If we have an interface and a reference type then we can do
388 // reference equality.
390 // If we have two reference types and one is assignable to the
391 // other then we can do reference equality.
393 return left.IsInterface || right.IsInterface ||
394 AreReferenceAssignable(left, right) ||
395 AreReferenceAssignable(right, left);
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) {
404 if (right.IsInterface && !left.IsValueType) {
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)) {
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)) {
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) {
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);
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) {
450 MethodInfo[] cMethods = nnConvType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
451 method = FindConversionOperator(cMethods, convertFrom, convertToType, implicitOnly);
452 if (method != null) {
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);
462 if (method != null) {
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")) {
474 if (!TypeUtils.AreEquivalent(mi.ReturnType, typeTo)) {
477 ParameterInfo[] pis = mi.GetParametersCached();
478 if (!TypeUtils.AreEquivalent(pis[0].ParameterType, typeFrom)) {
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);
497 case TypeCode.Single:
498 case TypeCode.Double:
499 case TypeCode.Decimal:
506 case TypeCode.UInt16:
508 case TypeCode.UInt32:
510 case TypeCode.UInt64:
511 case TypeCode.Single:
512 case TypeCode.Double:
513 case TypeCode.Decimal:
521 case TypeCode.Single:
522 case TypeCode.Double:
523 case TypeCode.Decimal:
527 case TypeCode.UInt16:
530 case TypeCode.UInt32:
532 case TypeCode.UInt64:
533 case TypeCode.Single:
534 case TypeCode.Double:
535 case TypeCode.Decimal:
542 case TypeCode.Single:
543 case TypeCode.Double:
544 case TypeCode.Decimal:
548 case TypeCode.UInt32:
550 case TypeCode.UInt32:
551 case TypeCode.UInt64:
552 case TypeCode.Single:
553 case TypeCode.Double:
554 case TypeCode.Decimal:
559 case TypeCode.UInt64:
561 case TypeCode.Single:
562 case TypeCode.Double:
563 case TypeCode.Decimal:
569 case TypeCode.UInt16:
571 case TypeCode.UInt32:
573 case TypeCode.UInt64:
574 case TypeCode.Single:
575 case TypeCode.Double:
576 case TypeCode.Decimal:
580 case TypeCode.Single:
581 return (tcDest == TypeCode.Double);
586 private static bool IsImplicitReferenceConversion(Type source, Type destination) {
587 return destination.IsAssignableFrom(source);
590 private static bool IsImplicitBoxingConversion(Type source, Type destination) {
591 if (source.IsValueType && (destination == typeof(object) || destination == typeof(System.ValueType)))
593 if (source.IsEnum && destination == typeof(System.Enum))
598 private static bool IsImplicitNullableConversion(Type source, Type destination) {
599 if (IsNullableType(destination))
600 return IsImplicitlyConvertible(GetNonNullableType(source), GetNonNullableType(destination));
604 internal static bool IsSameOrSubclass(Type type, Type subType) {
605 return AreEquivalent(type, subType) || subType.IsSubclassOf(type);
608 internal static void ValidateType(Type type) {
609 if (type.IsGenericTypeDefinition) {
610 throw Error.TypeIsGeneric(type);
612 if (type.ContainsGenericParameters) {
613 throw Error.TypeContainsGenericParameters(type);
618 internal static Type FindGenericType(Type definition, Type type) {
619 while (type != null && type != typeof(object)) {
620 if (type.IsGenericType && AreEquivalent(type.GetGenericTypeDefinition(), definition)) {
623 if (definition.IsInterface) {
624 foreach (Type itype in type.GetInterfaces()) {
625 Type found = FindGenericType(definition, itype);
630 type = type.BaseType;
635 internal static bool IsUnsigned(Type type) {
636 type = GetNonNullableType(type);
637 switch (Type.GetTypeCode(type)) {
639 case TypeCode.UInt16:
641 case TypeCode.UInt32:
642 case TypeCode.UInt64:
649 internal static bool IsFloatingPoint(Type type) {
650 type = GetNonNullableType(type);
651 switch (Type.GetTypeCode(type)) {
652 case TypeCode.Single:
653 case TypeCode.Double:
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.
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.
669 internal static MethodInfo GetBooleanOperator(Type type, string name) {
671 MethodInfo result = type.GetMethodValidated(name, AnyStatic, null, new Type[] { type }, null);
672 if (result != null && result.IsSpecialName && !result.ContainsGenericParameters) {
675 type = type.BaseType;
676 } while (type != null);
680 internal static Type GetNonRefType(this Type type) {
681 return type.IsByRef ? type.GetElementType() : type;
684 private static readonly Assembly _mscorlib = typeof(object).Assembly;
685 private static readonly Assembly _systemCore = typeof(Expression).Assembly;
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).
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
702 var asm = t.Assembly;
703 if (asm != _mscorlib && asm != _systemCore) {
704 // Not in mscorlib or our assembly
708 if (t.IsGenericType) {
709 foreach (Type g in t.GetGenericArguments()) {