//////////////////////////////////////////////////////////////////////////////// // Determine whether the arg type satisfies the typeBnd constraint. Note that // typeBnd could be just about any type (since we added naked type parameter // constraints). private static bool SatisfiesBound(CSemanticChecker checker, CType arg, CType typeBnd) { if (typeBnd == arg) { return(true); } switch (typeBnd.GetTypeKind()) { default: Debug.Assert(false, "Unexpected type."); return(false); case TypeKind.TK_VoidType: case TypeKind.TK_PointerType: case TypeKind.TK_ErrorType: return(false); case TypeKind.TK_ArrayType: case TypeKind.TK_TypeParameterType: break; case TypeKind.TK_NullableType: typeBnd = typeBnd.AsNullableType().GetAts(checker.GetErrorContext()); if (null == typeBnd) { return(true); } break; case TypeKind.TK_AggregateType: break; } Debug.Assert(typeBnd.IsAggregateType() || typeBnd.IsTypeParameterType() || typeBnd.IsArrayType()); switch (arg.GetTypeKind()) { default: return(false); case TypeKind.TK_ErrorType: case TypeKind.TK_PointerType: return(false); case TypeKind.TK_NullableType: arg = arg.AsNullableType().GetAts(checker.GetErrorContext()); if (null == arg) { return(true); } // Fall through. goto case TypeKind.TK_TypeParameterType; case TypeKind.TK_TypeParameterType: case TypeKind.TK_ArrayType: case TypeKind.TK_AggregateType: return(checker.GetSymbolLoader().HasBaseConversion(arg, typeBnd)); } }
// It would be nice to make this a virtual method on typeSym. public AggregateType GetAggTypeSym(CType typeSym) { Debug.Assert(typeSym != null); Debug.Assert(typeSym.IsAggregateType() || typeSym.IsTypeParameterType() || typeSym.IsArrayType() || typeSym.IsNullableType()); switch (typeSym.GetTypeKind()) { case TypeKind.TK_AggregateType: return(typeSym.AsAggregateType()); case TypeKind.TK_ArrayType: return(GetReqPredefType(PredefinedType.PT_ARRAY)); case TypeKind.TK_TypeParameterType: return(typeSym.AsTypeParameterType().GetEffectiveBaseClass()); case TypeKind.TK_NullableType: return(typeSym.AsNullableType().GetAts(ErrorContext)); } Debug.Assert(false, "Bad typeSym!"); return(null); }
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 IsBaseClass(CType pDerived, CType pBase) { Debug.Assert(pDerived != null); Debug.Assert(pBase != null); // A base class has got to be a class. The derived type might be a struct. if (!pBase.isClassType()) { return(false); } if (pDerived.IsNullableType()) { pDerived = pDerived.AsNullableType().GetAts(ErrorContext); if (pDerived == null) { return(false); } } if (!pDerived.IsAggregateType()) { return(false); } AggregateType atsDer = pDerived.AsAggregateType(); AggregateType atsBase = pBase.AsAggregateType(); AggregateType atsCur = atsDer.GetBaseClass(); while (atsCur != null) { if (atsCur == atsBase) { return(true); } atsCur = atsCur.GetBaseClass(); } return(false); }
/*************************************************************************************************** Determines whether there is a wrapping conversion from typeSrc to typeDst 13.7 Conversions involving nullable types The following terms are used in the subsequent sections: * The term wrapping denotes the process of packaging a value, of type T, in an instance of type T?. A value x of type T is wrapped to type T? by evaluating the expression new T?(x). ***************************************************************************************************/ public static bool FWrappingConv(CType typeSrc, CType typeDst) { return typeDst.IsNullableType() && typeSrc == typeDst.AsNullableType().GetUnderlyingType(); }
private CType SubstTypeCore(CType type, SubstContext pctx) { CType typeSrc; CType typeDst; switch (type.GetTypeKind()) { default: Debug.Assert(false); return type; case TypeKind.TK_NullType: case TypeKind.TK_VoidType: case TypeKind.TK_OpenTypePlaceholderType: case TypeKind.TK_MethodGroupType: case TypeKind.TK_BoundLambdaType: case TypeKind.TK_UnboundLambdaType: case TypeKind.TK_NaturalIntegerType: case TypeKind.TK_ArgumentListType: return type; case TypeKind.TK_ParameterModifierType: typeDst = SubstTypeCore(typeSrc = type.AsParameterModifierType().GetParameterType(), pctx); return (typeDst == typeSrc) ? type : GetParameterModifier(typeDst, type.AsParameterModifierType().isOut); case TypeKind.TK_ArrayType: typeDst = SubstTypeCore(typeSrc = type.AsArrayType().GetElementType(), pctx); return (typeDst == typeSrc) ? type : GetArray(typeDst, type.AsArrayType().rank); case TypeKind.TK_PointerType: typeDst = SubstTypeCore(typeSrc = type.AsPointerType().GetReferentType(), pctx); return (typeDst == typeSrc) ? type : GetPointer(typeDst); case TypeKind.TK_NullableType: typeDst = SubstTypeCore(typeSrc = type.AsNullableType().GetUnderlyingType(), pctx); return (typeDst == typeSrc) ? type : GetNullable(typeDst); case TypeKind.TK_AggregateType: if (type.AsAggregateType().GetTypeArgsAll().size > 0) { AggregateType ats = type.AsAggregateType(); TypeArray typeArgs = SubstTypeArray(ats.GetTypeArgsAll(), pctx); if (ats.GetTypeArgsAll() != typeArgs) return GetAggregate(ats.getAggregate(), typeArgs); } return type; case TypeKind.TK_ErrorType: if (type.AsErrorType().HasParent()) { ErrorType err = type.AsErrorType(); Debug.Assert(err.nameText != null && err.typeArgs != null); CType pParentType = null; if (err.HasTypeParent()) { pParentType = SubstTypeCore(err.GetTypeParent(), pctx); } TypeArray typeArgs = SubstTypeArray(err.typeArgs, pctx); if (typeArgs != err.typeArgs || (err.HasTypeParent() && pParentType != err.GetTypeParent())) { return GetErrorType(pParentType, err.GetNSParent(), err.nameText, typeArgs); } } return type; case TypeKind.TK_TypeParameterType: { TypeParameterSymbol tvs = type.AsTypeParameterType().GetTypeParameterSymbol(); int index = tvs.GetIndexInTotalParameters(); if (tvs.IsMethodTypeParameter()) { if ((pctx.grfst & SubstTypeFlags.DenormMeth) != 0 && tvs.parent != null) return type; Debug.Assert(tvs.GetIndexInOwnParameters() == tvs.GetIndexInTotalParameters()); if (index < pctx.ctypeMeth) { Debug.Assert(pctx.prgtypeMeth != null); return pctx.prgtypeMeth[index]; } else { return ((pctx.grfst & SubstTypeFlags.NormMeth) != 0 ? GetStdMethTypeVar(index) : type); } } if ((pctx.grfst & SubstTypeFlags.DenormClass) != 0 && tvs.parent != null) return type; return index < pctx.ctypeCls ? pctx.prgtypeCls[index] : ((pctx.grfst & SubstTypeFlags.NormClass) != 0 ? GetStdClsTypeVar(index) : type); } } }
//////////////////////////////////////////////////////////////////////////////// // Determine whether the arg type satisfies the typeBnd constraint. Note that // typeBnd could be just about any type (since we added naked type parameter // constraints). private static bool SatisfiesBound(CSemanticChecker checker, CType arg, CType typeBnd) { if (typeBnd == arg) return true; switch (typeBnd.GetTypeKind()) { default: Debug.Assert(false, "Unexpected type."); return false; case TypeKind.TK_VoidType: case TypeKind.TK_PointerType: case TypeKind.TK_ErrorType: return false; case TypeKind.TK_ArrayType: case TypeKind.TK_TypeParameterType: break; case TypeKind.TK_NullableType: typeBnd = typeBnd.AsNullableType().GetAts(checker.GetErrorContext()); if (null == typeBnd) return true; break; case TypeKind.TK_AggregateType: break; } Debug.Assert(typeBnd.IsAggregateType() || typeBnd.IsTypeParameterType() || typeBnd.IsArrayType()); switch (arg.GetTypeKind()) { default: return false; case TypeKind.TK_ErrorType: case TypeKind.TK_PointerType: return false; case TypeKind.TK_NullableType: arg = arg.AsNullableType().GetAts(checker.GetErrorContext()); if (null == arg) return true; // Fall through. goto case TypeKind.TK_TypeParameterType; case TypeKind.TK_TypeParameterType: case TypeKind.TK_ArrayType: case TypeKind.TK_AggregateType: return checker.GetSymbolLoader().HasBaseConversion(arg, typeBnd); } }
///////////////////////////////////////////////////////////////////////////////// private static EXPR GenerateOptionalArgument( SymbolLoader symbolLoader, ExprFactory exprFactory, MethodOrPropertySymbol methprop, CType type, int index) { CType pParamType = type; CType pRawParamType = type.IsNullableType() ? type.AsNullableType().GetUnderlyingType() : type; EXPR optionalArgument = null; if (methprop.HasDefaultParameterValue(index)) { CType pConstValType = methprop.GetDefaultParameterValueConstValType(index); CONSTVAL cv = methprop.GetDefaultParameterValue(index); if (pConstValType.isPredefType(PredefinedType.PT_DATETIME) && (pRawParamType.isPredefType(PredefinedType.PT_DATETIME) || pRawParamType.isPredefType(PredefinedType.PT_OBJECT) || pRawParamType.isPredefType(PredefinedType.PT_VALUE))) { // This is the specific case where we want to create a DateTime // but the constval that stores it is a long. AggregateType dateTimeType = symbolLoader.GetReqPredefType(PredefinedType.PT_DATETIME); optionalArgument = exprFactory.CreateConstant(dateTimeType, new CONSTVAL(DateTime.FromBinary(cv.longVal))); } else if (pConstValType.isSimpleOrEnumOrString()) { // In this case, the constval is a simple type (all the numerics, including // decimal), or an enum or a string. This covers all the substantial values, // and everything else that can be encoded is just null or default(something). // For enum parameters, we create a constant of the enum type. For everything // else, we create the appropriate constant. if (pRawParamType.isEnumType() && pConstValType == pRawParamType.underlyingType()) { optionalArgument = exprFactory.CreateConstant(pRawParamType, cv); } else { optionalArgument = exprFactory.CreateConstant(pConstValType, cv); } } else if ((pParamType.IsRefType() || pParamType.IsNullableType()) && cv.IsNullRef()) { // We have an "= null" default value with a reference type or a nullable type. optionalArgument = exprFactory.CreateNull(); } else { // We have a default value that is encoded as a nullref, and that nullref is // interpreted as default(something). For instance, the pParamType could be // a type parameter type or a non-simple value type. optionalArgument = exprFactory.CreateZeroInit(pParamType); } } else { // There was no default parameter specified, so generally use default(T), // except for some cases when the parameter type in metatdata is object. if (pParamType.isPredefType(PredefinedType.PT_OBJECT)) { if (methprop.MarshalAsObject(index)) { // For [opt] parameters of type object, if we have marshal(iunknown), // marshal(idispatch), or marshal(interface), then we emit a null. optionalArgument = exprFactory.CreateNull(); } else { // Otherwise, we generate Type.Missing AggregateSymbol agg = symbolLoader.GetOptPredefAgg(PredefinedType.PT_MISSING); Name name = symbolLoader.GetNameManager().GetPredefinedName(PredefinedName.PN_CAP_VALUE); FieldSymbol field = symbolLoader.LookupAggMember(name, agg, symbmask_t.MASK_FieldSymbol).AsFieldSymbol(); FieldWithType fwt = new FieldWithType(field, agg.getThisType()); EXPRFIELD exprField = exprFactory.CreateField(0, agg.getThisType(), null, 0, fwt, null); if (agg.getThisType() != type) { optionalArgument = exprFactory.CreateCast(0, type, exprField); } else { optionalArgument = exprField; } } } else { // Every type aside from object that doesn't have a default value gets // its default value. optionalArgument = exprFactory.CreateZeroInit(pParamType); } } Debug.Assert(optionalArgument != null); optionalArgument.IsOptionalArgument = true; return optionalArgument; }
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; }
// Check the constraints of any type arguments in the given Type. public static bool CheckConstraints(CSemanticChecker checker, ErrorHandling errHandling, CType type, CheckConstraintsFlags flags) { type = type.GetNakedType(false); if (type.IsNullableType()) { CType typeT = type.AsNullableType().GetAts(checker.GetErrorContext()); if (typeT != null) type = typeT; else type = type.GetNakedType(true); } if (!type.IsAggregateType()) return true; AggregateType ats = type.AsAggregateType(); if (ats.GetTypeArgsAll().size == 0) { // Common case: there are no type vars, so there are no constraints. ats.fConstraintsChecked = true; ats.fConstraintError = false; return true; } if (ats.fConstraintsChecked) { // Already checked. if (!ats.fConstraintError || (flags & CheckConstraintsFlags.NoDupErrors) != 0) { // No errors or no need to report errors again. return !ats.fConstraintError; } } TypeArray typeVars = ats.getAggregate().GetTypeVars(); TypeArray typeArgsThis = ats.GetTypeArgsThis(); TypeArray typeArgsAll = ats.GetTypeArgsAll(); Debug.Assert(typeVars.size == typeArgsThis.size); if (!ats.fConstraintsChecked) { ats.fConstraintsChecked = true; ats.fConstraintError = false; } // Check the outer type first. If CheckConstraintsFlags.Outer is not specified and the // outer type has already been checked then don't bother checking it. if (ats.outerType != null && ((flags & CheckConstraintsFlags.Outer) != 0 || !ats.outerType.fConstraintsChecked)) { CheckConstraints(checker, errHandling, ats.outerType, flags); ats.fConstraintError |= ats.outerType.fConstraintError; } if (typeVars.size > 0) ats.fConstraintError |= !CheckConstraintsCore(checker, errHandling, ats.getAggregate(), typeVars, typeArgsThis, typeArgsAll, null, (flags & CheckConstraintsFlags.NoErrors)); // Now check type args themselves. for (int i = 0; i < typeArgsThis.size; i++) { CType arg = typeArgsThis.Item(i).GetNakedType(true); if (arg.IsAggregateType() && !arg.AsAggregateType().fConstraintsChecked) { CheckConstraints(checker, errHandling, arg.AsAggregateType(), flags | CheckConstraintsFlags.Outer); if (arg.AsAggregateType().fConstraintError) ats.fConstraintError = true; } } return !ats.fConstraintError; }
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.Count > 0) { bIsNullable = pArgBnds[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.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.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 IsBaseClass(CType pDerived, CType pBase) { Debug.Assert(pDerived != null); Debug.Assert(pBase != null); // A base class has got to be a class. The derived type might be a struct. if (!pBase.isClassType()) { return false; } if (pDerived.IsNullableType()) { pDerived = pDerived.AsNullableType().GetAts(ErrorContext); if (pDerived == null) { return false; } } if (!pDerived.IsAggregateType()) { return false; } AggregateType atsDer = pDerived.AsAggregateType(); AggregateType atsBase = pBase.AsAggregateType(); AggregateType atsCur = atsDer.GetBaseClass(); while (atsCur != null) { if (atsCur == atsBase) { return true; } atsCur = atsCur.GetBaseClass(); } return false; }
//////////////////////////////////////////////////////////////////////////////// // For a base call we need to remap from the virtual to the specific override // to invoke. This is also used to map a virtual on pObject (like ToString) to // the specific override when the pObject is a simple type (int, bool, char, // etc). In these cases it is safe to assume that any override won't later be // removed.... We start searching from "typeObj" up the superclass hierarchy // until we find a method with an exact signature match. public static void RemapToOverride(SymbolLoader symbolLoader, SymWithType pswt, CType typeObj) { // For a property/indexer we remap the accessors, not the property/indexer. // Since every event has both accessors we remap the event instead of the accessors. Debug.Assert(pswt && (pswt.Sym.IsMethodSymbol() || pswt.Sym.IsEventSymbol() || pswt.Sym.IsMethodOrPropertySymbol())); Debug.Assert(typeObj != null); // Don't remap static or interface methods. if (typeObj.IsNullableType()) { typeObj = typeObj.AsNullableType().GetAts(symbolLoader.GetErrorContext()); if (typeObj == null) { VSFAIL("Why did GetAts return null?"); return; } } // Don't remap non-virtual members if (!typeObj.IsAggregateType() || typeObj.isInterfaceType() || !pswt.Sym.IsVirtual()) { return; } symbmask_t mask = pswt.Sym.mask(); AggregateType atsObj = typeObj.AsAggregateType(); // Search for an override version of the method. while (atsObj != null && atsObj.getAggregate() != pswt.Sym.parent) { for (Symbol symT = symbolLoader.LookupAggMember(pswt.Sym.name, atsObj.getAggregate(), mask); symT != null; symT = symbolLoader.LookupNextSym(symT, atsObj.getAggregate(), mask)) { if (symT.IsOverride() && (symT.SymBaseVirtual() == pswt.Sym || symT.SymBaseVirtual() == pswt.Sym.SymBaseVirtual())) { pswt.Set(symT, atsObj); return; } } atsObj = atsObj.GetBaseClass(); } }
/* * BindImplicitConversion * * This is a complex routine with complex parameters. Generally, this should * be called through one of the helper methods that insulates you * from the complexity of the interface. This routine handles all the logic * associated with implicit conversions. * * exprSrc - the expression being converted. Can be null if only type conversion * info is being supplied. * typeSrc - type of the source * typeDest - type of the destination * exprDest - returns an expression of the src converted to the dest. If null, we * only care about whether the conversion can be attempted, not the * expression tree. * flags - flags possibly customizing the conversions allowed. E.g., can suppress * user-defined conversions. * * returns true if the conversion can be made, false if not. */ public bool Bind() { // 13.1 Implicit conversions // // The following conversions are classified as implicit conversions: // // * Identity conversions // * Implicit numeric conversions // * Implicit enumeration conversions // * Implicit reference conversions // * Boxing conversions // * Implicit type parameter conversions // * Implicit constant expression conversions // * User-defined implicit conversions // * Implicit conversions from an anonymous method expression to a compatible delegate type // * Implicit conversion from a method group to a compatible delegate type // * Conversions from the null type (11.2.7) to any nullable type // * Implicit nullable conversions // * Lifted user-defined implicit conversions // // Implicit conversions can occur in a variety of situations, including function member invocations // (14.4.3), cast expressions (14.6.6), and assignments (14.14). // Can't convert to or from the error type. if (typeSrc == null || typeDest == null || typeDest.IsNeverSameType()) { return(false); } Debug.Assert(typeSrc != null && typeDest != null); // types must be supplied. Debug.Assert(exprSrc == null || typeSrc == exprSrc.type); // type of source should be correct if source supplied Debug.Assert(!needsExprDest || exprSrc != null); // need source expr to create dest expr switch (typeDest.GetTypeKind()) { case TypeKind.TK_ErrorType: Debug.Assert(typeDest.AsErrorType().HasTypeParent() || typeDest.AsErrorType().HasNSParent()); if (typeSrc != typeDest) { return(false); } if (needsExprDest) { exprDest = exprSrc; } return(true); case TypeKind.TK_NullType: // Can only convert to the null type if src is null. if (!typeSrc.IsNullType()) { return(false); } if (needsExprDest) { exprDest = exprSrc; } return(true); case TypeKind.TK_MethodGroupType: VSFAIL("Something is wrong with Type.IsNeverSameType()"); return(false); case TypeKind.TK_NaturalIntegerType: case TypeKind.TK_ArgumentListType: return(typeSrc == typeDest); case TypeKind.TK_VoidType: return(false); default: break; } if (typeSrc.IsErrorType()) { Debug.Assert(!typeDest.IsErrorType()); return(false); } // 13.1.1 Identity conversion // // An identity conversion converts from any type to the same type. This conversion exists only // such that an entity that already has a required type can be said to be convertible to that type. if (typeSrc == typeDest && ((flags & CONVERTTYPE.ISEXPLICIT) == 0 || (!typeSrc.isPredefType(PredefinedType.PT_FLOAT) && !typeSrc.isPredefType(PredefinedType.PT_DOUBLE)))) { if (needsExprDest) { exprDest = exprSrc; } return(true); } if (typeDest.IsNullableType()) { return(BindNubConversion(typeDest.AsNullableType())); } if (typeSrc.IsNullableType()) { return(bindImplicitConversionFromNullable(typeSrc.AsNullableType())); } if ((flags & CONVERTTYPE.ISEXPLICIT) != 0) { flags |= CONVERTTYPE.NOUDC; } // Get the fundamental types of destination. FUNDTYPE ftDest = typeDest.fundType(); Debug.Assert(ftDest != FUNDTYPE.FT_NONE || typeDest.IsParameterModifierType()); switch (typeSrc.GetTypeKind()) { default: VSFAIL("Bad type symbol kind"); break; case TypeKind.TK_MethodGroupType: if (exprSrc.isMEMGRP()) { EXPRCALL outExpr; bool retVal = binder.BindGrpConversion(exprSrc.asMEMGRP(), typeDest, needsExprDest, out outExpr, false); exprDest = outExpr; return(retVal); } return(false); case TypeKind.TK_VoidType: case TypeKind.TK_ErrorType: case TypeKind.TK_ParameterModifierType: case TypeKind.TK_ArgumentListType: return(false); case TypeKind.TK_NullType: if (bindImplicitConversionFromNull()) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_ArrayType: if (bindImplicitConversionFromArray()) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_PointerType: if (bindImplicitConversionFromPointer()) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_TypeParameterType: if (bindImplicitConversionFromTypeVar(typeSrc.AsTypeParameterType())) { return(true); } // If not, try user defined implicit conversions. break; case TypeKind.TK_AggregateType: // TypeReference and ArgIterator can't be boxed (or converted to anything else) if (typeSrc.isSpecialByRefType()) { return(false); } if (bindImplicitConversionFromAgg(typeSrc.AsAggregateType())) { return(true); } // If not, try user defined implicit conversions. break; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // Every incoming dynamic operand should be implicitly convertible // to any type that it is an instance of. if (exprSrc != null && exprSrc.RuntimeObject != null && typeDest.AssociatedSystemType.IsInstanceOfType(exprSrc.RuntimeObject) && binder.GetSemanticChecker().CheckTypeAccess(typeDest, binder.Context.ContextForMemberLookup())) { if (needsExprDest) { binder.bindSimpleCast(exprSrc, exprTypeDest, out exprDest, exprSrc.flags & EXPRFLAG.EXF_CANTBENULL); } return(true); } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // END RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // 13.1.8 User-defined implicit conversions // // A user-defined implicit conversion consists of an optional standard implicit conversion, // followed by execution of a user-defined implicit conversion operator, followed by another // optional standard implicit conversion. The exact rules for evaluating user-defined // conversions are described in 13.4.3. if (0 == (flags & CONVERTTYPE.NOUDC)) { return(binder.bindUserDefinedConversion(exprSrc, typeSrc, typeDest, needsExprDest, out exprDest, true)); } // No conversion was found. return(false); }
/*************************************************************************************************** * Determines whether there is a wrapping conversion from typeSrc to typeDst * * 13.7 Conversions involving nullable types * * The following terms are used in the subsequent sections: * The term wrapping denotes the process of packaging a value, of type T, in an instance of type T?. * A value x of type T is wrapped to type T? by evaluating the expression new T?(x). ***************************************************************************************************/ public static bool FWrappingConv(CType typeSrc, CType typeDst) { return(typeDst.IsNullableType() && typeSrc == typeDst.AsNullableType().GetUnderlyingType()); }
///////////////////////////////////////////////////////////////////////////////// private void LiftArgument(EXPR pArgument, CType pParameterType, bool bConvertBeforeLift, out EXPR ppLiftedArgument, out EXPR ppNonLiftedArgument) { EXPR pLiftedArgument = mustConvert(pArgument, pParameterType); if (pLiftedArgument != pArgument) { MarkAsIntermediateConversion(pLiftedArgument); } EXPR pNonLiftedArgument = pArgument; if (pParameterType.IsNullableType()) { if (pNonLiftedArgument.isNull()) { pNonLiftedArgument = mustCast(pNonLiftedArgument, pParameterType); } pNonLiftedArgument = mustCast(pNonLiftedArgument, pParameterType.AsNullableType().GetUnderlyingType()); if (bConvertBeforeLift) { MarkAsIntermediateConversion(pNonLiftedArgument); } } else { pNonLiftedArgument = pLiftedArgument; } ppLiftedArgument = pLiftedArgument; ppNonLiftedArgument = pNonLiftedArgument; }
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 static Type CalculateAssociatedSystemType(CType src) { Type result = null; switch (src.GetTypeKind()) { case TypeKind.TK_ArrayType: ArrayType a = src.AsArrayType(); Type elementType = a.GetElementType().AssociatedSystemType; if (a.rank == 1) { result = elementType.MakeArrayType(); } else { result = elementType.MakeArrayType(a.rank); } break; case TypeKind.TK_NullableType: NullableType n = src.AsNullableType(); Type underlyingType = n.GetUnderlyingType().AssociatedSystemType; result = typeof(Nullable<>).MakeGenericType(underlyingType); break; case TypeKind.TK_PointerType: PointerType p = src.AsPointerType(); Type referentType = p.GetReferentType().AssociatedSystemType; result = referentType.MakePointerType(); break; case TypeKind.TK_ParameterModifierType: ParameterModifierType r = src.AsParameterModifierType(); Type parameterType = r.GetParameterType().AssociatedSystemType; result = parameterType.MakeByRefType(); break; case TypeKind.TK_AggregateType: result = CalculateAssociatedSystemTypeForAggregate(src.AsAggregateType()); break; case TypeKind.TK_TypeParameterType: TypeParameterType t = src.AsTypeParameterType(); if (t.IsMethodTypeParameter()) { MethodInfo meth = t.GetOwningSymbol().AsMethodSymbol().AssociatedMemberInfo as MethodInfo; result = meth.GetGenericArguments()[t.GetIndexInOwnParameters()]; } else { Type parentType = t.GetOwningSymbol().AsAggregateSymbol().AssociatedSystemType; result = parentType.GetTypeInfo().GenericTypeParameters[t.GetIndexInOwnParameters()]; } break; case TypeKind.TK_ArgumentListType: case TypeKind.TK_BoundLambdaType: case TypeKind.TK_ErrorType: case TypeKind.TK_MethodGroupType: case TypeKind.TK_NaturalIntegerType: case TypeKind.TK_NullType: case TypeKind.TK_OpenTypePlaceholderType: case TypeKind.TK_UnboundLambdaType: case TypeKind.TK_VoidType: default: break; } Debug.Assert(result != null || src.GetTypeKind() == TypeKind.TK_AggregateType); return result; }
// Check the constraints of any type arguments in the given Type. public static bool CheckConstraints(CSemanticChecker checker, ErrorHandling errHandling, CType type, CheckConstraintsFlags flags) { type = type.GetNakedType(false); if (type.IsNullableType()) { CType typeT = type.AsNullableType().GetAts(checker.GetErrorContext()); if (typeT != null) { type = typeT; } else { type = type.GetNakedType(true); } } if (!type.IsAggregateType()) { return(true); } AggregateType ats = type.AsAggregateType(); if (ats.GetTypeArgsAll().Count == 0) { // Common case: there are no type vars, so there are no constraints. ats.fConstraintsChecked = true; ats.fConstraintError = false; return(true); } if (ats.fConstraintsChecked) { // Already checked. if (!ats.fConstraintError || (flags & CheckConstraintsFlags.NoDupErrors) != 0) { // No errors or no need to report errors again. return(!ats.fConstraintError); } } TypeArray typeVars = ats.getAggregate().GetTypeVars(); TypeArray typeArgsThis = ats.GetTypeArgsThis(); TypeArray typeArgsAll = ats.GetTypeArgsAll(); Debug.Assert(typeVars.Count == typeArgsThis.Count); if (!ats.fConstraintsChecked) { ats.fConstraintsChecked = true; ats.fConstraintError = false; } // Check the outer type first. If CheckConstraintsFlags.Outer is not specified and the // outer type has already been checked then don't bother checking it. if (ats.outerType != null && ((flags & CheckConstraintsFlags.Outer) != 0 || !ats.outerType.fConstraintsChecked)) { CheckConstraints(checker, errHandling, ats.outerType, flags); ats.fConstraintError |= ats.outerType.fConstraintError; } if (typeVars.Count > 0) { ats.fConstraintError |= !CheckConstraintsCore(checker, errHandling, ats.getAggregate(), typeVars, typeArgsThis, typeArgsAll, null, (flags & CheckConstraintsFlags.NoErrors)); } // Now check type args themselves. for (int i = 0; i < typeArgsThis.Count; i++) { CType arg = typeArgsThis[i].GetNakedType(true); if (arg.IsAggregateType() && !arg.AsAggregateType().fConstraintsChecked) { CheckConstraints(checker, errHandling, arg.AsAggregateType(), flags | CheckConstraintsFlags.Outer); if (arg.AsAggregateType().fConstraintError) { ats.fConstraintError = true; } } } return(!ats.fConstraintError); }
private static Type CalculateAssociatedSystemType(CType src) { Type result = null; switch (src.GetTypeKind()) { case TypeKind.TK_ArrayType: ArrayType a = src.AsArrayType(); Type elementType = a.GetElementType().AssociatedSystemType; if (a.rank == 1) { result = elementType.MakeArrayType(); } else { result = elementType.MakeArrayType(a.rank); } break; case TypeKind.TK_NullableType: NullableType n = src.AsNullableType(); Type underlyingType = n.GetUnderlyingType().AssociatedSystemType; result = typeof(Nullable <>).MakeGenericType(underlyingType); break; case TypeKind.TK_PointerType: PointerType p = src.AsPointerType(); Type referentType = p.GetReferentType().AssociatedSystemType; result = referentType.MakePointerType(); break; case TypeKind.TK_ParameterModifierType: ParameterModifierType r = src.AsParameterModifierType(); Type parameterType = r.GetParameterType().AssociatedSystemType; result = parameterType.MakeByRefType(); break; case TypeKind.TK_AggregateType: result = CalculateAssociatedSystemTypeForAggregate(src.AsAggregateType()); break; case TypeKind.TK_TypeParameterType: TypeParameterType t = src.AsTypeParameterType(); Type parentType = null; if (t.IsMethodTypeParameter()) { MethodInfo meth = t.GetOwningSymbol().AsMethodSymbol().AssociatedMemberInfo as MethodInfo; result = meth.GetGenericArguments()[t.GetIndexInOwnParameters()]; } else { parentType = t.GetOwningSymbol().AsAggregateSymbol().AssociatedSystemType; result = parentType.GetTypeInfo().GenericTypeParameters[t.GetIndexInOwnParameters()]; } break; case TypeKind.TK_ArgumentListType: case TypeKind.TK_BoundLambdaType: case TypeKind.TK_ErrorType: case TypeKind.TK_MethodGroupType: case TypeKind.TK_NaturalIntegerType: case TypeKind.TK_NullType: case TypeKind.TK_OpenTypePlaceholderType: case TypeKind.TK_UnboundLambdaType: case TypeKind.TK_VoidType: default: break; } Debug.Assert(result != null || src.GetTypeKind() == TypeKind.TK_AggregateType); return(result); }
// It would be nice to make this a virtual method on typeSym. public AggregateType GetAggTypeSym(CType typeSym) { Debug.Assert(typeSym != null); Debug.Assert(typeSym.IsAggregateType() || typeSym.IsTypeParameterType() || typeSym.IsArrayType() || typeSym.IsNullableType()); switch (typeSym.GetTypeKind()) { case TypeKind.TK_AggregateType: return typeSym.AsAggregateType(); case TypeKind.TK_ArrayType: return GetReqPredefType(PredefinedType.PT_ARRAY); case TypeKind.TK_TypeParameterType: return typeSym.AsTypeParameterType().GetEffectiveBaseClass(); case TypeKind.TK_NullableType: return typeSym.AsNullableType().GetAts(ErrorContext); } Debug.Assert(false, "Bad typeSym!"); return null; }
//////////////////////////////////////////////////////////////////////////////// private bool ExactNullableInference(CType pSource, CType pDest) { // SPEC: Otherwise, if U is the CType U1? and V is the CType V1? // SPEC: then an exact inference is made from U to V. if (!pSource.IsNullableType() || !pDest.IsNullableType()) { return false; } ExactInference(pSource.AsNullableType().GetUnderlyingType(), pDest.AsNullableType().GetUnderlyingType()); return true; }
public void ErrAppendType(CType pType, SubstContext pctx, bool fArgs) { if (pctx != null) { if (!pctx.FNop()) { pType = GetTypeManager().SubstType(pType, pctx); } // We shouldn't use the SubstContext again so set it to NULL. pctx = null; } switch (pType.GetTypeKind()) { case TypeKind.TK_AggregateType: { AggregateType pAggType = pType.AsAggregateType(); // Check for a predefined class with a special "nice" name for // error reported. string text = PredefinedTypes.GetNiceName(pAggType.getAggregate()); if (text != null) { // Found a nice name. ErrAppendString(text); } else if (pAggType.getAggregate().IsAnonymousType()) { ErrAppendPrintf("AnonymousType#{0}", GetTypeID(pAggType)); break; } else { if (pAggType.outerType != null) { ErrAppendType(pAggType.outerType, pctx); ErrAppendChar('.'); } else { // In a namespace. ErrAppendParentSym(pAggType.getAggregate(), pctx); } ErrAppendName(pAggType.getAggregate().name); } ErrAppendTypeParameters(pAggType.GetTypeArgsThis(), pctx, true); break; } case TypeKind.TK_TypeParameterType: if (null == pType.GetName()) { // It's a standard type variable. if (pType.AsTypeParameterType().IsMethodTypeParameter()) { ErrAppendChar('!'); } ErrAppendChar('!'); ErrAppendPrintf("{0}", pType.AsTypeParameterType().GetIndexInTotalParameters()); } else { ErrAppendName(pType.GetName()); } break; case TypeKind.TK_ErrorType: if (pType.AsErrorType().HasParent()) { Debug.Assert(pType.AsErrorType().nameText != null && pType.AsErrorType().typeArgs != null); ErrAppendParentType(pType, pctx); ErrAppendName(pType.AsErrorType().nameText); ErrAppendTypeParameters(pType.AsErrorType().typeArgs, pctx, true); } else { // Load the string "<error>". Debug.Assert(null == pType.AsErrorType().typeArgs); ErrAppendId(MessageID.ERRORSYM); } break; case TypeKind.TK_NullType: // Load the string "<null>". ErrAppendId(MessageID.NULL); break; case TypeKind.TK_OpenTypePlaceholderType: // Leave blank. break; case TypeKind.TK_BoundLambdaType: ErrAppendId(MessageID.AnonMethod); break; case TypeKind.TK_UnboundLambdaType: ErrAppendId(MessageID.Lambda); break; case TypeKind.TK_MethodGroupType: ErrAppendId(MessageID.MethodGroup); break; case TypeKind.TK_ArgumentListType: ErrAppendString(TokenFacts.GetText(TokenKind.ArgList)); break; case TypeKind.TK_ArrayType: { CType elementType = pType.AsArrayType().GetBaseElementType(); int rank; if (null == elementType) { Debug.Assert(false, "No element type"); break; } ErrAppendType(elementType, pctx); for (elementType = pType; elementType != null && elementType.IsArrayType(); elementType = elementType.AsArrayType().GetElementType()) { rank = elementType.AsArrayType().rank; // Add [] with (rank-1) commas inside ErrAppendChar('['); #if ! CSEE // known rank. if (rank > 1) { ErrAppendChar('*'); } #endif for (int i = rank; i > 1; --i) { ErrAppendChar(','); #if ! CSEE ErrAppendChar('*'); #endif } ErrAppendChar(']'); } break; } case TypeKind.TK_VoidType: ErrAppendName(GetNameManager().Lookup(TokenFacts.GetText(TokenKind.Void))); break; case TypeKind.TK_ParameterModifierType: // add ref or out ErrAppendString(pType.AsParameterModifierType().isOut ? "out " : "ref "); // add base type name ErrAppendType(pType.AsParameterModifierType().GetParameterType(), pctx); break; case TypeKind.TK_PointerType: // Generate the base type. ErrAppendType(pType.AsPointerType().GetReferentType(), pctx); { // add the trailing * ErrAppendChar('*'); } break; case TypeKind.TK_NullableType: ErrAppendType(pType.AsNullableType().GetUnderlyingType(), pctx); ErrAppendChar('?'); break; case TypeKind.TK_NaturalIntegerType: default: // Shouldn't happen. Debug.Assert(false, "Bad type kind"); break; } }