private AggCastResult bindExplicitConversionFromDecimalToEnum(AggregateType aggTypeDest) { Debug.Assert(_typeSrc != null); Debug.Assert(_typeSrc.IsPredefType(PredefinedType.PT_DECIMAL)); Debug.Assert(aggTypeDest.IsEnumType); // There is an explicit conversion from decimal to all integral types. if (_exprSrc.GetConst() != null) { // Fold the constant cast if possible. ConstCastResult result = _binder.bindConstantCast(_exprSrc, _typeDest, _needsExprDest, out _exprDest, true); if (result == ConstCastResult.Success) { return(AggCastResult.Success); // else, don't fold and use a regular cast, below. } if (result == ConstCastResult.CheckFailure && 0 == (_flags & CONVERTTYPE.CHECKOVERFLOW)) { return(AggCastResult.Abort); } } // All casts from decimal to integer types are bound as user-defined conversions. bool bIsConversionOK = true; if (_needsExprDest) { // According the language, this is a standard conversion, but it is implemented // through a user-defined conversion. Because it's a standard conversion, we don't // test the CONVERTTYPE.NOUDC flag here. CType underlyingType = aggTypeDest.UnderlyingEnumType; bIsConversionOK = _binder.bindUserDefinedConversion(_exprSrc, _typeSrc, underlyingType, _needsExprDest, out _exprDest, false); if (bIsConversionOK) { // upcast to the Enum type _binder.bindSimpleCast(_exprDest, _typeDest, out _exprDest); } } return(bIsConversionOK ? AggCastResult.Success : AggCastResult.Failure); }
private static CType TypeFromOperands(Expr first, Expr second) { CType type = first.Type; if (type.IsPredefType(PredefinedType.PT_STRING)) { return(type); } Debug.Assert(second.Type.IsPredefType(PredefinedType.PT_STRING)); return(second.Type); }
private bool bindImplicitConversionFromNull() { // null type can be implicitly converted to any reference type or pointer type or type // variable with reference-type constraint. FUNDTYPE ftDest = _typeDest.FundamentalType; if (ftDest != FUNDTYPE.FT_REF && ftDest != FUNDTYPE.FT_PTR && // null is convertible to System.Nullable<T>. !_typeDest.IsPredefType(PredefinedType.PT_G_OPTIONAL)) { return false; } if (_needsExprDest) { // If the conversion argument is a constant null then return a ZEROINIT. // Otherwise, bind this as a cast to the destination type. In a later // rewrite pass we will rewrite the cast as SEQ(side effects, ZEROINIT). _exprDest = _exprSrc is ExprConstant ? ExprFactory.CreateZeroInit(_typeDest) : ExprFactory.CreateCast(_typeDest, _exprSrc); } return true; }
public bool HasBaseConversion(CType pSource, CType pDest) { // By a "base conversion" we mean: // // * an identity conversion // * an implicit reference conversion // * an implicit boxing conversion // * an implicit type parameter conversion // // In other words, these are conversions that can be made to a base // class, base interface or co/contravariant type without any change in // representation other than boxing. A conversion from, say, int to double, // is NOT a "base conversion", because representation is changed. A conversion // from, say, lambda to expression tree is not a "base conversion" because // do not have a type. // // The existence of a base conversion depends solely upon the source and // destination types, not the source expression. // // This notion is not found in the spec but it is useful in the implementation. if (pSource is AggregateType && pDest.IsPredefType(PredefinedType.PT_OBJECT)) { // If we are going from any aggregate type (class, struct, interface, enum or delegate) // to object, we immediately return true. This may seem like a mere optimization -- // after all, if we have an aggregate then we have some kind of implicit conversion // to object. // // However, it is not a mere optimization; this introduces a control flow change // in error reporting scenarios for unresolved type forwarders. If a type forwarder // cannot be resolved then the resulting type symbol will be an aggregate, but // we will not be able to classify it into class, struct, etc. // // We know that we will have an error in this case; we do not wish to compound // that error by giving a spurious "you cannot convert this thing to object" // error, which, after all, will go away when the type forwarding problem is // fixed. return(true); } return(HasIdentityOrImplicitReferenceConversion(pSource, pDest) || HasImplicitBoxingConversion(pSource, pDest)); }
private bool HasArrayConversionToInterface(ArrayType pSource, CType pDest) { Debug.Assert(pSource != null); Debug.Assert(pDest != null); if (!pSource.IsSZArray || !pDest.IsInterfaceType) { return(false); } // * From a single-dimensional array type S[] to IList<T> or IReadOnlyList<T> and their base // interfaces, provided that there is an implicit identity or reference // conversion from S to T. // We only have six interfaces to check. IList<T>, IReadOnlyList<T> and their bases: // * The base interface of IList<T> is ICollection<T>. // * The base interface of ICollection<T> is IEnumerable<T>. // * The base interface of IEnumerable<T> is IEnumerable. // * The base interface of IReadOnlyList<T> is IReadOnlyCollection<T>. // * The base interface of IReadOnlyCollection<T> is IEnumerable<T>. if (pDest.IsPredefType(PredefinedType.PT_IENUMERABLE)) { return(true); } AggregateType atsDest = (AggregateType)pDest; AggregateSymbol aggDest = atsDest.OwningAggregate; if (!aggDest.isPredefAgg(PredefinedType.PT_G_ILIST) && !aggDest.isPredefAgg(PredefinedType.PT_G_ICOLLECTION) && !aggDest.isPredefAgg(PredefinedType.PT_G_IENUMERABLE) && !aggDest.isPredefAgg(PredefinedType.PT_G_IREADONLYCOLLECTION) && !aggDest.isPredefAgg(PredefinedType.PT_G_IREADONLYLIST)) { return(false); } Debug.Assert(atsDest.TypeArgsAll.Count == 1); return(HasIdentityOrImplicitReferenceConversion(pSource.ElementType, atsDest.TypeArgsAll[0])); }
public static Expr CreateZeroInit(CType type) { Debug.Assert(type != null); if (type.IsEnumType) { // For enum types, we create a constant that has the default value // as an object pointer. return(CreateConstant(type, ConstVal.Get(Activator.CreateInstance(type.AssociatedSystemType)))); } Debug.Assert(type.FundamentalType > FUNDTYPE.FT_NONE); Debug.Assert(type.FundamentalType < FUNDTYPE.FT_COUNT); switch (type.FundamentalType) { case FUNDTYPE.FT_PTR: { // Just allocate a new node and fill it in. return(CreateCast(0, type, CreateNull())); } case FUNDTYPE.FT_STRUCT: if (type.IsPredefType(PredefinedType.PT_DECIMAL)) { goto default; } goto case FUNDTYPE.FT_VAR; case FUNDTYPE.FT_VAR: return(new ExprZeroInit(type)); default: return(CreateConstant(type, ConstVal.GetDefaultValue(type.ConstValKind))); } }
private static bool CheckSingleConstraint(Symbol symErr, TypeParameterType var, CType arg, TypeArray typeArgsCls, TypeArray typeArgsMeth, CheckConstraintsFlags flags) { Debug.Assert(!(arg is PointerType)); Debug.Assert(!arg.IsStaticClass); bool fReportErrors = 0 == (flags & CheckConstraintsFlags.NoErrors); if (var.HasRefConstraint && !arg.IsReferenceType) { if (fReportErrors) { throw ErrorHandling.Error(ErrorCode.ERR_RefConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); } return(false); } TypeArray bnds = TypeManager.SubstTypeArray(var.Bounds, typeArgsCls, typeArgsMeth); int itypeMin = 0; if (var.HasValConstraint) { // If we have a type variable that is constrained to a value type, then we // want to check if its a nullable type, so that we can report the // constraint error below. In order to do this however, we need to check // that either the type arg is not a value type, or it is a nullable type. // // To check whether or not its a nullable type, we need to get the resolved // bound from the type argument and check against that. if (!arg.IsNonNullableValueType) { if (fReportErrors) { throw ErrorHandling.Error(ErrorCode.ERR_ValConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); } return(false); } // Since FValCon() is set it is redundant to check System.ValueType as well. if (bnds.Count != 0 && bnds[0].IsPredefType(PredefinedType.PT_VALUE)) { itypeMin = 1; } } for (int j = itypeMin; j < bnds.Count; j++) { CType typeBnd = bnds[j]; if (!SatisfiesBound(arg, typeBnd)) { if (fReportErrors) { // The bound isn't satisfied because of a constraint type. Explain to the user why not. // There are 4 main cases, based on the type of the supplied type argument: // - reference type, or type parameter known to be a reference type // - nullable type, from which there is a boxing conversion to the constraint type(see below for details) // - type variable // - value type // These cases are broken out because: a) The sets of conversions which can be used // for constraint satisfaction is different based on the type argument supplied, // and b) Nullable is one funky type, and user's can use all the help they can get // when using it. ErrorCode error; if (arg.IsReferenceType) { // A reference type can only satisfy bounds to types // to which they have an implicit reference conversion error = ErrorCode.ERR_GenericConstraintNotSatisfiedRefType; } else if (arg is NullableType nubArg && SymbolLoader.HasBaseConversion(nubArg.UnderlyingType, typeBnd)) // This is inlining FBoxingConv { // nullable types do not satisfy bounds to every type that they are boxable to // They only satisfy bounds of object and ValueType if (typeBnd.IsPredefType(PredefinedType.PT_ENUM) || nubArg.UnderlyingType == typeBnd) { // Nullable types don't satisfy bounds of EnumType, or the underlying type of the enum // even though the conversion from Nullable to these types is a boxing conversion // This is a rare case, because these bounds can never be directly stated ... // These bounds can only occur when one type paramter is constrained to a second type parameter // and the second type parameter is instantiated with Enum or the underlying type of the first type // parameter error = ErrorCode.ERR_GenericConstraintNotSatisfiedNullableEnum; } else { // Nullable types don't satisfy the bounds of any interface type // even when there is a boxing conversion from the Nullable type to // the interface type. This will be a relatively common scenario // so we cal it out separately from the previous case. Debug.Assert(typeBnd.IsInterfaceType); error = ErrorCode.ERR_GenericConstraintNotSatisfiedNullableInterface; } } else { // Value types can only satisfy bounds through boxing conversions. // Note that the exceptional case of Nullable types and boxing is handled above. error = ErrorCode.ERR_GenericConstraintNotSatisfiedValType; } throw ErrorHandling.Error(error, new ErrArg(symErr), new ErrArg(typeBnd, ErrArgFlags.Unique), var, new ErrArg(arg, ErrArgFlags.Unique)); } 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 is MethodGroupType) { 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.TypeKind) { 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_ArgumentListType: return(_typeSrc == _typeDest); case TypeKind.TK_VoidType: return(false); default: break; } // 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.FundamentalType; Debug.Assert(ftDest != FUNDTYPE.FT_NONE || _typeDest is ParameterModifierType); switch (_typeSrc.TypeKind) { default: Debug.Fail($"Bad type symbol kind: {_typeSrc.TypeKind}"); break; case TypeKind.TK_VoidType: 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) && CSemanticChecker.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); }
private bool HasImplicitReferenceConversion(CType pSource, CType pDest) { Debug.Assert(pSource != null); Debug.Assert(pDest != null); Debug.Assert(!(pSource is TypeParameterType)); // The implicit reference conversions are: // * From any reference type to Object. if (pSource.IsReferenceType && pDest.IsPredefType(PredefinedType.PT_OBJECT)) { return(true); } if (pSource is AggregateType aggSource) { if (pDest is AggregateType aggDest) { switch (aggSource.OwningAggregate.AggKind()) { case AggKindEnum.Class: switch (aggDest.OwningAggregate.AggKind()) { case AggKindEnum.Class: // * From any class type S to any class type T provided S is derived from T. return(IsBaseClass(aggSource, aggDest)); case AggKindEnum.Interface: // ORIGINAL RULES: // // * From any class type S to any interface type T provided S implements T. // if (pSource.isClassType() && pDest.isInterfaceType() && IsBaseInterface(pSource, pDest)) // { // return true; // } // // * from any interface type S to any interface type T, provided S is derived from T. // if (pSource.isInterfaceType() && pDest.isInterfaceType() && IsBaseInterface(pSource, pDest)) // { // return true; // } // VARIANCE EXTENSIONS: // * From any class type S to any interface type T provided S implements an interface // convertible to T. // * From any interface type S to any interface type T provided S implements an interface // convertible to T. // * From any interface type S to any interface type T provided S is not T and S is // an interface convertible to T. return(HasAnyBaseInterfaceConversion(aggSource, aggDest)); } break; case AggKindEnum.Interface: if (aggDest.IsInterfaceType) { return(HasAnyBaseInterfaceConversion(aggSource, aggDest) || HasInterfaceConversion(aggSource, aggDest)); } break; case AggKindEnum.Delegate: // * From any delegate type to System.Delegate // // SPEC OMISSION: // // The spec should actually say // // * From any delegate type to System.Delegate // * From any delegate type to System.MulticastDelegate // * From any delegate type to any interface implemented by System.MulticastDelegate if (aggDest.IsPredefType(PredefinedType.PT_MULTIDEL) || aggDest.IsPredefType(PredefinedType.PT_DELEGATE) || IsBaseInterface( GetPredefindType(PredefinedType.PT_MULTIDEL), aggDest)) { return(true); } // VARIANCE EXTENSION: // * From any delegate type S to a delegate type T provided S is not T and // S is a delegate convertible to T return(pDest.IsDelegateType && HasDelegateConversion(aggSource, aggDest)); } } } else if (pSource is ArrayType arrSource) { // * From an array type S with an element type SE to an array type T with element type TE // provided that all of the following are true: // * S and T differ only in element type. In other words, S and T have the same number of dimensions. // * Both SE and TE are reference types. // * An implicit reference conversion exists from SE to TE. if (pDest is ArrayType arrDest) { return(HasCovariantArrayConversion(arrSource, arrDest)); } if (pDest is AggregateType aggDest) { // * From any array type to System.Array or any interface implemented by System.Array. if (aggDest.IsPredefType(PredefinedType.PT_ARRAY) || IsBaseInterface(GetPredefindType(PredefinedType.PT_ARRAY), aggDest)) { return(true); } // * From a single-dimensional array type S[] to IList<T> and its base // interfaces, provided that there is an implicit identity or reference // conversion from S to T. return(HasArrayConversionToInterface(arrSource, pDest)); } } else if (pSource is NullType) { // * From the null literal to any reference type // NOTE: We extend the specification here. The C# 3.0 spec does not describe // a "null type". Rather, it says that the null literal is typeless, and is // convertible to any reference or nullable type. However, the C# 2.0 and 3.0 // implementations have a "null type" which some expressions other than the // null literal may have. (For example, (null??null), which is also an // extension to the specification.) return(pDest.IsReferenceType || pDest is NullableType); } return(false); }