private bool bindExplicitConversionFromIListToArray(ArrayType arrayDest) { // 13.2.2 // // The explicit reference conversions are: // // * From System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyList<T> and their base interfaces // to a one-dimensional array-type S[], provided there is an implicit or explicit reference conversion from // S[] to System.Collections.Generic.IList<T> or System.Collections.Generic.IReadOnlyList<T>. This is precisely when either S and T // are the same type or there is an implicit or explicit reference conversion from S to T. if (!arrayDest.IsSZArray || !(_typeSrc is AggregateType aggSrc) || !aggSrc.isInterfaceType() || aggSrc.GetTypeArgsAll().Count != 1) { return(false); } AggregateSymbol aggIList = GetSymbolLoader().GetPredefAgg(PredefinedType.PT_G_ILIST); AggregateSymbol aggIReadOnlyList = GetSymbolLoader().GetPredefAgg(PredefinedType.PT_G_IREADONLYLIST); if ((aggIList == null || !GetSymbolLoader().IsBaseAggregate(aggIList, aggSrc.getAggregate())) && (aggIReadOnlyList == null || !GetSymbolLoader().IsBaseAggregate(aggIReadOnlyList, aggSrc.getAggregate()))) { return(false); } CType typeArr = arrayDest.GetElementType(); CType typeLst = aggSrc.GetTypeArgsAll()[0]; Debug.Assert(!typeArr.IsNeverSameType()); if (typeArr != typeLst && !CConversions.FExpRefConv(GetSymbolLoader(), typeArr, typeLst)) { return(false); } if (_needsExprDest) { _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_REFCHECK); } return(true); }
/* * BindExplicitConversion * * This is a complex routine with complex parameter. Generally, this should * be called through one of the helper methods that insulates you * from the complexity of the interface. This routine handles all the logic * associated with explicit conversions. * * Note that this function calls BindImplicitConversion first, so the main * logic is only concerned with conversions that can be made explicitly, but * not implicitly. */ public bool Bind() { // To test for a standard conversion, call canConvert(exprSrc, typeDest, STANDARDANDCONVERTTYPE.NOUDC) and // canConvert(typeDest, typeSrc, STANDARDANDCONVERTTYPE.NOUDC). Debug.Assert((_flags & CONVERTTYPE.STANDARD) == 0); // 13.2 Explicit conversions // // The following conversions are classified as explicit conversions: // // * All implicit conversions // * Explicit numeric conversions // * Explicit enumeration conversions // * Explicit reference conversions // * Explicit interface conversions // * Unboxing conversions // * Explicit type parameter conversions // * User-defined explicit conversions // * Explicit nullable conversions // * Lifted user-defined explicit conversions // // Explicit conversions can occur in cast expressions (14.6.6). // // The explicit conversions that are not implicit conversions are conversions that cannot be // proven always to succeed, conversions that are known possibly to lose information, and // conversions across domains of types sufficiently different to merit explicit notation. // The set of explicit conversions includes all implicit conversions. // Don't try user-defined conversions now because we'll try them again later. if (_binder.BindImplicitConversion(_exprSrc, _typeSrc, _exprTypeDest, _pDestinationTypeForLambdaErrorReporting, _needsExprDest, out _exprDest, _flags | CONVERTTYPE.ISEXPLICIT)) { return(true); } if (_typeSrc == null || _typeDest == null || _typeSrc is ErrorType || _typeDest is ErrorType || _typeDest.IsNeverSameType()) { return(false); } if (_typeDest is NullableType) { // This is handled completely by BindImplicitConversion. return(false); } if (_typeSrc is NullableType) { return(bindExplicitConversionFromNub()); } if (bindExplicitConversionFromArrayToIList()) { return(true); } // if we were casting an integral constant to another constant type, // then, if the constant were in range, then the above call would have succeeded. // But it failed, and so we know that the constant is not in range switch (_typeDest.GetTypeKind()) { default: VSFAIL("Bad type kind"); return(false); case TypeKind.TK_VoidType: return(false); // Can't convert to a method group or anon method. case TypeKind.TK_NullType: return(false); // Can never convert TO the null type. case TypeKind.TK_TypeParameterType: if (bindExplicitConversionToTypeVar()) { return(true); } break; case TypeKind.TK_ArrayType: if (bindExplicitConversionToArray((ArrayType)_typeDest)) { return(true); } break; case TypeKind.TK_PointerType: if (bindExplicitConversionToPointer()) { return(true); } break; case TypeKind.TK_AggregateType: { AggCastResult result = bindExplicitConversionToAggregate(_typeDest as AggregateType); if (result == AggCastResult.Success) { return(true); } if (result == AggCastResult.Abort) { return(false); } break; } } // No built-in conversion was found. Maybe a user-defined conversion? if (0 == (_flags & CONVERTTYPE.NOUDC)) { return(_binder.bindUserDefinedConversion(_exprSrc, _typeSrc, _typeDest, _needsExprDest, out _exprDest, false)); } return(false); }
/* * BindImplicitConversion * * This is a complex routine with complex parameters. Generally, this should * be called through one of the helper methods that insulates you * from the complexity of the interface. This routine handles all the logic * associated with implicit conversions. * * exprSrc - the expression being converted. Can be null if only type conversion * info is being supplied. * typeSrc - type of the source * typeDest - type of the destination * exprDest - returns an expression of the src converted to the dest. If null, we * only care about whether the conversion can be attempted, not the * expression tree. * flags - flags possibly customizing the conversions allowed. E.g., can suppress * user-defined conversions. * * returns true if the conversion can be made, false if not. */ public bool Bind() { // 13.1 Implicit conversions // // The following conversions are classified as implicit conversions: // // * Identity conversions // * Implicit numeric conversions // * Implicit enumeration conversions // * Implicit reference conversions // * Boxing conversions // * Implicit type parameter conversions // * Implicit constant expression conversions // * User-defined implicit conversions // * Implicit conversions from an anonymous method expression to a compatible delegate type // * Implicit conversion from a method group to a compatible delegate type // * Conversions from the null type (11.2.7) to any nullable type // * Implicit nullable conversions // * Lifted user-defined implicit conversions // // Implicit conversions can occur in a variety of situations, including function member invocations // (14.4.3), cast expressions (14.6.6), and assignments (14.14). // Can't convert to or from the error type. if (_typeSrc == null || _typeDest == null || _typeDest.IsNeverSameType()) { return(false); } Debug.Assert(_typeSrc != null && _typeDest != null); // types must be supplied. Debug.Assert(_exprSrc == null || _typeSrc == _exprSrc.Type); // type of source should be correct if source supplied Debug.Assert(!_needsExprDest || _exprSrc != null); // need source expr to create dest expr switch (_typeDest.GetTypeKind()) { case TypeKind.TK_ErrorType: Debug.Assert(((ErrorType)_typeDest).HasParent); if (_typeSrc != _typeDest) { return(false); } if (_needsExprDest) { _exprDest = _exprSrc; } return(true); case TypeKind.TK_NullType: // Can only convert to the null type if src is null. if (!(_typeSrc is NullType)) { return(false); } if (_needsExprDest) { _exprDest = _exprSrc; } return(true); case TypeKind.TK_MethodGroupType: Debug.Fail("Something is wrong with Type.IsNeverSameType()"); return(false); case TypeKind.TK_ArgumentListType: return(_typeSrc == _typeDest); case TypeKind.TK_VoidType: return(false); default: break; } if (_typeSrc is ErrorType) { Debug.Assert(!(_typeDest is ErrorType)); return(false); } // 13.1.1 Identity conversion // // An identity conversion converts from any type to the same type. This conversion exists only // such that an entity that already has a required type can be said to be convertible to that type. if (_typeSrc == _typeDest && ((_flags & CONVERTTYPE.ISEXPLICIT) == 0 || (!_typeSrc.isPredefType(PredefinedType.PT_FLOAT) && !_typeSrc.isPredefType(PredefinedType.PT_DOUBLE)))) { if (_needsExprDest) { _exprDest = _exprSrc; } return(true); } if (_typeDest is NullableType nubDest) { return(BindNubConversion(nubDest)); } if (_typeSrc is NullableType nubSrc) { return(bindImplicitConversionFromNullable(nubSrc)); } if ((_flags & CONVERTTYPE.ISEXPLICIT) != 0) { _flags |= CONVERTTYPE.NOUDC; } // Get the fundamental types of destination. FUNDTYPE ftDest = _typeDest.fundType(); Debug.Assert(ftDest != FUNDTYPE.FT_NONE || _typeDest is ParameterModifierType); switch (_typeSrc.GetTypeKind()) { default: Debug.Fail($"Bad type symbol kind: {_typeSrc.GetTypeKind()}"); break; case TypeKind.TK_VoidType: case TypeKind.TK_ErrorType: case TypeKind.TK_ParameterModifierType: case TypeKind.TK_ArgumentListType: return(false); case TypeKind.TK_NullType: if (bindImplicitConversionFromNull()) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_ArrayType: if (bindImplicitConversionFromArray()) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_PointerType: if (bindImplicitConversionFromPointer()) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_AggregateType: if (bindImplicitConversionFromAgg(_typeSrc as AggregateType)) { return(true); } // If not, try user defined implicit conversions. break; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // Every incoming dynamic operand should be implicitly convertible // to any type that it is an instance of. object srcRuntimeObject = _exprSrc?.RuntimeObject; if (srcRuntimeObject != null && _typeDest.AssociatedSystemType.IsInstanceOfType(srcRuntimeObject) && _binder.GetSemanticChecker().CheckTypeAccess(_typeDest, _binder.Context.ContextForMemberLookup)) { if (_needsExprDest) { _binder.bindSimpleCast(_exprSrc, _typeDest, out _exprDest, _exprSrc.Flags & EXPRFLAG.EXF_CANTBENULL); } return(true); } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // END RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // 13.1.8 User-defined implicit conversions // // A user-defined implicit conversion consists of an optional standard implicit conversion, // followed by execution of a user-defined implicit conversion operator, followed by another // optional standard implicit conversion. The exact rules for evaluating user-defined // conversions are described in 13.4.3. if (0 == (_flags & CONVERTTYPE.NOUDC)) { return(_binder.bindUserDefinedConversion(_exprSrc, _typeSrc, _typeDest, _needsExprDest, out _exprDest, true)); } // No conversion was found. return(false); }
/* * BindImplicitConversion * * This is a complex routine with complex parameters. Generally, this should * be called through one of the helper methods that insulates you * from the complexity of the interface. This routine handles all the logic * associated with implicit conversions. * * exprSrc - the expression being converted. Can be null if only type conversion * info is being supplied. * typeSrc - type of the source * typeDest - type of the destination * exprDest - returns an expression of the src converted to the dest. If null, we * only care about whether the conversion can be attempted, not the * expression tree. * flags - flags possibly customizing the conversions allowed. E.g., can suppress * user-defined conversions. * * returns true if the conversion can be made, false if not. */ public bool Bind() { // 13.1 Implicit conversions // // The following conversions are classified as implicit conversions: // // * Identity conversions // * Implicit numeric conversions // * Implicit enumeration conversions // * Implicit reference conversions // * Boxing conversions // * Implicit type parameter conversions // * Implicit constant expression conversions // * User-defined implicit conversions // * Implicit conversions from an anonymous method expression to a compatible delegate type // * Implicit conversion from a method group to a compatible delegate type // * Conversions from the null type (11.2.7) to any nullable type // * Implicit nullable conversions // * Lifted user-defined implicit conversions // // Implicit conversions can occur in a variety of situations, including function member invocations // (14.4.3), cast expressions (14.6.6), and assignments (14.14). // Can't convert to or from the error type. if (typeSrc == null || typeDest == null || typeDest.IsNeverSameType()) { return(false); } Debug.Assert(typeSrc != null && typeDest != null); // types must be supplied. Debug.Assert(exprSrc == null || typeSrc == exprSrc.type); // type of source should be correct if source supplied Debug.Assert(!needsExprDest || exprSrc != null); // need source expr to create dest expr switch (typeDest.GetTypeKind()) { case TypeKind.TK_ErrorType: Debug.Assert(typeDest.AsErrorType().HasTypeParent() || typeDest.AsErrorType().HasNSParent()); if (typeSrc != typeDest) { return(false); } if (needsExprDest) { exprDest = exprSrc; } return(true); case TypeKind.TK_NullType: // Can only convert to the null type if src is null. if (!typeSrc.IsNullType()) { return(false); } if (needsExprDest) { exprDest = exprSrc; } return(true); case TypeKind.TK_MethodGroupType: VSFAIL("Something is wrong with Type.IsNeverSameType()"); return(false); case TypeKind.TK_NaturalIntegerType: case TypeKind.TK_ArgumentListType: return(typeSrc == typeDest); case TypeKind.TK_VoidType: return(false); default: break; } if (typeSrc.IsErrorType()) { Debug.Assert(!typeDest.IsErrorType()); return(false); } // 13.1.1 Identity conversion // // An identity conversion converts from any type to the same type. This conversion exists only // such that an entity that already has a required type can be said to be convertible to that type. if (typeSrc == typeDest && ((flags & CONVERTTYPE.ISEXPLICIT) == 0 || (!typeSrc.isPredefType(PredefinedType.PT_FLOAT) && !typeSrc.isPredefType(PredefinedType.PT_DOUBLE)))) { if (needsExprDest) { exprDest = exprSrc; } return(true); } if (typeDest.IsNullableType()) { return(BindNubConversion(typeDest.AsNullableType())); } if (typeSrc.IsNullableType()) { return(bindImplicitConversionFromNullable(typeSrc.AsNullableType())); } if ((flags & CONVERTTYPE.ISEXPLICIT) != 0) { flags |= CONVERTTYPE.NOUDC; } // Get the fundamental types of destination. FUNDTYPE ftDest = typeDest.fundType(); Debug.Assert(ftDest != FUNDTYPE.FT_NONE || typeDest.IsParameterModifierType()); switch (typeSrc.GetTypeKind()) { default: VSFAIL("Bad type symbol kind"); break; case TypeKind.TK_MethodGroupType: if (exprSrc.isMEMGRP()) { EXPRCALL outExpr; bool retVal = binder.BindGrpConversion(exprSrc.asMEMGRP(), typeDest, needsExprDest, out outExpr, false); exprDest = outExpr; return(retVal); } return(false); case TypeKind.TK_VoidType: case TypeKind.TK_ErrorType: case TypeKind.TK_ParameterModifierType: case TypeKind.TK_ArgumentListType: return(false); case TypeKind.TK_NullType: if (bindImplicitConversionFromNull()) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_ArrayType: if (bindImplicitConversionFromArray()) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_PointerType: if (bindImplicitConversionFromPointer()) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_TypeParameterType: if (bindImplicitConversionFromTypeVar(typeSrc.AsTypeParameterType())) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_AggregateType: // TypeReference and ArgIterator can't be boxed (or converted to anything else) if (typeSrc.isSpecialByRefType()) { return(false); } if (bindImplicitConversionFromAgg(typeSrc.AsAggregateType())) { return(true); } // If not, try user defined implicit conversions. break; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // Every incoming dynamic operand should be implicitly convertible // to any type that it is an instance of. if (exprSrc != null && exprSrc.RuntimeObject != null && typeDest.AssociatedSystemType.IsInstanceOfType(exprSrc.RuntimeObject) && binder.GetSemanticChecker().CheckTypeAccess(typeDest, binder.Context.ContextForMemberLookup())) { if (needsExprDest) { binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, exprSrc.flags & EXPRFLAG.EXF_CANTBENULL); } return(true); } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // END RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // 13.1.8 User-defined implicit conversions // // A user-defined implicit conversion consists of an optional standard implicit conversion, // followed by execution of a user-defined implicit conversion operator, followed by another // optional standard implicit conversion. The exact rules for evaluating user-defined // conversions are described in 13.4.3. if (0 == (flags & CONVERTTYPE.NOUDC)) { return(binder.bindUserDefinedConversion(exprSrc, typeSrc, typeDest, needsExprDest, out exprDest, true)); } // No conversion was found. return(false); }
/*************************************************************************************************** 13.1.1 Identity conversion An identity conversion converts from any type to the same type. This conversion exists only such that an entity that already has a required type can be said to be convertible to that type. Always returns false if the types are error, anonymous method, or method group ***************************************************************************************************/ public static bool FIsSameType(CType typeSrc, CType typeDst) { return typeSrc == typeDst && !typeSrc.IsNeverSameType(); }
/*************************************************************************************************** * Determine whether there is an explicit or implicit reference conversion (or identity conversion) * from typeSrc to typeDst. This is when: * * 13.2.3 Explicit reference conversions * * The explicit reference conversions are: * From object to any reference-type. * From any class-type S to any class-type T, provided S is a base class of T. * From any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T. * From any interface-type S to any class-type T, provided T is not sealed or provided T implements S. * From any interface-type S to any interface-type T, provided S is not derived from T. * From an array-type S with an element type SE to an array-type T with an element type TE, provided all of the following are true: * o S and T differ only in element type. (In other words, S and T have the same number of dimensions.) * o An explicit reference conversion exists from SE to TE. * From System.Array and the interfaces it implements, to any array-type. * From System.Delegate and the interfaces it implements, to any delegate-type. * From a one-dimensional array-type S[] to System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyList<T> and their base interfaces, provided there is an explicit reference conversion from S to T. * From a generic delegate type S to generic delegate type T, provided all of the follow are true: * o Both types are constructed generic types of the same generic delegate type, D<X1,... Xk>.That is, * S is D<S1,... Sk> and T is D<T1,... Tk>. * o S is not compatible with or identical to T. * o If type parameter Xi is declared to be invariant then Si must be identical to Ti. * o If type parameter Xi is declared to be covariant ("out") then Si must be convertible * to Ti via an identify conversion, implicit reference conversion, or explicit reference conversion. * o If type parameter Xi is declared to be contravariant ("in") then either Si must be identical to Ti, * or Si and Ti must both be reference types. * From System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyList<T> and their base interfaces to a one-dimensional array-type S[], provided there is an implicit or explicit reference conversion from S[] to System.Collections.Generic.IList<T> or System.Collections.Generic.IReadOnlyList<T>. This is precisely when either S and T are the same type or there is an implicit or explicit reference conversion from S to T. * * For a type-parameter T that is known to be a reference type (§25.7), the following explicit reference conversions exist: * From the effective base class C of T to T and from any base class of C to T. * From any interface-type to T. * From T to any interface-type I provided there isn’t already an implicit reference conversion from T to I. * From a type-parameter U to T provided that T depends on U (§25.7). [Note: Since T is known to be a reference type, within the scope of T, the run-time type of U will always be a reference type, even if U is not known to be a reference type at compile-time. end note] * * Both src and dst are reference types and there is a builtin explicit conversion from * src to dst. * Or src is a reference type and dst is a base type of src (in which case the conversion is * implicit as well). * Or dst is a reference type and src is a base type of dst. * * The latter two cases can happen with type variables even though the other type variable is not * a reference type. ***************************************************************************************************/ public static bool FExpRefConv(SymbolLoader loader, CType typeSrc, CType typeDst) { Debug.Assert(typeSrc != null); Debug.Assert(typeDst != null); if (typeSrc.IsRefType() && typeDst.IsRefType()) { // is there an implicit reference conversion in either direction? // this handles the bulk of the cases ... if (loader.HasIdentityOrImplicitReferenceConversion(typeSrc, typeDst) || loader.HasIdentityOrImplicitReferenceConversion(typeDst, typeSrc)) { return(true); } // For a type-parameter T that is known to be a reference type (§25.7), the following explicit reference conversions exist: // • From any interface-type to T. // • From T to any interface-type I provided there isn’t already an implicit reference conversion from T to I. if (typeSrc.isInterfaceType() && typeDst.IsTypeParameterType()) { return(true); } if (typeSrc.IsTypeParameterType() && typeDst.isInterfaceType()) { return(true); } // * From any class-type S to any interface-type T, provided S is not sealed // * From any interface-type S to any class-type T, provided T is not sealed // * From any interface-type S to any interface-type T, provided S is not derived from T. if (typeSrc.IsAggregateType() && typeDst.IsAggregateType()) { AggregateSymbol aggSrc = typeSrc.AsAggregateType().getAggregate(); AggregateSymbol aggDest = typeDst.AsAggregateType().getAggregate(); if ((aggSrc.IsClass() && !aggSrc.IsSealed() && aggDest.IsInterface()) || (aggSrc.IsInterface() && aggDest.IsClass() && !aggDest.IsSealed()) || (aggSrc.IsInterface() && aggDest.IsInterface())) { return(true); } } // * From an array-type S with an element type SE to an array-type T with an element type TE, provided all of the following are true: // o S and T differ only in element type. (In other words, S and T have the same number of dimensions.) // o An explicit reference conversion exists from SE to TE. if (typeSrc.IsArrayType() && typeDst.IsArrayType()) { return(typeSrc.AsArrayType().rank == typeDst.AsArrayType().rank&& FExpRefConv(loader, typeSrc.AsArrayType().GetElementType(), typeDst.AsArrayType().GetElementType())); } // * From a one-dimensional array-type S[] to System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyList<T> // and their base interfaces, provided there is an explicit reference conversion from S to T. if (typeSrc.IsArrayType()) { if (typeSrc.AsArrayType().rank != 1 || !typeDst.isInterfaceType() || typeDst.AsAggregateType().GetTypeArgsAll().Size != 1) { return(false); } AggregateSymbol aggIList = loader.GetOptPredefAgg(PredefinedType.PT_G_ILIST); AggregateSymbol aggIReadOnlyList = loader.GetOptPredefAgg(PredefinedType.PT_G_IREADONLYLIST); if ((aggIList == null || !loader.IsBaseAggregate(aggIList, typeDst.AsAggregateType().getAggregate())) && (aggIReadOnlyList == null || !loader.IsBaseAggregate(aggIReadOnlyList, typeDst.AsAggregateType().getAggregate()))) { return(false); } return(FExpRefConv(loader, typeSrc.AsArrayType().GetElementType(), typeDst.AsAggregateType().GetTypeArgsAll().Item(0))); } if (typeDst.IsArrayType() && typeSrc.IsAggregateType()) { // * From System.Array and the interfaces it implements, to any array-type. if (loader.HasIdentityOrImplicitReferenceConversion(loader.GetReqPredefType(PredefinedType.PT_ARRAY), typeSrc)) { return(true); } // * From System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyList<T> and their base interfaces to a // one-dimensional array-type S[], provided there is an implicit or explicit reference conversion from S[] to // System.Collections.Generic.IList<T> or System.Collections.Generic.IReadOnlyList<T>. This is precisely when either S and T // are the same type or there is an implicit or explicit reference conversion from S to T. ArrayType arrayDest = typeDst.AsArrayType(); AggregateType aggtypeSrc = typeSrc.AsAggregateType(); if (arrayDest.rank != 1 || !typeSrc.isInterfaceType() || aggtypeSrc.GetTypeArgsAll().Size != 1) { return(false); } AggregateSymbol aggIList = loader.GetOptPredefAgg(PredefinedType.PT_G_ILIST); AggregateSymbol aggIReadOnlyList = loader.GetOptPredefAgg(PredefinedType.PT_G_IREADONLYLIST); if ((aggIList == null || !loader.IsBaseAggregate(aggIList, aggtypeSrc.getAggregate())) && (aggIReadOnlyList == null || !loader.IsBaseAggregate(aggIReadOnlyList, aggtypeSrc.getAggregate()))) { return(false); } CType typeArr = arrayDest.GetElementType(); CType typeLst = aggtypeSrc.GetTypeArgsAll().Item(0); Debug.Assert(!typeArr.IsNeverSameType()); return(typeArr == typeLst || FExpRefConv(loader, typeArr, typeLst)); } if (HasGenericDelegateExplicitReferenceConversion(loader, typeSrc, typeDst)) { return(true); } } else if (typeSrc.IsRefType()) { // conversion of T . U, where T : class, U // .. these constraints implies where U : class return(loader.HasIdentityOrImplicitReferenceConversion(typeSrc, typeDst)); } else if (typeDst.IsRefType()) { // conversion of T . U, where U : class, T // .. these constraints implies where T : class return(loader.HasIdentityOrImplicitReferenceConversion(typeDst, typeSrc)); } return(false); }
/*************************************************************************************************** * 13.1.1 Identity conversion * * An identity conversion converts from any type to the same type. This conversion exists only * such that an entity that already has a required type can be said to be convertible to that type. * * Always returns false if the types are error, anonymous method, or method group ***************************************************************************************************/ public static bool FIsSameType(CType typeSrc, CType typeDst) { return(typeSrc == typeDst && !typeSrc.IsNeverSameType()); }