/*
* 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);
}