예제 #1
0
            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);
            }
예제 #2
0
        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;
            }
예제 #4
0
        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));
        }
예제 #5
0
        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]));
        }
예제 #6
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);
                }
            }
예제 #8
0
            /*
             * 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);
            }
예제 #9
0
        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);
        }