private bool TryArrayVarianceAdjustmentToGetAccessibleType(CSemanticChecker semanticChecker, BindingContext bindingContext, ArrayType typeSrc, out CType typeDst) { Debug.Assert(typeSrc != null); typeDst = null; // We are here because we have an array type with an inaccessible element type. If possible, // we should create a new array type that has an accessible element type for which a // conversion exists. CType elementType = typeSrc.GetElementType(); if (!elementType.IsRefType()) { // Covariant array conversions exist for reference types only. return(false); } CType intermediateType; if (GetBestAccessibleType(semanticChecker, bindingContext, elementType, out intermediateType)) { typeDst = this.GetArray(intermediateType, typeSrc.rank, typeSrc.IsSZArray); Debug.Assert(semanticChecker.CheckTypeAccess(typeDst, bindingContext.ContextForMemberLookup)); return(true); } return(false); }
/*************************************************************************************************** * * There exists an explicit conversion ... * 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. ***************************************************************************************************/ public static bool HasGenericDelegateExplicitReferenceConversion(SymbolLoader loader, CType pSource, CType pTarget) { if (!pSource.isDelegateType() || !pTarget.isDelegateType() || pSource.getAggregate() != pTarget.getAggregate() || loader.HasIdentityOrImplicitReferenceConversion(pSource, pTarget)) { return(false); } TypeArray pTypeParams = pSource.getAggregate().GetTypeVarsAll(); TypeArray pSourceArgs = ((AggregateType)pSource).GetTypeArgsAll(); TypeArray pTargetArgs = ((AggregateType)pTarget).GetTypeArgsAll(); Debug.Assert(pTypeParams.Count == pSourceArgs.Count); Debug.Assert(pTypeParams.Count == pTargetArgs.Count); for (int iParam = 0; iParam < pTypeParams.Count; ++iParam) { CType pSourceArg = pSourceArgs[iParam]; CType pTargetArg = pTargetArgs[iParam]; // If they're identical then this one is automatically good, so skip it. // If we have an error type, then we're in some fault tolerance. Let it through. if (pSourceArg == pTargetArg) { continue; } TypeParameterType pParam = (TypeParameterType)pTypeParams[iParam]; if (pParam.Invariant) { return(false); } if (pParam.Covariant) { if (!FExpRefConv(loader, pSourceArg, pTargetArg)) { return(false); } } else if (pParam.Contravariant) { if (!pSourceArg.IsRefType() || !pTargetArg.IsRefType()) { return(false); } } } return(true); }
public bool HasImplicitBoxingConversion(CType pSource, CType pDest) { Debug.Assert(pSource != null); Debug.Assert(pDest != null); // Certain type parameter conversions are classified as boxing conversions. if (pSource.IsTypeParameterType() && HasImplicitBoxingTypeParameterConversion(pSource.AsTypeParameterType(), pDest)) { return(true); } // The rest of the boxing conversions only operate when going from a value type // to a reference type. if (!pSource.IsValType() || !pDest.IsRefType()) { return(false); } // A boxing conversion exists from a nullable type to a reference type // if and only if a boxing conversion exists from the underlying type. if (pSource.IsNullableType()) { return(HasImplicitBoxingConversion(pSource.AsNullableType().GetUnderlyingType(), pDest)); } // A boxing conversion exists from any non-nullable value type to object, // to System.ValueType, and to any interface type implemented by the // non-nullable value type. Furthermore, an enum type can be converted // to the type System.Enum. // We set the base class of the structs to System.ValueType, System.Enum, etc, // so we can just check here. if (IsBaseClass(pSource, pDest)) { return(true); } if (HasAnyBaseInterfaceConversion(pSource, pDest)) { return(true); } return(false); }
private bool HasImplicitBoxingConversion(CType pSource, CType pDest) { Debug.Assert(pSource != null); Debug.Assert(pDest != null); Debug.Assert(!(pSource is TypeParameterType)); // The rest of the boxing conversions only operate when going from a value type // to a reference type. if (!pDest.IsRefType()) { return(false); } // A boxing conversion exists from a nullable type to a reference type // if and only if a boxing conversion exists from the underlying type. if (pSource is NullableType nubSource) { pSource = nubSource.UnderlyingType; // pSource.IsValType() known to be true. } else if (!pSource.IsValType()) { return(false); } // A boxing conversion exists from any non-nullable value type to object, // to System.ValueType, and to any interface type implemented by the // non-nullable value type. Furthermore, an enum type can be converted // to the type System.Enum. // We set the base class of the structs to System.ValueType, System.Enum, etc, // so we can just check here. return(IsBaseClass(pSource, pDest) || HasAnyBaseInterfaceConversion(pSource, pDest)); }
private static bool CheckSingleConstraint(CSemanticChecker checker, ErrorHandling errHandling, Symbol symErr, TypeParameterType var, CType arg, TypeArray typeArgsCls, TypeArray typeArgsMeth, CheckConstraintsFlags flags) { bool fReportErrors = 0 == (flags & CheckConstraintsFlags.NoErrors); if (arg is OpenTypePlaceholderType) { return(true); } if (arg is ErrorType) { // Error should have been reported previously. return(false); } if (checker.CheckBogus(arg)) { if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_BogusType, arg); } return(false); } if (arg is PointerType) { if (fReportErrors) { errHandling.Error(ErrorCode.ERR_BadTypeArgument, arg); } return(false); } if (arg.isStaticClass()) { if (fReportErrors) { checker.ReportStaticClassError(null, arg, ErrorCode.ERR_GenericArgIsStaticClass); } return(false); } bool fError = false; if (var.HasRefConstraint() && !arg.IsRefType()) { if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_RefConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); } fError = true; } TypeArray bnds = checker.GetSymbolLoader().GetTypeManager().SubstTypeArray(var.GetBounds(), 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. bool bIsValueType = arg.IsValType(); bool bIsNullable = arg is NullableType; if (bIsValueType && arg is TypeParameterType typeArg) { TypeArray pArgBnds = typeArg.GetBounds(); if (pArgBnds.Count > 0) { bIsNullable = pArgBnds[0] is NullableType; } } if (!bIsValueType || bIsNullable) { if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_ValConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); } fError = true; } // 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(checker, 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.IsRefType()) { // 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 && checker.GetSymbolLoader().HasBaseConversion(nubArg.GetUnderlyingType(), 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.GetUnderlyingType() == 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 if (arg is TypeParameterType) { // Type variables can satisfy bounds through boxing and type variable conversions Debug.Assert(!arg.IsRefType()); error = ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar; } 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; } errHandling.Error(error, new ErrArgRef(symErr), new ErrArg(typeBnd, ErrArgFlags.Unique), var, new ErrArgRef(arg, ErrArgFlags.Unique)); } fError = true; } }
/*************************************************************************************************** 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; }
// WARNING: These methods do not precisely match the spec. // WARNING: For example most also return true for identiy conversions, // WARNING: FExpRefConv includes all Implict and Explicit reference conversions. /*************************************************************************************************** Determine whether there is an implicit reference conversion from typeSrc to typeDst. This is when the source is a reference type and the destination is a base type of the source. Note that typeDst.IsRefType() may still return false (when both are type parameters). ***************************************************************************************************/ public static bool FImpRefConv(SymbolLoader loader, CType typeSrc, CType typeDst) { return typeSrc.IsRefType() && loader.HasIdentityOrImplicitReferenceConversion(typeSrc, typeDst); }
private static bool CheckSingleConstraint(CSemanticChecker checker, ErrorHandling errHandling, Symbol symErr, TypeParameterType var, CType arg, TypeArray typeArgsCls, TypeArray typeArgsMeth, CheckConstraintsFlags flags) { bool fReportErrors = 0 == (flags & CheckConstraintsFlags.NoErrors); if (arg.IsOpenTypePlaceholderType()) { return true; } if (arg.IsErrorType()) { // Error should have been reported previously. return false; } if (checker.CheckBogus(arg)) { if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_BogusType, arg); } return false; } if (arg.IsPointerType() || arg.isSpecialByRefType()) { if (fReportErrors) { errHandling.Error(ErrorCode.ERR_BadTypeArgument, arg); } return false; } if (arg.isStaticClass()) { if (fReportErrors) { checker.ReportStaticClassError(null, arg, ErrorCode.ERR_GenericArgIsStaticClass); } return false; } bool fError = false; if (var.HasRefConstraint() && !arg.IsRefType()) { if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_RefConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); } fError = true; } TypeArray bnds = checker.GetSymbolLoader().GetTypeManager().SubstTypeArray(var.GetBounds(), 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. bool bIsValueType = arg.IsValType(); bool bIsNullable = arg.IsNullableType(); if (bIsValueType && arg.IsTypeParameterType()) { TypeArray pArgBnds = arg.AsTypeParameterType().GetBounds(); if (pArgBnds.size > 0) { bIsNullable = pArgBnds.Item(0).IsNullableType(); } } if (!bIsValueType || bIsNullable) { if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_ValConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); } fError = true; } // Since FValCon() is set it is redundant to check System.ValueType as well. if (bnds.size != 0 && bnds.Item(0).isPredefType(PredefinedType.PT_VALUE)) { itypeMin = 1; } } for (int j = itypeMin; j < bnds.size; j++) { CType typeBnd = bnds.Item(j); if (!SatisfiesBound(checker, arg, typeBnd)) { if (fReportErrors) { // The bound isn't satisfied because of a constaint 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 varaiable // - 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.IsRefType()) { // A reference type can only satisfy bounds to types // to which they have an implicit reference conversion error = ErrorCode.ERR_GenericConstraintNotSatisfiedRefType; } else if (arg.IsNullableType() && checker.GetSymbolLoader().HasBaseConversion(arg.AsNullableType().GetUnderlyingType(), 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) || arg.AsNullableType().GetUnderlyingType() == 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 if (arg.IsTypeParameterType()) { // Type variables can satisfy bounds through boxing and type variable conversions Debug.Assert(!arg.IsRefType()); error = ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar; } 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; } errHandling.Error(error, new ErrArgRef(symErr), new ErrArg(typeBnd, ErrArgFlags.Unique), var, new ErrArgRef(arg, ErrArgFlags.Unique)); } fError = true; } } // Check the newable constraint. if (!var.HasNewConstraint() || arg.IsValType()) { return !fError; } if (arg.isClassType()) { AggregateSymbol agg = arg.AsAggregateType().getAggregate(); // Due to late binding nature of IDE created symbols, the AggregateSymbol might not // have all the information necessary yet, if it is not fully bound. // by calling LookupAggMember, it will ensure that we will update all the // information necessary at least for the given method. checker.GetSymbolLoader().LookupAggMember(checker.GetNameManager().GetPredefName(PredefinedName.PN_CTOR), agg, symbmask_t.MASK_ALL); if (agg.HasPubNoArgCtor() && !agg.IsAbstract()) { return !fError; } } else if (arg.IsTypeParameterType() && arg.AsTypeParameterType().HasNewConstraint()) { return !fError; } if (fReportErrors) { errHandling.ErrorRef(ErrorCode.ERR_NewConstraintNotSatisfied, symErr, new ErrArgNoRef(var), arg); } 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.IsRefType() && pDest.isPredefType(PredefinedType.PT_OBJECT)) { return(true); } if (pSource is AggregateType aggSource) { if (pDest is AggregateType aggDest) { switch (aggSource.GetOwningAggregate().AggKind()) { case AggKindEnum.Class: switch (aggDest.GetOwningAggregate().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.IsRefType() || pDest is NullableType); } return(false); }
/*************************************************************************************************** * 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 is TypeParameterType) { return(true); } if (typeSrc is TypeParameterType && 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 is AggregateType atSrc && typeDst is AggregateType atDst) { AggregateSymbol aggSrc = atSrc.getAggregate(); AggregateSymbol aggDest = atDst.getAggregate(); if ((aggSrc.IsClass() && !aggSrc.IsSealed() && aggDest.IsInterface()) || (aggSrc.IsInterface() && aggDest.IsClass() && !aggDest.IsSealed()) || (aggSrc.IsInterface() && aggDest.IsInterface())) { return(true); } } if (typeSrc is ArrayType arrSrc) { // * 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 (typeDst is ArrayType arrDst) { return(arrSrc.rank == arrDst.rank && arrSrc.IsSZArray == arrDst.IsSZArray && FExpRefConv(loader, arrSrc.GetElementType(), arrDst.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 (!arrSrc.IsSZArray || !typeDst.isInterfaceType()) { return(false); } AggregateType aggDst = (AggregateType)typeDst; TypeArray typeArgsAll = aggDst.GetTypeArgsAll(); if (typeArgsAll.Count != 1) { return(false); } AggregateSymbol aggIList = loader.GetPredefAgg(PredefinedType.PT_G_ILIST); AggregateSymbol aggIReadOnlyList = loader.GetPredefAgg(PredefinedType.PT_G_IREADONLYLIST); if ((aggIList == null || !SymbolLoader.IsBaseAggregate(aggIList, aggDst.getAggregate())) && (aggIReadOnlyList == null || !SymbolLoader.IsBaseAggregate(aggIReadOnlyList, aggDst.getAggregate()))) { return(false); } return(FExpRefConv(loader, arrSrc.GetElementType(), typeArgsAll[0])); } if (typeDst is ArrayType arrayDest && typeSrc is AggregateType aggtypeSrc) { // * From System.Array and the interfaces it implements, to any array-type. if (loader.HasIdentityOrImplicitReferenceConversion(loader.GetPredefindType(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. if (!arrayDest.IsSZArray || !typeSrc.isInterfaceType() || aggtypeSrc.GetTypeArgsAll().Count != 1) { return(false); } AggregateSymbol aggIList = loader.GetPredefAgg(PredefinedType.PT_G_ILIST); AggregateSymbol aggIReadOnlyList = loader.GetPredefAgg(PredefinedType.PT_G_IREADONLYLIST); if ((aggIList == null || !SymbolLoader.IsBaseAggregate(aggIList, aggtypeSrc.getAggregate())) && (aggIReadOnlyList == null || !SymbolLoader.IsBaseAggregate(aggIReadOnlyList, aggtypeSrc.getAggregate()))) { return(false); } CType typeArr = arrayDest.GetElementType(); CType typeLst = aggtypeSrc.GetTypeArgsAll()[0]; Debug.Assert(!(typeArr is MethodGroupType)); 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); }
// WARNING: These methods do not precisely match the spec. // WARNING: For example most also return true for identity conversions, // WARNING: FExpRefConv includes all Implicit and Explicit reference conversions. /*************************************************************************************************** * Determine whether there is an implicit reference conversion from typeSrc to typeDst. This is * when the source is a reference type and the destination is a base type of the source. Note * that typeDst.IsRefType() may still return false (when both are type parameters). ***************************************************************************************************/ public static bool FImpRefConv(SymbolLoader loader, CType typeSrc, CType typeDst) { return(typeSrc.IsRefType() && loader.HasIdentityOrImplicitReferenceConversion(typeSrc, typeDst)); }
public bool HasImplicitBoxingConversion(CType pSource, CType pDest) { Debug.Assert(pSource != null); Debug.Assert(pDest != null); // Certain type parameter conversions are classified as boxing conversions. if (pSource.IsTypeParameterType() && HasImplicitBoxingTypeParameterConversion(pSource.AsTypeParameterType(), pDest)) { return true; } // The rest of the boxing conversions only operate when going from a value type // to a reference type. if (!pSource.IsValType() || !pDest.IsRefType()) { return false; } // A boxing conversion exists from a nullable type to a reference type // if and only if a boxing conversion exists from the underlying type. if (pSource.IsNullableType()) { return HasImplicitBoxingConversion(pSource.AsNullableType().GetUnderlyingType(), pDest); } // A boxing conversion exists from any non-nullable value type to object, // to System.ValueType, and to any interface type implemented by the // non-nullable value type. Furthermore, an enum type can be converted // to the type System.Enum. // We set the base class of the structs to System.ValueType, System.Enum, etc, // so we can just check here. if (IsBaseClass(pSource, pDest)) { return true; } if (HasAnyBaseInterfaceConversion(pSource, pDest)) { return true; } return false; }
public bool HasImplicitReferenceConversion(CType pSource, CType pDest) { Debug.Assert(pSource != null); Debug.Assert(pDest != null); // The implicit reference conversions are: // * From any reference type to Object. if (pSource.IsRefType() && pDest.isPredefType(PredefinedType.PT_OBJECT)) { return true; } // * From any class type S to any class type T provided S is derived from T. if (pSource.isClassType() && pDest.isClassType() && IsBaseClass(pSource, pDest)) { return true; } // 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. if (pSource.isClassType() && pDest.isInterfaceType() && HasAnyBaseInterfaceConversion(pSource, pDest)) { return true; } if (pSource.isInterfaceType() && pDest.isInterfaceType() && HasAnyBaseInterfaceConversion(pSource, pDest)) { return true; } if (pSource.isInterfaceType() && pDest.isInterfaceType() && pSource != pDest && HasInterfaceConversion(pSource.AsAggregateType(), pDest.AsAggregateType())) { return true; } // * 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 (pSource.IsArrayType() && pDest.IsArrayType() && HasCovariantArrayConversion(pSource.AsArrayType(), pDest.AsArrayType())) { return true; } // * From any array type to System.Array or any interface implemented by System.Array. if (pSource.IsArrayType() && (pDest.isPredefType(PredefinedType.PT_ARRAY) || IsBaseInterface(GetReqPredefType(PredefinedType.PT_ARRAY, false), pDest))) { 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. if (pSource.IsArrayType() && HasArrayConversionToInterface(pSource.AsArrayType(), pDest)) { return true; } // * 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 (pSource.isDelegateType() && (pDest.isPredefType(PredefinedType.PT_MULTIDEL) || pDest.isPredefType(PredefinedType.PT_DELEGATE) || IsBaseInterface(GetReqPredefType(PredefinedType.PT_MULTIDEL, false), pDest))) { 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 if (pSource.isDelegateType() && pDest.isDelegateType() && HasDelegateConversion(pSource.AsAggregateType(), pDest.AsAggregateType())) { return true; } // * 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.) if (pSource.IsNullType() && pDest.IsRefType()) { return true; } if (pSource.IsNullType() && pDest.IsNullableType()) { return true; } // * Implicit conversions involving type parameters that are known to be reference types. if (pSource.IsTypeParameterType() && HasImplicitReferenceTypeParameterConversion(pSource.AsTypeParameterType(), pDest)) { return true; } return false; }