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 bindExplicitConversionFromNub() { Debug.Assert(_typeSrc != null); Debug.Assert(_typeDest != null); // If S and T are value types and there is a builtin conversion from S => T then there is an // explicit conversion from S? => T that throws on null. if (_typeDest.IsValType() && _binder.BindExplicitConversion(null, _typeSrc.StripNubs(), _exprTypeDest, _pDestinationTypeForLambdaErrorReporting, _flags | CONVERTTYPE.NOUDC)) { if (_needsExprDest) { Expr valueSrc = _exprSrc; // This is a holdover from the days when you could have nullable of nullable. // Can we remove this loop? while (valueSrc.Type is NullableType) { valueSrc = _binder.BindNubValue(valueSrc); } Debug.Assert(valueSrc.Type == _typeSrc.StripNubs()); if (!_binder.BindExplicitConversion(valueSrc, valueSrc.Type, _exprTypeDest, _pDestinationTypeForLambdaErrorReporting, _needsExprDest, out _exprDest, _flags | CONVERTTYPE.NOUDC)) { VSFAIL("BindExplicitConversion failed unexpectedly"); return(false); } if (_exprDest is ExprUserDefinedConversion udc) { udc.Argument = _exprSrc; } } return(true); } if ((_flags & CONVERTTYPE.NOUDC) == 0) { return(_binder.bindUserDefinedConversion(_exprSrc, _typeSrc, _typeDest, _needsExprDest, out _exprDest, false)); } return(false); }
private bool bindExplicitConversionFromNub() { Debug.Assert(typeSrc != null); Debug.Assert(typeDest != null); // If S and T are value types and there is a builtin conversion from S => T then there is an // explicit conversion from S? => T that throws on null. if (typeDest.IsValType() && binder.BindExplicitConversion(null, typeSrc.StripNubs(), exprTypeDest, m_pDestinationTypeForLambdaErrorReporting, flags | CONVERTTYPE.NOUDC)) { if (needsExprDest) { EXPR valueSrc = exprSrc; // UNDONE: This is a holdover from the days when you could have nullable of nullable. // UNDONE: Can we remove this loop? while (valueSrc.type.IsNullableType()) { valueSrc = binder.BindNubValue(valueSrc); } Debug.Assert(valueSrc.type == typeSrc.StripNubs()); if (!binder.BindExplicitConversion(valueSrc, valueSrc.type, exprTypeDest, m_pDestinationTypeForLambdaErrorReporting, needsExprDest, out exprDest, flags | CONVERTTYPE.NOUDC)) { VSFAIL("BindExplicitConversion failed unexpectedly"); return(false); } if (exprDest.kind == ExpressionKind.EK_USERDEFINEDCONVERSION) { exprDest.asUSERDEFINEDCONVERSION().Argument = exprSrc; } } return(true); } if ((flags & CONVERTTYPE.NOUDC) == 0) { return(binder.bindUserDefinedConversion(exprSrc, typeSrc, typeDest, needsExprDest, out exprDest, false)); } return(false); }
private bool bindExplicitConversionFromNub() { Debug.Assert(_typeSrc != null); Debug.Assert(_typeDest != null); // If S and T are value types and there is a builtin conversion from S => T then there is an // explicit conversion from S? => T that throws on null. if (_typeDest.IsValType() && _binder.BindExplicitConversion(null, _typeSrc.StripNubs(), _typeDest, _flags | CONVERTTYPE.NOUDC)) { if (_needsExprDest) { Expr valueSrc = _exprSrc; if (valueSrc.Type is NullableType) { valueSrc = _binder.BindNubValue(valueSrc); } Debug.Assert(valueSrc.Type == _typeSrc.StripNubs()); if (!_binder.BindExplicitConversion(valueSrc, valueSrc.Type, _typeDest, _needsExprDest, out _exprDest, _flags | CONVERTTYPE.NOUDC)) { Debug.Fail("BindExplicitConversion failed unexpectedly"); return(false); } if (_exprDest is ExprUserDefinedConversion udc) { udc.Argument = _exprSrc; } } return(true); } if ((_flags & CONVERTTYPE.NOUDC) == 0) { return(_binder.bindUserDefinedConversion(_exprSrc, _typeSrc, _typeDest, _needsExprDest, out _exprDest, false)); } 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; } }
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; }
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; }