private static Type CalculateAssociatedSystemType(CType src) { Type result = null; switch (src.GetTypeKind()) { case TypeKind.TK_ArrayType: ArrayType a = src.AsArrayType(); Type elementType = a.GetElementType().AssociatedSystemType; if (a.rank == 1) { result = elementType.MakeArrayType(); } else { result = elementType.MakeArrayType(a.rank); } break; case TypeKind.TK_NullableType: NullableType n = src.AsNullableType(); Type underlyingType = n.GetUnderlyingType().AssociatedSystemType; result = typeof(Nullable <>).MakeGenericType(underlyingType); break; case TypeKind.TK_PointerType: PointerType p = src.AsPointerType(); Type referentType = p.GetReferentType().AssociatedSystemType; result = referentType.MakePointerType(); break; case TypeKind.TK_ParameterModifierType: ParameterModifierType r = src.AsParameterModifierType(); Type parameterType = r.GetParameterType().AssociatedSystemType; result = parameterType.MakeByRefType(); break; case TypeKind.TK_AggregateType: result = CalculateAssociatedSystemTypeForAggregate(src.AsAggregateType()); break; case TypeKind.TK_TypeParameterType: TypeParameterType t = src.AsTypeParameterType(); if (t.IsMethodTypeParameter()) { MethodInfo meth = t.GetOwningSymbol().AsMethodSymbol().AssociatedMemberInfo as MethodInfo; result = meth.GetGenericArguments()[t.GetIndexInOwnParameters()]; } else { Type parentType = t.GetOwningSymbol().AsAggregateSymbol().AssociatedSystemType; result = parentType.GetGenericArguments()[t.GetIndexInOwnParameters()]; } break; case TypeKind.TK_ArgumentListType: case TypeKind.TK_BoundLambdaType: case TypeKind.TK_ErrorType: case TypeKind.TK_MethodGroupType: case TypeKind.TK_NaturalIntegerType: case TypeKind.TK_NullType: case TypeKind.TK_OpenTypePlaceholderType: case TypeKind.TK_UnboundLambdaType: case TypeKind.TK_VoidType: default: break; } Debug.Assert(result != null || src.GetTypeKind() == TypeKind.TK_AggregateType); return(result); }
/*************************************************************************************************** * Called by BindImplicitConversion when the destination type is Nullable<T>. The following * conversions are handled by this method: * * For S in { object, ValueType, interfaces implemented by underlying type} there is an explicit * unboxing conversion S => T? * System.Enum => T? there is an unboxing conversion if T is an enum type * null => T? implemented as default(T?) * * Implicit T?* => T?+ implemented by either wrapping or calling GetValueOrDefault the * appropriate number of times. * If imp/exp S => T then imp/exp S => T?+ implemented by converting to T then wrapping the * appropriate number of times. * If imp/exp S => T then imp/exp S?+ => T?+ implemented by calling GetValueOrDefault (m-1) times * then calling HasValue, producing a null if it returns false, otherwise calling Value, * converting to T then wrapping the appropriate number of times. * * The 3 rules above can be summarized with the following recursive rules: * * If imp/exp S => T? then imp/exp S? => T? implemented as * qs.HasValue ? (T?)(qs.Value) : default(T?) * If imp/exp S => T then imp/exp S => T? implemented as new T?((T)s) * * This method also handles calling bindUserDefinedConverion. This method does NOT handle * the following conversions: * * Implicit boxing conversion from S? to { object, ValueType, Enum, ifaces implemented by S }. (Handled by BindImplicitConversion.) * If imp/exp S => T then explicit S?+ => T implemented by calling Value the appropriate number * of times. (Handled by BindExplicitConversion.) * * The recursive equivalent is: * * If imp/exp S => T and T is not nullable then explicit S? => T implemented as qs.Value * * Some nullable conversion are NOT standard conversions. In particular, if S => T is implicit * then S? => T is not standard. Similarly if S => T is not implicit then S => T? is not standard. ***************************************************************************************************/ private bool BindNubConversion(NullableType nubDst) { // This code assumes that STANDARD and ISEXPLICIT are never both set. // bindUserDefinedConversion should ensure this! Debug.Assert(0 != (~_flags & (CONVERTTYPE.STANDARD | CONVERTTYPE.ISEXPLICIT))); Debug.Assert(_exprSrc == null || _exprSrc.type == _typeSrc); Debug.Assert(!_needsExprDest || _exprSrc != null); Debug.Assert(_typeSrc != nubDst); // BindImplicitConversion should have taken care of this already. AggregateType atsDst = nubDst.GetAts(GetErrorContext()); if (atsDst == null) { return(false); } // Check for the unboxing conversion. This takes precedence over the wrapping conversions. if (GetSymbolLoader().HasBaseConversion(nubDst.GetUnderlyingType(), _typeSrc) && !CConversions.FWrappingConv(_typeSrc, nubDst)) { // These should be different! Fix the caller if typeSrc is an AggregateType of Nullable. Debug.Assert(atsDst != _typeSrc); // typeSrc is a base type of the destination nullable type so there is an explicit // unboxing conversion. if (0 == (_flags & CONVERTTYPE.ISEXPLICIT)) { return(false); } if (_needsExprDest) { _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_UNBOX); } return(true); } int cnubDst; int cnubSrc; CType typeDstBase = nubDst.StripNubs(out cnubDst); EXPRCLASS exprTypeDstBase = GetExprFactory().MakeClass(typeDstBase); CType typeSrcBase = _typeSrc.StripNubs(out cnubSrc); ConversionFunc pfn = (_flags & CONVERTTYPE.ISEXPLICIT) != 0 ? (ConversionFunc)_binder.BindExplicitConversion : (ConversionFunc)_binder.BindImplicitConversion; if (cnubSrc == 0) { Debug.Assert(_typeSrc == typeSrcBase); // The null type can be implicitly converted to T? as the default value. if (_typeSrc.IsNullType()) { // If we have the constant null, generate it as a default value of T?. If we have // some crazy expression which has been determined to be always null, like (null??null) // keep it in its expression form and transform it in the nullable rewrite pass. if (_needsExprDest) { if (_exprSrc.isCONSTANT_OK()) { _exprDest = GetExprFactory().CreateZeroInit(nubDst); } else { _exprDest = GetExprFactory().CreateCast(0x00, _typeDest, _exprSrc); } } return(true); } EXPR exprTmp = _exprSrc; // If there is an implicit/explicit S => T then there is an implicit/explicit S => T? if (_typeSrc == typeDstBase || pfn(_exprSrc, _typeSrc, exprTypeDstBase, nubDst, _needsExprDest, out exprTmp, _flags | CONVERTTYPE.NOUDC)) { if (_needsExprDest) { EXPRUSERDEFINEDCONVERSION exprUDC = exprTmp.kind == ExpressionKind.EK_USERDEFINEDCONVERSION ? exprTmp.asUSERDEFINEDCONVERSION() : null; if (exprUDC != null) { exprTmp = exprUDC.UserDefinedCall; } // This logic is left over from the days when T?? was legal. However there are error/LAF cases that necessitates the loop. // typeSrc is not nullable so just wrap the required number of times. For legal code (cnubDst <= 0). for (int i = 0; i < cnubDst; i++) { exprTmp = _binder.BindNubNew(exprTmp); exprTmp.asCALL().nubLiftKind = NullableCallLiftKind.NullableConversionConstructor; } if (exprUDC != null) { exprUDC.UserDefinedCall = exprTmp; exprUDC.setType((CType)exprTmp.type); exprTmp = exprUDC; } Debug.Assert(exprTmp.type == nubDst); _exprDest = exprTmp; } return(true); } // No builtin conversion. Maybe there is a user defined conversion.... return(0 == (_flags & CONVERTTYPE.NOUDC) && _binder.bindUserDefinedConversion(_exprSrc, _typeSrc, nubDst, _needsExprDest, out _exprDest, 0 == (_flags & CONVERTTYPE.ISEXPLICIT))); } // Both are Nullable so there is only a conversion if there is a conversion between the base types. // That is, if there is an implicit/explicit S => T then there is an implicit/explicit S?+ => T?+. if (typeSrcBase != typeDstBase && !pfn(null, typeSrcBase, exprTypeDstBase, nubDst, false, out _exprDest, _flags | CONVERTTYPE.NOUDC)) { // No builtin conversion. Maybe there is a user defined conversion.... return(0 == (_flags & CONVERTTYPE.NOUDC) && _binder.bindUserDefinedConversion(_exprSrc, _typeSrc, nubDst, _needsExprDest, out _exprDest, 0 == (_flags & CONVERTTYPE.ISEXPLICIT))); } if (_needsExprDest) { MethWithInst mwi = new MethWithInst(null, null); EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); EXPRCALL exprDst = GetExprFactory().CreateCall(0, nubDst, _exprSrc, pMemGroup, null); // Here we want to first check whether or not the conversions work on the base types. EXPR arg1 = _binder.mustCast(_exprSrc, typeSrcBase); EXPRCLASS arg2 = GetExprFactory().MakeClass(typeDstBase); bool convertible; if (0 != (_flags & CONVERTTYPE.ISEXPLICIT)) { convertible = _binder.BindExplicitConversion(arg1, arg1.type, arg2, typeDstBase, out arg1, _flags | CONVERTTYPE.NOUDC); } else { convertible = _binder.BindImplicitConversion(arg1, arg1.type, arg2, typeDstBase, out arg1, _flags | CONVERTTYPE.NOUDC); } if (!convertible) { VSFAIL("bind(Im|Ex)plicitConversion failed unexpectedly"); return(false); } exprDst.castOfNonLiftedResultToLiftedType = _binder.mustCast(arg1, nubDst, 0); exprDst.nubLiftKind = NullableCallLiftKind.NullableConversion; exprDst.pConversions = exprDst.castOfNonLiftedResultToLiftedType; _exprDest = exprDst; } return(true); }
private bool bindImplicitConversionFromNullable(NullableType nubSrc) { // We can convert T? using a boxing conversion, we can convert it to ValueType, and // we can convert it to any interface implemented by T. // // 13.1.5 Boxing Conversions // // A nullable-type has a boxing conversion to the same set of types to which the nullable-type's // underlying type has boxing conversions. A boxing conversion applied to a value of a nullable-type // proceeds as follows: // // * If the HasValue property of the nullable value evaluates to false, then the result of the // boxing conversion is the null reference of the appropriate type. // // Otherwise, the result is obtained by boxing the result of evaluating the Value property on // the nullable value. AggregateType atsNub = nubSrc.GetAts(GetErrorContext()); if (atsNub == null) { return false; } if (atsNub == _typeDest) { if (_needsExprDest) { _exprDest = _exprSrc; } return true; } if (GetSymbolLoader().HasBaseConversion(nubSrc.GetUnderlyingType(), _typeDest) && !CConversions.FUnwrappingConv(nubSrc, _typeDest)) { if (_needsExprDest) { _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_BOX); if (!_typeDest.isPredefType(PredefinedType.PT_OBJECT)) { // The base type of a nullable is always a non-nullable value type, // therefore so is typeDest unless typeDest is PT_OBJECT. In this case the conversion // needs to be unboxed. We only need this if we actually will use the result. _binder.bindSimpleCast(_exprDest, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_FORCE_UNBOX); } } return true; } return 0 == (_flags & CONVERTTYPE.NOUDC) && _binder.bindUserDefinedConversion(_exprSrc, nubSrc, _typeDest, _needsExprDest, out _exprDest, true); }
/*************************************************************************************************** * Called by BindImplicitConversion when the destination type is Nullable<T>. The following * conversions are handled by this method: * * For S in { object, ValueType, interfaces implemented by underlying type} there is an explicit * unboxing conversion S => T? * System.Enum => T? there is an unboxing conversion if T is an enum type * null => T? implemented as default(T?) * * Implicit T?* => T?+ implemented by either wrapping or calling GetValueOrDefault the * appropriate number of times. * If imp/exp S => T then imp/exp S => T?+ implemented by converting to T then wrapping the * appropriate number of times. * If imp/exp S => T then imp/exp S?+ => T?+ implemented by calling GetValueOrDefault (m-1) times * then calling HasValue, producing a null if it returns false, otherwise calling Value, * converting to T then wrapping the appropriate number of times. * * The 3 rules above can be summarized with the following recursive rules: * * If imp/exp S => T? then imp/exp S? => T? implemented as * qs.HasValue ? (T?)(qs.Value) : default(T?) * If imp/exp S => T then imp/exp S => T? implemented as new T?((T)s) * * This method also handles calling bindUserDefinedConverion. This method does NOT handle * the following conversions: * * Implicit boxing conversion from S? to { object, ValueType, Enum, ifaces implemented by S }. (Handled by BindImplicitConversion.) * If imp/exp S => T then explicit S?+ => T implemented by calling Value the appropriate number * of times. (Handled by BindExplicitConversion.) * * The recursive equivalent is: * * If imp/exp S => T and T is not nullable then explicit S? => T implemented as qs.Value * * Some nullable conversion are NOT standard conversions. In particular, if S => T is implicit * then S? => T is not standard. Similarly if S => T is not implicit then S => T? is not standard. ***************************************************************************************************/ private bool BindNubConversion(NullableType nubDst) { // This code assumes that STANDARD and ISEXPLICIT are never both set. // bindUserDefinedConversion should ensure this! Debug.Assert(0 != (~_flags & (CONVERTTYPE.STANDARD | CONVERTTYPE.ISEXPLICIT))); Debug.Assert(_exprSrc == null || _exprSrc.Type == _typeSrc); Debug.Assert(!_needsExprDest || _exprSrc != null); Debug.Assert(_typeSrc != nubDst); // BindImplicitConversion should have taken care of this already. AggregateType atsDst = nubDst.GetAts(); // Check for the unboxing conversion. This takes precedence over the wrapping conversions. if (GetSymbolLoader().HasBaseConversion(nubDst.GetUnderlyingType(), _typeSrc) && !CConversions.FWrappingConv(_typeSrc, nubDst)) { // These should be different! Fix the caller if typeSrc is an AggregateType of Nullable. Debug.Assert(atsDst != _typeSrc); // typeSrc is a base type of the destination nullable type so there is an explicit // unboxing conversion. if (0 == (_flags & CONVERTTYPE.ISEXPLICIT)) { return(false); } if (_needsExprDest) { _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_UNBOX); } return(true); } bool dstWasNullable; bool srcWasNullable; CType typeDstBase = nubDst.StripNubs(out dstWasNullable); ExprClass exprTypeDstBase = GetExprFactory().CreateClass(typeDstBase); CType typeSrcBase = _typeSrc.StripNubs(out srcWasNullable); ConversionFunc pfn = (_flags & CONVERTTYPE.ISEXPLICIT) != 0 ? (ConversionFunc)_binder.BindExplicitConversion : (ConversionFunc)_binder.BindImplicitConversion; if (!srcWasNullable) { Debug.Assert(_typeSrc == typeSrcBase); // The null type can be implicitly converted to T? as the default value. if (_typeSrc is NullType) { // If we have the constant null, generate it as a default value of T?. If we have // some crazy expression which has been determined to be always null, like (null??null) // keep it in its expression form and transform it in the nullable rewrite pass. if (_needsExprDest) { if (_exprSrc.isCONSTANT_OK()) { _exprDest = GetExprFactory().CreateZeroInit(nubDst); } else { _exprDest = GetExprFactory().CreateCast(_typeDest, _exprSrc); } } return(true); } Expr exprTmp = _exprSrc; // If there is an implicit/explicit S => T then there is an implicit/explicit S => T? if (_typeSrc == typeDstBase || pfn(_exprSrc, _typeSrc, exprTypeDstBase, nubDst, _needsExprDest, out exprTmp, _flags | CONVERTTYPE.NOUDC)) { if (_needsExprDest) { ExprUserDefinedConversion exprUDC = exprTmp as ExprUserDefinedConversion; if (exprUDC != null) { exprTmp = exprUDC.UserDefinedCall; } if (dstWasNullable) { ExprCall call = _binder.BindNubNew(exprTmp); exprTmp = call; call.NullableCallLiftKind = NullableCallLiftKind.NullableConversionConstructor; } if (exprUDC != null) { exprUDC.UserDefinedCall = exprTmp; exprTmp = exprUDC; } Debug.Assert(exprTmp.Type == nubDst); _exprDest = exprTmp; } return(true); } // No builtin conversion. Maybe there is a user defined conversion.... return(0 == (_flags & CONVERTTYPE.NOUDC) && _binder.bindUserDefinedConversion(_exprSrc, _typeSrc, nubDst, _needsExprDest, out _exprDest, 0 == (_flags & CONVERTTYPE.ISEXPLICIT))); } // Both are Nullable so there is only a conversion if there is a conversion between the base types. // That is, if there is an implicit/explicit S => T then there is an implicit/explicit S?+ => T?+. if (typeSrcBase != typeDstBase && !pfn(null, typeSrcBase, exprTypeDstBase, nubDst, false, out _exprDest, _flags | CONVERTTYPE.NOUDC)) { // No builtin conversion. Maybe there is a user defined conversion.... return(0 == (_flags & CONVERTTYPE.NOUDC) && _binder.bindUserDefinedConversion(_exprSrc, _typeSrc, nubDst, _needsExprDest, out _exprDest, 0 == (_flags & CONVERTTYPE.ISEXPLICIT))); } if (_needsExprDest) { MethWithInst mwi = new MethWithInst(null, null); ExprMemberGroup pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); ExprCall exprDst = GetExprFactory().CreateCall(0, nubDst, _exprSrc, pMemGroup, null); // Here we want to first check whether or not the conversions work on the base types. Expr arg1 = _binder.mustCast(_exprSrc, typeSrcBase); ExprClass arg2 = GetExprFactory().CreateClass(typeDstBase); bool convertible = (_flags & CONVERTTYPE.ISEXPLICIT) != 0 ? _binder.BindExplicitConversion( arg1, arg1.Type, arg2, typeDstBase, out arg1, _flags | CONVERTTYPE.NOUDC) : _binder.BindImplicitConversion( arg1, arg1.Type, arg2, typeDstBase, out arg1, _flags | CONVERTTYPE.NOUDC); if (!convertible) { Debug.Fail("bind(Im|Ex)plicitConversion failed unexpectedly"); return(false); } exprDst.CastOfNonLiftedResultToLiftedType = _binder.mustCast(arg1, nubDst, 0); exprDst.NullableCallLiftKind = NullableCallLiftKind.NullableConversion; exprDst.PConversions = exprDst.CastOfNonLiftedResultToLiftedType; _exprDest = exprDst; } return(true); }
/*************************************************************************************************** Called by BindImplicitConversion when the destination type is Nullable<T>. The following conversions are handled by this method: * For S in { object, ValueType, interfaces implemented by underlying type} there is an explicit unboxing conversion S => T? * System.Enum => T? there is an unboxing conversion if T is an enum type * null => T? implemented as default(T?) * Implicit T?* => T?+ implemented by either wrapping or calling GetValueOrDefault the appropriate number of times. * If imp/exp S => T then imp/exp S => T?+ implemented by converting to T then wrapping the appropriate number of times. * If imp/exp S => T then imp/exp S?+ => T?+ implemented by calling GetValueOrDefault (m-1) times then calling HasValue, producing a null if it returns false, otherwise calling Value, converting to T then wrapping the appropriate number of times. The 3 rules above can be summarized with the following recursive rules: * If imp/exp S => T? then imp/exp S? => T? implemented as qs.HasValue ? (T?)(qs.Value) : default(T?) * If imp/exp S => T then imp/exp S => T? implemented as new T?((T)s) This method also handles calling bindUserDefinedConverion. This method does NOT handle the following conversions: * Implicit boxing conversion from S? to { object, ValueType, Enum, ifaces implemented by S }. (Handled by BindImplicitConversion.) * If imp/exp S => T then explicit S?+ => T implemented by calling Value the appropriate number of times. (Handled by BindExplicitConversion.) The recursive equivalent is: * If imp/exp S => T and T is not nullable then explicit S? => T implemented as qs.Value Some nullable conversion are NOT standard conversions. In particular, if S => T is implicit then S? => T is not standard. Similarly if S => T is not implicit then S => T? is not standard. ***************************************************************************************************/ private bool BindNubConversion(NullableType nubDst) { // This code assumes that STANDARD and ISEXPLICIT are never both set. // bindUserDefinedConversion should ensure this! Debug.Assert(0 != (~_flags & (CONVERTTYPE.STANDARD | CONVERTTYPE.ISEXPLICIT))); Debug.Assert(_exprSrc == null || _exprSrc.type == _typeSrc); Debug.Assert(!_needsExprDest || _exprSrc != null); Debug.Assert(_typeSrc != nubDst); // BindImplicitConversion should have taken care of this already. AggregateType atsDst = nubDst.GetAts(GetErrorContext()); if (atsDst == null) return false; // Check for the unboxing conversion. This takes precedence over the wrapping conversions. if (GetSymbolLoader().HasBaseConversion(nubDst.GetUnderlyingType(), _typeSrc) && !CConversions.FWrappingConv(_typeSrc, nubDst)) { // These should be different! Fix the caller if typeSrc is an AggregateType of Nullable. Debug.Assert(atsDst != _typeSrc); // typeSrc is a base type of the destination nullable type so there is an explicit // unboxing conversion. if (0 == (_flags & CONVERTTYPE.ISEXPLICIT)) { return false; } if (_needsExprDest) { _binder.bindSimpleCast(_exprSrc, _exprTypeDest, out _exprDest, EXPRFLAG.EXF_UNBOX); } return true; } int cnubDst; int cnubSrc; CType typeDstBase = nubDst.StripNubs(out cnubDst); EXPRCLASS exprTypeDstBase = GetExprFactory().MakeClass(typeDstBase); CType typeSrcBase = _typeSrc.StripNubs(out cnubSrc); ConversionFunc pfn = (_flags & CONVERTTYPE.ISEXPLICIT) != 0 ? (ConversionFunc)_binder.BindExplicitConversion : (ConversionFunc)_binder.BindImplicitConversion; if (cnubSrc == 0) { Debug.Assert(_typeSrc == typeSrcBase); // The null type can be implicitly converted to T? as the default value. if (_typeSrc.IsNullType()) { // If we have the constant null, generate it as a default value of T?. If we have // some crazy expression which has been determined to be always null, like (null??null) // keep it in its expression form and transform it in the nullable rewrite pass. if (_needsExprDest) { if (_exprSrc.isCONSTANT_OK()) { _exprDest = GetExprFactory().CreateZeroInit(nubDst); } else { _exprDest = GetExprFactory().CreateCast(0x00, _typeDest, _exprSrc); } } return true; } EXPR exprTmp = _exprSrc; // If there is an implicit/explicit S => T then there is an implicit/explicit S => T? if (_typeSrc == typeDstBase || pfn(_exprSrc, _typeSrc, exprTypeDstBase, nubDst, _needsExprDest, out exprTmp, _flags | CONVERTTYPE.NOUDC)) { if (_needsExprDest) { EXPRUSERDEFINEDCONVERSION exprUDC = exprTmp.kind == ExpressionKind.EK_USERDEFINEDCONVERSION ? exprTmp.asUSERDEFINEDCONVERSION() : null; if (exprUDC != null) { exprTmp = exprUDC.UserDefinedCall; } // This logic is left over from the days when T?? was legal. However there are error/LAF cases that necessitates the loop. // typeSrc is not nullable so just wrap the required number of times. For legal code (cnubDst <= 0). for (int i = 0; i < cnubDst; i++) { exprTmp = _binder.BindNubNew(exprTmp); exprTmp.asCALL().nubLiftKind = NullableCallLiftKind.NullableConversionConstructor; } if (exprUDC != null) { exprUDC.UserDefinedCall = exprTmp; exprUDC.setType((CType)exprTmp.type); exprTmp = exprUDC; } Debug.Assert(exprTmp.type == nubDst); _exprDest = exprTmp; } return true; } // No builtin conversion. Maybe there is a user defined conversion.... return 0 == (_flags & CONVERTTYPE.NOUDC) && _binder.bindUserDefinedConversion(_exprSrc, _typeSrc, nubDst, _needsExprDest, out _exprDest, 0 == (_flags & CONVERTTYPE.ISEXPLICIT)); } // Both are Nullable so there is only a conversion if there is a conversion between the base types. // That is, if there is an implicit/explicit S => T then there is an implicit/explicit S?+ => T?+. if (typeSrcBase != typeDstBase && !pfn(null, typeSrcBase, exprTypeDstBase, nubDst, false, out _exprDest, _flags | CONVERTTYPE.NOUDC)) { // No builtin conversion. Maybe there is a user defined conversion.... return 0 == (_flags & CONVERTTYPE.NOUDC) && _binder.bindUserDefinedConversion(_exprSrc, _typeSrc, nubDst, _needsExprDest, out _exprDest, 0 == (_flags & CONVERTTYPE.ISEXPLICIT)); } if (_needsExprDest) { MethWithInst mwi = new MethWithInst(null, null); EXPRMEMGRP pMemGroup = GetExprFactory().CreateMemGroup(null, mwi); EXPRCALL exprDst = GetExprFactory().CreateCall(0, nubDst, _exprSrc, pMemGroup, null); // Here we want to first check whether or not the conversions work on the base types. EXPR arg1 = _binder.mustCast(_exprSrc, typeSrcBase); EXPRCLASS arg2 = GetExprFactory().MakeClass(typeDstBase); bool convertible; if (0 != (_flags & CONVERTTYPE.ISEXPLICIT)) { convertible = _binder.BindExplicitConversion(arg1, arg1.type, arg2, typeDstBase, out arg1, _flags | CONVERTTYPE.NOUDC); } else { convertible = _binder.BindImplicitConversion(arg1, arg1.type, arg2, typeDstBase, out arg1, _flags | CONVERTTYPE.NOUDC); } if (!convertible) { VSFAIL("bind(Im|Ex)plicitConversion failed unexpectedly"); return false; } exprDst.castOfNonLiftedResultToLiftedType = _binder.mustCast(arg1, nubDst, 0); exprDst.nubLiftKind = NullableCallLiftKind.NullableConversion; exprDst.pConversions = exprDst.castOfNonLiftedResultToLiftedType; _exprDest = exprDst; } return true; }