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);
}