1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 using System.Linq;
7 using System.Linq.Expressions;
8 using System.Reflection;
9 
10 namespace System.Dynamic.Utils
11 {
12     internal static class TypeUtils
13     {
14         private static readonly Type[] s_arrayAssignableInterfaces = typeof(int[]).GetInterfaces()
15             .Where(i => i.IsGenericType)
16             .Select(i => i.GetGenericTypeDefinition())
17             .ToArray();
18 
GetNonNullableType(this Type type)19         public static Type GetNonNullableType(this Type type) => IsNullableType(type) ? type.GetGenericArguments()[0] : type;
20 
GetNullableType(this Type type)21         public static Type GetNullableType(this Type type)
22         {
23             Debug.Assert(type != null, "type cannot be null");
24             if (type.IsValueType && !IsNullableType(type))
25             {
26                 return typeof(Nullable<>).MakeGenericType(type);
27             }
28 
29             return type;
30         }
31 
IsNullableType(this Type type)32         public static bool IsNullableType(this Type type) => type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
33 
IsNullableOrReferenceType(this Type type)34         public static bool IsNullableOrReferenceType(this Type type) => !type.IsValueType || IsNullableType(type);
35 
IsBool(this Type type)36         public static bool IsBool(this Type type) => GetNonNullableType(type) == typeof(bool);
37 
IsNumeric(this Type type)38         public static bool IsNumeric(this Type type)
39         {
40             type = GetNonNullableType(type);
41             if (!type.IsEnum)
42             {
43                 switch (type.GetTypeCode())
44                 {
45                     case TypeCode.Char:
46                     case TypeCode.SByte:
47                     case TypeCode.Byte:
48                     case TypeCode.Int16:
49                     case TypeCode.Int32:
50                     case TypeCode.Int64:
51                     case TypeCode.Double:
52                     case TypeCode.Single:
53                     case TypeCode.UInt16:
54                     case TypeCode.UInt32:
55                     case TypeCode.UInt64:
56                         return true;
57                 }
58             }
59 
60             return false;
61         }
62 
IsInteger(this Type type)63         public static bool IsInteger(this Type type)
64         {
65             type = GetNonNullableType(type);
66             if (!type.IsEnum)
67             {
68                 switch (type.GetTypeCode())
69                 {
70                     case TypeCode.Byte:
71                     case TypeCode.SByte:
72                     case TypeCode.Int16:
73                     case TypeCode.Int32:
74                     case TypeCode.Int64:
75                     case TypeCode.UInt16:
76                     case TypeCode.UInt32:
77                     case TypeCode.UInt64:
78                         return true;
79                 }
80             }
81 
82             return false;
83         }
84 
IsInteger64(this Type type)85         public static bool IsInteger64(this Type type)
86         {
87             type = GetNonNullableType(type);
88             if (!type.IsEnum)
89             {
90                 switch (type.GetTypeCode())
91                 {
92                     case TypeCode.Int64:
93                     case TypeCode.UInt64:
94                         return true;
95                 }
96             }
97 
98             return false;
99         }
100 
IsArithmetic(this Type type)101         public static bool IsArithmetic(this Type type)
102         {
103             type = GetNonNullableType(type);
104             if (!type.IsEnum)
105             {
106                 switch (type.GetTypeCode())
107                 {
108                     case TypeCode.Int16:
109                     case TypeCode.Int32:
110                     case TypeCode.Int64:
111                     case TypeCode.Double:
112                     case TypeCode.Single:
113                     case TypeCode.UInt16:
114                     case TypeCode.UInt32:
115                     case TypeCode.UInt64:
116                         return true;
117                 }
118             }
119 
120             return false;
121         }
122 
IsUnsignedInt(this Type type)123         public static bool IsUnsignedInt(this Type type)
124         {
125             type = GetNonNullableType(type);
126             if (!type.IsEnum)
127             {
128                 switch (type.GetTypeCode())
129                 {
130                     case TypeCode.UInt16:
131                     case TypeCode.UInt32:
132                     case TypeCode.UInt64:
133                         return true;
134                 }
135             }
136 
137             return false;
138         }
139 
IsIntegerOrBool(this Type type)140         public static bool IsIntegerOrBool(this Type type)
141         {
142             type = GetNonNullableType(type);
143             if (!type.IsEnum)
144             {
145                 switch (type.GetTypeCode())
146                 {
147                     case TypeCode.Int64:
148                     case TypeCode.Int32:
149                     case TypeCode.Int16:
150                     case TypeCode.UInt64:
151                     case TypeCode.UInt32:
152                     case TypeCode.UInt16:
153                     case TypeCode.Boolean:
154                     case TypeCode.SByte:
155                     case TypeCode.Byte:
156                         return true;
157                 }
158             }
159 
160             return false;
161         }
162 
IsNumericOrBool(this Type type)163         public static bool IsNumericOrBool(this Type type) => IsNumeric(type) || IsBool(type);
164 
165         // Checks if the type is a valid target for an instance call
IsValidInstanceType(MemberInfo member, Type instanceType)166         public static bool IsValidInstanceType(MemberInfo member, Type instanceType)
167         {
168             Type targetType = member.DeclaringType;
169             if (AreReferenceAssignable(targetType, instanceType))
170             {
171                 return true;
172             }
173 
174             if (targetType == null)
175             {
176                 return false;
177             }
178 
179             if (instanceType.IsValueType)
180             {
181                 if (AreReferenceAssignable(targetType, typeof(object)))
182                 {
183                     return true;
184                 }
185 
186                 if (AreReferenceAssignable(targetType, typeof(ValueType)))
187                 {
188                     return true;
189                 }
190 
191                 if (instanceType.IsEnum && AreReferenceAssignable(targetType, typeof(Enum)))
192                 {
193                     return true;
194                 }
195 
196                 // A call to an interface implemented by a struct is legal whether the struct has
197                 // been boxed or not.
198                 if (targetType.IsInterface)
199                 {
200                     foreach (Type interfaceType in instanceType.GetTypeInfo().ImplementedInterfaces)
201                     {
202                         if (AreReferenceAssignable(targetType, interfaceType))
203                         {
204                             return true;
205                         }
206                     }
207                 }
208             }
209 
210             return false;
211         }
212 
HasIdentityPrimitiveOrNullableConversionTo(this Type source, Type dest)213         public static bool HasIdentityPrimitiveOrNullableConversionTo(this Type source, Type dest)
214         {
215             Debug.Assert(source != null && dest != null);
216 
217             // Identity conversion
218             if (AreEquivalent(source, dest))
219             {
220                 return true;
221             }
222 
223             // Nullable conversions
224             if (IsNullableType(source) && AreEquivalent(dest, GetNonNullableType(source)))
225             {
226                 return true;
227             }
228 
229             if (IsNullableType(dest) && AreEquivalent(source, GetNonNullableType(dest)))
230             {
231                 return true;
232             }
233 
234             // Primitive runtime conversions
235             // All conversions amongst enum, bool, char, integer and float types
236             // (and their corresponding nullable types) are legal except for
237             // nonbool==>bool and nonbool==>bool? which are only legal from
238             // bool-backed enums.
239             return IsConvertible(source) && IsConvertible(dest)
240                    && (GetNonNullableType(dest) != typeof(bool)
241                    || source.IsEnum && source.GetEnumUnderlyingType() == typeof(bool));
242         }
243 
HasReferenceConversionTo(this Type source, Type dest)244         public static bool HasReferenceConversionTo(this Type source, Type dest)
245         {
246             Debug.Assert(source != null && dest != null);
247 
248             // void -> void conversion is handled elsewhere
249             // (it's an identity conversion)
250             // All other void conversions are disallowed.
251             if (source == typeof(void) || dest == typeof(void))
252             {
253                 return false;
254             }
255 
256             Type nnSourceType = GetNonNullableType(source);
257             Type nnDestType = GetNonNullableType(dest);
258 
259             // Down conversion
260             if (nnSourceType.IsAssignableFrom(nnDestType))
261             {
262                 return true;
263             }
264 
265             // Up conversion
266             if (nnDestType.IsAssignableFrom(nnSourceType))
267             {
268                 return true;
269             }
270 
271             // Interface conversion
272             if (source.IsInterface || dest.IsInterface)
273             {
274                 return true;
275             }
276 
277             // Variant delegate conversion
278             if (IsLegalExplicitVariantDelegateConversion(source, dest))
279             {
280                 return true;
281             }
282 
283             // Object conversion handled by assignable above.
284             Debug.Assert(source != typeof(object) && dest != typeof(object));
285 
286             return (source.IsArray || dest.IsArray) && StrictHasReferenceConversionTo(source, dest, true);
287         }
288 
StrictHasReferenceConversionTo(this Type source, Type dest, bool skipNonArray)289         private static bool StrictHasReferenceConversionTo(this Type source, Type dest, bool skipNonArray)
290         {
291             // HasReferenceConversionTo was both too strict and too lax. It was too strict in prohibiting
292             // some valid conversions involving arrays, and too lax in allowing casts between interfaces
293             // and sealed classes that don't implement them. Unfortunately fixing the lax cases would be
294             // a breaking change, especially since such expressions will even work if only given null
295             // arguments.
296             // This method catches the cases that were incorrectly disallowed, but when it needs to
297             // examine possible conversions of element or type parameters it applies stricter rules.
298 
299             for(;;)
300             {
301                 if (!skipNonArray) // Skip if we just came from HasReferenceConversionTo and have just tested these
302                 {
303                     if (source.IsValueType | dest.IsValueType)
304                     {
305                         return false;
306                     }
307 
308                     // Includes to case of either being typeof(object)
309                     if (source.IsAssignableFrom(dest) || dest.IsAssignableFrom(source))
310                     {
311                         return true;
312                     }
313 
314                     if (source.IsInterface)
315                     {
316                         if (dest.IsInterface || dest.IsClass && !dest.IsSealed)
317                         {
318                             return true;
319                         }
320                     }
321                     else if (dest.IsInterface)
322                     {
323                         if (source.IsClass && !source.IsSealed)
324                         {
325                             return true;
326                         }
327                     }
328                 }
329 
330                 if (source.IsArray)
331                 {
332                     if (dest.IsArray)
333                     {
334                         if (source.GetArrayRank() != dest.GetArrayRank() || source.IsSZArray != dest.IsSZArray)
335                         {
336                             return false;
337                         }
338 
339                         source = source.GetElementType();
340                         dest = dest.GetElementType();
341                         skipNonArray = false;
342                     }
343                     else
344                     {
345                         return HasArrayToInterfaceConversion(source, dest);
346                     }
347                 }
348                 else if (dest.IsArray)
349                 {
350                     if (HasInterfaceToArrayConversion(source, dest))
351                     {
352                         return true;
353                     }
354 
355                     return IsImplicitReferenceConversion(typeof(Array), source);
356                 }
357                 else
358                 {
359                     return IsLegalExplicitVariantDelegateConversion(source, dest);
360                 }
361             }
362         }
363 
HasArrayToInterfaceConversion(Type source, Type dest)364         private static bool HasArrayToInterfaceConversion(Type source, Type dest)
365         {
366             Debug.Assert(source.IsArray);
367             if (!source.IsSZArray || !dest.IsInterface || !dest.IsGenericType)
368             {
369                 return false;
370             }
371 
372             Type[] destParams = dest.GetGenericArguments();
373             if (destParams.Length != 1)
374             {
375                 return false;
376             }
377 
378             Type destGen = dest.GetGenericTypeDefinition();
379 
380             foreach (Type iface in s_arrayAssignableInterfaces)
381             {
382                 if (AreEquivalent(destGen, iface))
383                 {
384                     return StrictHasReferenceConversionTo(source.GetElementType(), destParams[0], false);
385                 }
386             }
387 
388             return false;
389         }
390 
HasInterfaceToArrayConversion(Type source, Type dest)391         private static bool HasInterfaceToArrayConversion(Type source, Type dest)
392         {
393             Debug.Assert(dest.IsSZArray);
394             if (!dest.IsSZArray || !source.IsInterface || !source.IsGenericType)
395             {
396                 return false;
397             }
398 
399             Type[] sourceParams = source.GetGenericArguments();
400             if (sourceParams.Length != 1)
401             {
402                 return false;
403             }
404 
405             Type sourceGen = source.GetGenericTypeDefinition();
406 
407             foreach (Type iface in s_arrayAssignableInterfaces)
408             {
409                 if (AreEquivalent(sourceGen, iface))
410                 {
411                     return StrictHasReferenceConversionTo(sourceParams[0], dest.GetElementType(), false);
412                 }
413             }
414 
415             return false;
416         }
417 
IsCovariant(Type t)418         private static bool IsCovariant(Type t)
419         {
420             Debug.Assert(t != null);
421             return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Covariant);
422         }
423 
IsContravariant(Type t)424         private static bool IsContravariant(Type t)
425         {
426             Debug.Assert(t != null);
427             return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Contravariant);
428         }
429 
IsInvariant(Type t)430         private static bool IsInvariant(Type t)
431         {
432             Debug.Assert(t != null);
433             return 0 == (t.GenericParameterAttributes & GenericParameterAttributes.VarianceMask);
434         }
435 
IsDelegate(Type t)436         private static bool IsDelegate(Type t)
437         {
438             Debug.Assert(t != null);
439             return t.IsSubclassOf(typeof(MulticastDelegate));
440         }
441 
IsLegalExplicitVariantDelegateConversion(Type source, Type dest)442         public static bool IsLegalExplicitVariantDelegateConversion(Type source, Type dest)
443         {
444             Debug.Assert(source != null && dest != null);
445 
446             // There *might* be a legal conversion from a generic delegate type S to generic delegate type  T,
447             // provided all of the follow are true:
448             //   o Both types are constructed generic types of the same generic delegate type, D<X1,... Xk>.
449             //     That is, S = D<S1...>, T = D<T1...>.
450             //   o If type parameter Xi is declared to be invariant then Si must be identical to Ti.
451             //   o If type parameter Xi is declared to be covariant ("out") then Si must be convertible
452             //     to Ti via an identify conversion,  implicit reference conversion, or explicit reference conversion.
453             //   o If type parameter Xi is declared to be contravariant ("in") then either Si must be identical to Ti,
454             //     or Si and Ti must both be reference types.
455 
456             if (!IsDelegate(source) || !IsDelegate(dest) || !source.IsGenericType || !dest.IsGenericType)
457             {
458                 return false;
459             }
460 
461             Type genericDelegate = source.GetGenericTypeDefinition();
462 
463             if (dest.GetGenericTypeDefinition() != genericDelegate)
464             {
465                 return false;
466             }
467 
468             Type[] genericParameters = genericDelegate.GetGenericArguments();
469             Type[] sourceArguments = source.GetGenericArguments();
470             Type[] destArguments = dest.GetGenericArguments();
471 
472             Debug.Assert(genericParameters != null);
473             Debug.Assert(sourceArguments != null);
474             Debug.Assert(destArguments != null);
475             Debug.Assert(genericParameters.Length == sourceArguments.Length);
476             Debug.Assert(genericParameters.Length == destArguments.Length);
477 
478             for (int iParam = 0; iParam < genericParameters.Length; ++iParam)
479             {
480                 Type sourceArgument = sourceArguments[iParam];
481                 Type destArgument = destArguments[iParam];
482 
483                 Debug.Assert(sourceArgument != null && destArgument != null);
484 
485                 // If the arguments are identical then this one is automatically good, so skip it.
486                 if (AreEquivalent(sourceArgument, destArgument))
487                 {
488                     continue;
489                 }
490 
491                 Type genericParameter = genericParameters[iParam];
492 
493                 Debug.Assert(genericParameter != null);
494 
495                 if (IsInvariant(genericParameter))
496                 {
497                     return false;
498                 }
499 
500                 if (IsCovariant(genericParameter))
501                 {
502                     if (!sourceArgument.HasReferenceConversionTo(destArgument))
503                     {
504                         return false;
505                     }
506                 }
507                 else if (IsContravariant(genericParameter) && (sourceArgument.IsValueType || destArgument.IsValueType))
508                 {
509                     return false;
510                 }
511             }
512 
513             return true;
514         }
515 
IsConvertible(this Type type)516         public static bool IsConvertible(this Type type)
517         {
518             type = GetNonNullableType(type);
519             if (type.IsEnum)
520             {
521                 return true;
522             }
523 
524             switch (type.GetTypeCode())
525             {
526                 case TypeCode.Boolean:
527                 case TypeCode.Byte:
528                 case TypeCode.SByte:
529                 case TypeCode.Int16:
530                 case TypeCode.Int32:
531                 case TypeCode.Int64:
532                 case TypeCode.UInt16:
533                 case TypeCode.UInt32:
534                 case TypeCode.UInt64:
535                 case TypeCode.Single:
536                 case TypeCode.Double:
537                 case TypeCode.Char:
538                     return true;
539 
540                 default:
541                     return false;
542             }
543         }
544 
HasReferenceEquality(Type left, Type right)545         public static bool HasReferenceEquality(Type left, Type right)
546         {
547             if (left.IsValueType || right.IsValueType)
548             {
549                 return false;
550             }
551 
552             // If we have an interface and a reference type then we can do
553             // reference equality.
554 
555             // If we have two reference types and one is assignable to the
556             // other then we can do reference equality.
557 
558             return left.IsInterface || right.IsInterface || AreReferenceAssignable(left, right)
559                    || AreReferenceAssignable(right, left);
560         }
561 
HasBuiltInEqualityOperator(Type left, Type right)562         public static bool HasBuiltInEqualityOperator(Type left, Type right)
563         {
564             // If we have an interface and a reference type then we can do
565             // reference equality.
566             if (left.IsInterface && !right.IsValueType)
567             {
568                 return true;
569             }
570 
571             if (right.IsInterface && !left.IsValueType)
572             {
573                 return true;
574             }
575 
576             // If we have two reference types and one is assignable to the
577             // other then we can do reference equality.
578             if (!left.IsValueType && !right.IsValueType)
579             {
580                 if (AreReferenceAssignable(left, right) || AreReferenceAssignable(right, left))
581                 {
582                     return true;
583                 }
584             }
585 
586             // Otherwise, if the types are not the same then we definitely
587             // do not have a built-in equality operator.
588             if (!AreEquivalent(left, right))
589             {
590                 return false;
591             }
592 
593             // We have two identical value types, modulo nullability.  (If they were both the
594             // same reference type then we would have returned true earlier.)
595             Debug.Assert(left.IsValueType);
596 
597             // Equality between struct types is only defined for numerics, bools, enums,
598             // and their nullable equivalents.
599             Type nnType = GetNonNullableType(left);
600             return nnType == typeof(bool) || IsNumeric(nnType) || nnType.IsEnum;
601         }
602 
IsImplicitlyConvertibleTo(this Type source, Type destination)603         public static bool IsImplicitlyConvertibleTo(this Type source, Type destination) =>
604             AreEquivalent(source, destination) // identity conversion
605             || IsImplicitNumericConversion(source, destination)
606             || IsImplicitReferenceConversion(source, destination)
607             || IsImplicitBoxingConversion(source, destination)
608             || IsImplicitNullableConversion(source, destination);
609 
GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType)610         public static MethodInfo GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType)
611         {
612             Type nnExprType = GetNonNullableType(convertFrom);
613             Type nnConvType = GetNonNullableType(convertToType);
614 
615             // try exact match on types
616             MethodInfo[] eMethods = nnExprType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
617 
618             MethodInfo method = FindConversionOperator(eMethods, convertFrom, convertToType);
619             if (method != null)
620             {
621                 return method;
622             }
623 
624             MethodInfo[] cMethods = nnConvType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
625 
626             method = FindConversionOperator(cMethods, convertFrom, convertToType);
627             if (method != null)
628             {
629                 return method;
630             }
631 
632             if (AreEquivalent(nnExprType, convertFrom) && AreEquivalent(nnConvType, convertToType))
633             {
634                 return null;
635             }
636 
637             // try lifted conversion
638             return FindConversionOperator(eMethods, nnExprType, nnConvType)
639                    ?? FindConversionOperator(cMethods, nnExprType, nnConvType)
640                    ?? FindConversionOperator(eMethods, nnExprType, convertToType)
641                    ?? FindConversionOperator(cMethods, nnExprType, convertToType);
642         }
643 
FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo)644         private static MethodInfo FindConversionOperator(MethodInfo[] methods, Type typeFrom, Type typeTo)
645         {
646             foreach (MethodInfo mi in methods)
647             {
648                 if ((mi.Name == "op_Implicit" || mi.Name == "op_Explicit") && AreEquivalent(mi.ReturnType, typeTo))
649                 {
650                     ParameterInfo[] pis = mi.GetParametersCached();
651                     if (pis.Length == 1 && AreEquivalent(pis[0].ParameterType, typeFrom))
652                     {
653                         return mi;
654                     }
655                 }
656             }
657 
658             return null;
659         }
660 
661         [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
IsImplicitNumericConversion(Type source, Type destination)662         private static bool IsImplicitNumericConversion(Type source, Type destination)
663         {
664             TypeCode tcSource = source.GetTypeCode();
665             TypeCode tcDest = destination.GetTypeCode();
666 
667             switch (tcSource)
668             {
669                 case TypeCode.SByte:
670                     switch (tcDest)
671                     {
672                         case TypeCode.Int16:
673                         case TypeCode.Int32:
674                         case TypeCode.Int64:
675                         case TypeCode.Single:
676                         case TypeCode.Double:
677                         case TypeCode.Decimal:
678                             return true;
679                     }
680 
681                     break;
682                 case TypeCode.Byte:
683                     switch (tcDest)
684                     {
685                         case TypeCode.Int16:
686                         case TypeCode.UInt16:
687                         case TypeCode.Int32:
688                         case TypeCode.UInt32:
689                         case TypeCode.Int64:
690                         case TypeCode.UInt64:
691                         case TypeCode.Single:
692                         case TypeCode.Double:
693                         case TypeCode.Decimal:
694                             return true;
695                     }
696 
697                     break;
698                 case TypeCode.Int16:
699                     switch (tcDest)
700                     {
701                         case TypeCode.Int32:
702                         case TypeCode.Int64:
703                         case TypeCode.Single:
704                         case TypeCode.Double:
705                         case TypeCode.Decimal:
706                             return true;
707                     }
708 
709                     break;
710                 case TypeCode.UInt16:
711                     switch (tcDest)
712                     {
713                         case TypeCode.Int32:
714                         case TypeCode.UInt32:
715                         case TypeCode.Int64:
716                         case TypeCode.UInt64:
717                         case TypeCode.Single:
718                         case TypeCode.Double:
719                         case TypeCode.Decimal:
720                             return true;
721                     }
722 
723                     break;
724                 case TypeCode.Int32:
725                     switch (tcDest)
726                     {
727                         case TypeCode.Int64:
728                         case TypeCode.Single:
729                         case TypeCode.Double:
730                         case TypeCode.Decimal:
731                             return true;
732                     }
733 
734                     break;
735                 case TypeCode.UInt32:
736                     switch (tcDest)
737                     {
738                         case TypeCode.Int64:
739                         case TypeCode.UInt64:
740                         case TypeCode.Single:
741                         case TypeCode.Double:
742                         case TypeCode.Decimal:
743                             return true;
744                     }
745 
746                     break;
747                 case TypeCode.Int64:
748                 case TypeCode.UInt64:
749                     switch (tcDest)
750                     {
751                         case TypeCode.Single:
752                         case TypeCode.Double:
753                         case TypeCode.Decimal:
754                             return true;
755                     }
756 
757                     break;
758                 case TypeCode.Char:
759                     switch (tcDest)
760                     {
761                         case TypeCode.UInt16:
762                         case TypeCode.Int32:
763                         case TypeCode.UInt32:
764                         case TypeCode.Int64:
765                         case TypeCode.UInt64:
766                         case TypeCode.Single:
767                         case TypeCode.Double:
768                         case TypeCode.Decimal:
769                             return true;
770                     }
771 
772                     break;
773                 case TypeCode.Single:
774                     return tcDest == TypeCode.Double;
775             }
776 
777             return false;
778         }
779 
IsImplicitReferenceConversion(Type source, Type destination)780         private static bool IsImplicitReferenceConversion(Type source, Type destination) =>
781             destination.IsAssignableFrom(source);
782 
IsImplicitBoxingConversion(Type source, Type destination)783         private static bool IsImplicitBoxingConversion(Type source, Type destination) =>
784             source.IsValueType && (destination == typeof(object) || destination == typeof(ValueType)) || source.IsEnum && destination == typeof(Enum);
785 
IsImplicitNullableConversion(Type source, Type destination)786         private static bool IsImplicitNullableConversion(Type source, Type destination) =>
787             IsNullableType(destination) && IsImplicitlyConvertibleTo(GetNonNullableType(source), GetNonNullableType(destination));
788 
FindGenericType(Type definition, Type type)789         public static Type FindGenericType(Type definition, Type type)
790         {
791             while ((object)type != null && type != typeof(object))
792             {
793                 if (type.IsConstructedGenericType && AreEquivalent(type.GetGenericTypeDefinition(), definition))
794                 {
795                     return type;
796                 }
797 
798                 if (definition.IsInterface)
799                 {
800                     foreach (Type itype in type.GetTypeInfo().ImplementedInterfaces)
801                     {
802                         Type found = FindGenericType(definition, itype);
803                         if (found != null)
804                         {
805                             return found;
806                         }
807                     }
808                 }
809 
810                 type = type.BaseType;
811             }
812 
813             return null;
814         }
815 
816         /// <summary>
817         /// Searches for an operator method on the type. The method must have
818         /// the specified signature, no generic arguments, and have the
819         /// SpecialName bit set. Also searches inherited operator methods.
820         ///
821         /// NOTE: This was designed to satisfy the needs of op_True and
822         /// op_False, because we have to do runtime lookup for those. It may
823         /// not work right for unary operators in general.
824         /// </summary>
GetBooleanOperator(Type type, string name)825         public static MethodInfo GetBooleanOperator(Type type, string name)
826         {
827             do
828             {
829                 MethodInfo result = type.GetAnyStaticMethodValidated(name, new[] { type });
830                 if (result != null && result.IsSpecialName && !result.ContainsGenericParameters)
831                 {
832                     return result;
833                 }
834 
835                 type = type.BaseType;
836             } while (type != null);
837 
838             return null;
839         }
840 
GetNonRefType(this Type type)841         public static Type GetNonRefType(this Type type) => type.IsByRef ? type.GetElementType() : type;
842 
AreEquivalent(Type t1, Type t2)843         public static bool AreEquivalent(Type t1, Type t2) => t1 != null && t1.IsEquivalentTo(t2);
844 
AreReferenceAssignable(Type dest, Type src)845         public static bool AreReferenceAssignable(Type dest, Type src)
846         {
847             // This actually implements "Is this identity assignable and/or reference assignable?"
848             if (AreEquivalent(dest, src))
849             {
850                 return true;
851             }
852 
853             return !dest.IsValueType && !src.IsValueType && dest.IsAssignableFrom(src);
854         }
855 
IsSameOrSubclass(Type type, Type subType)856         public static bool IsSameOrSubclass(Type type, Type subType) =>
857             AreEquivalent(type, subType) || subType.IsSubclassOf(type);
858 
ValidateType(Type type, string paramName)859         public static void ValidateType(Type type, string paramName) => ValidateType(type, paramName, false, false);
860 
ValidateType(Type type, string paramName, bool allowByRef, bool allowPointer)861         public static void ValidateType(Type type, string paramName, bool allowByRef, bool allowPointer)
862         {
863             if (ValidateType(type, paramName, -1))
864             {
865                 if (!allowByRef && type.IsByRef)
866                 {
867                     throw System.Linq.Expressions.Error.TypeMustNotBeByRef(paramName);
868                 }
869 
870                 if (!allowPointer && type.IsPointer)
871                 {
872                     throw System.Linq.Expressions.Error.TypeMustNotBePointer(paramName);
873                 }
874             }
875         }
876 
ValidateType(Type type, string paramName, int index)877         public static bool ValidateType(Type type, string paramName, int index)
878         {
879             if (type == typeof(void))
880             {
881                 return false; // Caller can skip further checks.
882             }
883 
884             if (type.ContainsGenericParameters)
885             {
886                 throw type.IsGenericTypeDefinition
887                     ? System.Linq.Expressions.Error.TypeIsGeneric(type, paramName, index)
888                     : System.Linq.Expressions.Error.TypeContainsGenericParameters(type, paramName, index);
889             }
890 
891             return true;
892         }
893 
GetInvokeMethod(this Type delegateType)894         public static MethodInfo GetInvokeMethod(this Type delegateType)
895         {
896             Debug.Assert(typeof(Delegate).IsAssignableFrom(delegateType));
897             return delegateType.GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
898         }
899 
900 #if FEATURE_COMPILE
901 
IsUnsigned(this Type type)902         internal static bool IsUnsigned(this Type type) => IsUnsigned(GetNonNullableType(type).GetTypeCode());
903 
IsUnsigned(this TypeCode typeCode)904         internal static bool IsUnsigned(this TypeCode typeCode)
905         {
906             switch (typeCode)
907             {
908                 case TypeCode.Byte:
909                 case TypeCode.UInt16:
910                 case TypeCode.Char:
911                 case TypeCode.UInt32:
912                 case TypeCode.UInt64:
913                     return true;
914 
915                 default:
916                     return false;
917             }
918         }
919 
IsFloatingPoint(this Type type)920         internal static bool IsFloatingPoint(this Type type) => IsFloatingPoint(GetNonNullableType(type).GetTypeCode());
921 
IsFloatingPoint(this TypeCode typeCode)922         internal static bool IsFloatingPoint(this TypeCode typeCode)
923         {
924             switch (typeCode)
925             {
926                 case TypeCode.Single:
927                 case TypeCode.Double:
928                     return true;
929 
930                 default:
931                     return false;
932             }
933         }
934 
935 #endif
936     }
937 }
938