//
static class Convert
{
+ [Flags]
+ public enum UserConversionRestriction
+ {
+ None = 0,
+ ImplicitOnly = 1,
+ ProbingOnly = 1 << 1,
+ NullableSourceOnly = 1 << 2
+
+ }
//
// From a one-dimensional array-type S[] to System.Collections.IList<T> and base
// interfaces of this interface, provided there is an implicit reference conversion
// From T to a type parameter U, provided T depends on U
//
if (target_type.IsGenericParameter) {
- if (expr_type.TypeArguments != null) {
- foreach (var targ in expr_type.TypeArguments) {
- if (!TypeSpecComparer.Override.IsEqual (target_type, targ))
- continue;
-
- if (expr == null)
- return EmptyExpression.Null;
+ if (expr_type.TypeArguments != null && expr_type.HasDependencyOn (target_type)) {
+ if (expr == null)
+ return EmptyExpression.Null;
- if (expr_type.IsReferenceType && !((TypeParameterSpec)target_type).IsReferenceType)
- return new BoxedCast (expr, target_type);
+ if (expr_type.IsReferenceType && !((TypeParameterSpec) target_type).IsReferenceType)
+ return new BoxedCast (expr, target_type);
- return new ClassCast (expr, target_type);
- }
+ return new ClassCast (expr, target_type);
}
return null;
return null;
}
- static Expression ExplicitTypeParameterConversion (Expression source, TypeSpec source_type, TypeSpec target_type)
+ static Expression ExplicitTypeParameterConversionFromT (Expression source, TypeSpec source_type, TypeSpec target_type)
{
var target_tp = target_type as TypeParameterSpec;
if (target_tp != null) {
- if (target_tp.TypeArguments != null) {
- foreach (var targ in target_tp.TypeArguments) {
- if (!TypeSpecComparer.Override.IsEqual (source_type, targ))
- continue;
-
- return source == null ? EmptyExpression.Null : new ClassCast (source, target_type);
- }
- }
-/*
- if (target_tp.Interfaces != null) {
- foreach (TypeSpec iface in target_tp.Interfaces) {
- if (!TypeManager.IsGenericParameter (iface))
- continue;
-
- if (TypeManager.IsSubclassOf (source_type, iface))
- return source == null ? EmptyExpression.Null : new ClassCast (source, target_type, true);
- }
+ //
+ // From a type parameter U to T, provided T depends on U
+ //
+ if (target_tp.TypeArguments != null && target_tp.HasDependencyOn (source_type)) {
+ return source == null ? EmptyExpression.Null : new ClassCast (source, target_type);
}
-*/
- return null;
}
+ //
+ // From T to any interface-type I provided there is not already an implicit conversion from T to I
+ //
if (target_type.IsInterface)
return source == null ? EmptyExpression.Null : new ClassCast (source, target_type, true);
return null;
}
+ static Expression ExplicitTypeParameterConversionToT (Expression source, TypeSpec source_type, TypeParameterSpec target_type)
+ {
+ //
+ // From the effective base class C of T to T and from any base class of C to T
+ //
+ var effective = target_type.GetEffectiveBase ();
+ if (TypeSpecComparer.IsEqual (effective, source_type) || TypeSpec.IsBaseClass (effective, source_type, false))
+ return source == null ? EmptyExpression.Null : new ClassCast (source, target_type);
+
+ return null;
+ }
+
public static Expression ImplicitReferenceConversion (Expression expr, TypeSpec target_type, bool explicit_cast)
{
TypeSpec expr_type = expr.Type;
if (target_type.Kind == MemberKind.InternalCompilerType)
return target_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
- return TypeSpec.IsReferenceType (target_type);
+ return TypeSpec.IsReferenceType (target_type) || target_type.Kind == MemberKind.PointerType;
}
//
//
public static bool ImplicitConversionExists (ResolveContext ec, Expression expr, TypeSpec target_type)
{
- if (ImplicitStandardConversionExists (expr, target_type))
+ if (ImplicitStandardConversionExists (ec, expr, target_type))
return true;
if (expr.Type == InternalType.AnonymousMethod) {
return ame.ImplicitStandardConversionExists (ec, target_type);
}
+ // Conversion from __arglist to System.ArgIterator
+ if (expr.Type == InternalType.Arglist)
+ return target_type == ec.Module.PredefinedTypes.ArgIterator.TypeSpec;
+
+ return UserDefinedConversion (ec, expr, target_type,
+ UserConversionRestriction.ImplicitOnly | UserConversionRestriction.ProbingOnly, Location.Null) != null;
+ }
+
+ public static bool ImplicitStandardConversionExists (ResolveContext rc, Expression expr, TypeSpec target_type)
+ {
if (expr.eclass == ExprClass.MethodGroup) {
- if (target_type.IsDelegate && ec.Module.Compiler.Settings.Version != LanguageVersion.ISO_1) {
+ if (target_type.IsDelegate && rc.Module.Compiler.Settings.Version != LanguageVersion.ISO_1) {
MethodGroupExpr mg = expr as MethodGroupExpr;
if (mg != null)
- return DelegateCreation.ImplicitStandardConversionExists (ec, mg, target_type);
+ return DelegateCreation.ImplicitStandardConversionExists (rc, mg, target_type);
}
return false;
}
- // Conversion from __arglist to System.ArgIterator
- if (expr.Type == InternalType.Arglist)
- return target_type == ec.Module.PredefinedTypes.ArgIterator.TypeSpec;
-
- return UserDefinedConversion (ec, expr, target_type, true, true, Location.Null) != null;
+ return ImplicitStandardConversionExists (expr, target_type);
}
//
// by making use of FindMostEncomp* methods. Applies the correct rules separately
// for explicit and implicit conversion operators.
//
- static TypeSpec FindMostSpecificSource (List<MethodSpec> list, TypeSpec sourceType, Expression source, bool apply_explicit_conv_rules)
+ static TypeSpec FindMostSpecificSource (ResolveContext rc, List<MethodSpec> list, TypeSpec sourceType, Expression source, bool apply_explicit_conv_rules)
{
TypeSpec[] src_types_set = null;
var candidate_set = new List<TypeSpec> ();
foreach (TypeSpec param_type in src_types_set){
- if (ImplicitStandardConversionExists (source, param_type))
+ if (ImplicitStandardConversionExists (rc, source, param_type))
candidate_set.Add (param_type);
}
- if (candidate_set.Count != 0)
+ if (candidate_set.Count != 0) {
+ if (source.eclass == ExprClass.MethodGroup)
+ return InternalType.FakeInternalType;
+
return FindMostEncompassedType (candidate_set);
+ }
}
//
/// </summary>
static public Expression ImplicitUserConversion (ResolveContext ec, Expression source, TypeSpec target, Location loc)
{
- return UserDefinedConversion (ec, source, target, true, false, loc);
+ return UserDefinedConversion (ec, source, target, UserConversionRestriction.ImplicitOnly, loc);
}
/// <summary>
/// </summary>
static Expression ExplicitUserConversion (ResolveContext ec, Expression source, TypeSpec target, Location loc)
{
- return UserDefinedConversion (ec, source, target, false, false, loc);
+ return UserDefinedConversion (ec, source, target, 0, loc);
}
- static void FindApplicableUserDefinedConversionOperators (IList<MemberSpec> operators, Expression source, TypeSpec target, bool implicitOnly, ref List<MethodSpec> candidates)
+ static void FindApplicableUserDefinedConversionOperators (ResolveContext rc, IList<MemberSpec> operators, Expression source, TypeSpec target, UserConversionRestriction restr, ref List<MethodSpec> candidates)
{
if (source.Type.IsInterface) {
// Neither A nor B are interface-types
continue;
var t = op.Parameters.Types[0];
- if (source.Type != t && !ImplicitStandardConversionExists (source, t)) {
- if (implicitOnly)
+ if (source.Type != t && !ImplicitStandardConversionExists (rc, source, t)) {
+ if ((restr & UserConversionRestriction.ImplicitOnly) != 0)
continue;
if (!ImplicitStandardConversionExists (new EmptyExpression (t), source.Type))
- continue;
+ continue;
}
+ if ((restr & UserConversionRestriction.NullableSourceOnly) != 0 && !t.IsNullableType)
+ continue;
+
t = op.ReturnType;
if (t.IsInterface)
t = Nullable.NullableInfo.GetUnderlyingType (t);
if (!ImplicitStandardConversionExists (new EmptyExpression (t), target)) {
- if (implicitOnly)
+ if ((restr & UserConversionRestriction.ImplicitOnly) != 0)
continue;
if (texpr == null)
//
// User-defined conversions
//
- static Expression UserDefinedConversion (ResolveContext ec, Expression source, TypeSpec target, bool implicitOnly, bool probingOnly, Location loc)
+ public static Expression UserDefinedConversion (ResolveContext rc, Expression source, TypeSpec target, UserConversionRestriction restr, Location loc)
{
List<MethodSpec> candidates = null;
TypeSpec target_type = target;
Expression source_type_expr;
bool nullable_source = false;
+ var implicitOnly = (restr & UserConversionRestriction.ImplicitOnly) != 0;
if (source_type.IsNullableType) {
// No unwrapping conversion S? -> T for non-reference types
var operators = MemberCache.GetUserOperator (source_type, Operator.OpType.Implicit, declared_only);
if (operators != null) {
- FindApplicableUserDefinedConversionOperators (operators, source_type_expr, target_type, implicitOnly, ref candidates);
+ FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates);
}
if (!implicitOnly) {
operators = MemberCache.GetUserOperator (source_type, Operator.OpType.Explicit, declared_only);
if (operators != null) {
- FindApplicableUserDefinedConversionOperators (operators, source_type_expr, target_type, false, ref candidates);
+ FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates);
}
}
}
var operators = MemberCache.GetUserOperator (target_type, Operator.OpType.Implicit, declared_only);
if (operators != null) {
- FindApplicableUserDefinedConversionOperators (operators, source_type_expr, target_type, implicitOnly, ref candidates);
+ FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates);
}
if (!implicitOnly) {
operators = MemberCache.GetUserOperator (target_type, Operator.OpType.Explicit, declared_only);
if (operators != null) {
- FindApplicableUserDefinedConversionOperators (operators, source_type_expr, target_type, false, ref candidates);
+ FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates);
}
}
}
// Pass original source type to find the best match against input type and
// not the unwrapped expression
//
- s_x = FindMostSpecificSource (candidates, source.Type, source_type_expr, !implicitOnly);
+ s_x = FindMostSpecificSource (rc, candidates, source.Type, source_type_expr, !implicitOnly);
if (s_x == null)
return null;
//
// Unless running in probing more
//
- if (!probingOnly) {
- MethodSpec ambig_arg = null;
+ if ((restr & UserConversionRestriction.ProbingOnly) == 0) {
+ MethodSpec ambig_arg = candidates [0];
+ most_specific_operator = candidates [1];
+ /*
foreach (var candidate in candidates) {
if (candidate.ReturnType == t_x)
most_specific_operator = candidate;
else if (candidate.Parameters.Types[0] == s_x)
ambig_arg = candidate;
}
-
- ec.Report.Error (457, loc,
+ */
+ rc.Report.Error (457, loc,
"Ambiguous user defined operators `{0}' and `{1}' when converting from `{2}' to `{3}'",
ambig_arg.GetSignatureForError (), most_specific_operator.GetSignatureForError (),
source.Type.GetSignatureForError (), target.GetSignatureForError ());
if (s_x != source_type) {
var c = source as Constant;
if (c != null) {
- source = c.Reduce (ec, s_x);
+ source = c.Reduce (rc, s_x);
if (source == null)
c = null;
}
if (c == null) {
source = implicitOnly ?
- ImplicitConversionStandard (ec, source_type_expr, s_x, loc) :
- ExplicitConversionStandard (ec, source_type_expr, s_x, loc);
+ ImplicitConversionStandard (rc, source_type_expr, s_x, loc) :
+ ExplicitConversionStandard (rc, source_type_expr, s_x, loc);
}
} else {
source = source_type_expr;
}
- source = new UserCast (most_specific_operator, source, loc).Resolve (ec);
+ source = new UserCast (most_specific_operator, source, loc).Resolve (rc);
//
// Convert result type when it's different to best operator return type
//
// User operator is of T?
//
- if (t_x.IsNullableType && target.IsNullableType) {
+ if (t_x.IsNullableType && (target.IsNullableType || !implicitOnly)) {
//
// User operator return type does not match target type we need
// yet another conversion. This should happen for promoted numeric
var unwrap = Nullable.Unwrap.CreateUnwrapped (source);
source = implicitOnly ?
- ImplicitConversionStandard (ec, unwrap, target_type, loc) :
- ExplicitConversionStandard (ec, unwrap, target_type, loc);
+ ImplicitConversionStandard (rc, unwrap, target_type, loc) :
+ ExplicitConversionStandard (rc, unwrap, target_type, loc);
if (source == null)
return null;
- source = new Nullable.LiftedConversion (source, unwrap, target).Resolve (ec);
+ if (target.IsNullableType)
+ source = new Nullable.LiftedConversion (source, unwrap, target).Resolve (rc);
}
} else {
source = implicitOnly ?
- ImplicitConversionStandard (ec, source, target_type, loc) :
- ExplicitConversionStandard (ec, source, target_type, loc);
+ ImplicitConversionStandard (rc, source, target_type, loc) :
+ ExplicitConversionStandard (rc, source, target_type, loc);
if (source == null)
return null;
// only non-nullable type we need to lift it manually
//
if (nullable_source && !s_x.IsNullableType)
- return new Nullable.LiftedConversion (source, source_type_expr, target).Resolve (ec);
+ return new Nullable.LiftedConversion (source, source_type_expr, target).Resolve (rc);
//
// Target is of nullable type but source type is not, wrap the result expression
}
}
- if (ec.IsUnsafe) {
- var target_pc = target_type as PointerContainer;
- if (target_pc != null) {
- if (expr_type.IsPointer) {
- //
- // Pointer types are same when they have same element types
- //
- if (expr_type == target_pc)
- return expr;
+ var target_pc = target_type as PointerContainer;
+ if (target_pc != null) {
+ if (expr_type.IsPointer) {
+ //
+ // Pointer types are same when they have same element types
+ //
+ if (expr_type == target_pc)
+ return expr;
- if (target_pc.Element.Kind == MemberKind.Void)
- return EmptyCast.Create (expr, target_type);
+ if (target_pc.Element.Kind == MemberKind.Void)
+ return EmptyCast.Create (expr, target_type);
//return null;
- }
-
- if (expr_type == InternalType.NullLiteral)
- return new NullPointer (target_type, loc);
}
+
+ if (expr_type == InternalType.NullLiteral)
+ return new NullPointer (target_type, loc);
}
if (expr_type == InternalType.AnonymousMethod){
if (expr_type.IsStruct && TypeSpecComparer.IsEqual (expr_type, target_type))
return expr_type == target_type ? expr : EmptyCast.Create (expr, target_type);
+ var interpolated_string = expr as InterpolatedString;
+ if (interpolated_string != null) {
+ if (target_type == ec.Module.PredefinedTypes.IFormattable.TypeSpec || target_type == ec.Module.PredefinedTypes.FormattableString.TypeSpec)
+ return interpolated_string.ConvertTo (ec, target_type);
+ }
+
return null;
}
return e;
source.Error_ValueCannotBeConverted (ec, target_type, false);
+
return null;
}
return source == null ? EmptyExpression.Null : new UnboxCast (source, target_type);
//
- // Explicit type parameter conversion.
+ // Explicit type parameter conversion from T
//
if (source_type.Kind == MemberKind.TypeParameter)
- return ExplicitTypeParameterConversion (source, source_type, target_type);
+ return ExplicitTypeParameterConversionFromT (source, source_type, target_type);
bool target_is_value_type = target_type.Kind == MemberKind.Struct || target_type.Kind == MemberKind.Enum;
// From any interface-type S to to any class type T, provided T is not
// sealed, or provided T implements S.
//
+ // This also covers Explicit conversions involving type parameters
+ // section From any interface type to T
+ //
if (source_type.Kind == MemberKind.Interface) {
if (!target_type.IsSealed || target_type.ImplementsInterface (source_type, true)) {
if (source == null)
if (source_array.Rank == target_array.Rank) {
source_type = source_array.Element;
- if (!TypeSpec.IsReferenceType (source_type))
- return null;
-
var target_element = target_array.Element;
+
+ //
+ // LAMESPEC: Type parameters are special cased somehow but
+ // only when both source and target elements are type parameters
+ //
+ if ((source_type.Kind & target_element.Kind & MemberKind.TypeParameter) == MemberKind.TypeParameter) {
+ //
+ // Conversion is allowed unless source element type has struct constrain
+ //
+ if (TypeSpec.IsValueType (source_type))
+ return null;
+ } else {
+ if (!TypeSpec.IsReferenceType (source_type))
+ return null;
+ }
+
if (!TypeSpec.IsReferenceType (target_element))
return null;
return source == null ? EmptyExpression.Null : new ClassCast (source, target_type);
}
+ var tps = target_type as TypeParameterSpec;
+ if (tps != null)
+ return ExplicitTypeParameterConversionToT (source, source_type, tps);
+
return null;
}
target = TypeManager.GetTypeArguments (target_type) [0];
e = ExplicitConversionCore (ec, expr, target, loc);
if (e != null)
- return Nullable.Wrap.Create (e, target_type);
+ return TypeSpec.IsReferenceType (expr.Type) ? new UnboxCast (expr, target_type) : Nullable.Wrap.Create (e, target_type);
} else if (expr_type.IsNullableType) {
e = ImplicitBoxingConversion (expr, Nullable.NullableInfo.GetUnderlyingType (expr_type), target_type);
if (e != null)
}
e = ExplicitUserConversion (ec, expr, target_type, loc);
+
if (e != null)
return e;