//
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
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) {
+ //
+ // 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);
}
-
-/*
- 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);
- }
- }
-*/
- 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;
}
//
return null;
}
+ static Expression ImplicitTupleLiteralConversion (ResolveContext rc, Expression source, TypeSpec targetType, Location loc)
+ {
+ var targetTypeArgument = targetType.TypeArguments;
+ if (source.Type.Arity != targetTypeArgument.Length)
+ return null;
+
+ var namedTarget = targetType as NamedTupleSpec;
+ var tupleLiteral = source as TupleLiteral;
+ Expression instance;
+
+ if (tupleLiteral == null && !ExpressionAnalyzer.IsInexpensiveLoad (source)) {
+ var expr_variable = LocalVariable.CreateCompilerGenerated (source.Type, rc.CurrentBlock, loc);
+ source = new CompilerAssign (expr_variable.CreateReferenceExpression (rc, loc), source, loc);
+ instance = expr_variable.CreateReferenceExpression (rc, loc);
+ } else {
+ instance = null;
+ }
+
+ var converted = new List<Expression> (targetType.Arity);
+ for (int i = 0; i < targetType.Arity; ++i) {
+ Expression elementSrc;
+ if (tupleLiteral != null) {
+ elementSrc = tupleLiteral.Elements [i].Expr;
+
+ if (namedTarget != null) {
+ var elementSrcName = tupleLiteral.Elements [i].Name;
+ if (elementSrcName != null && elementSrcName != namedTarget.Elements [i]) {
+ rc.Report.Warning (8123, 1, loc,
+ "The tuple element name `{0}' is ignored because a different name or no name is specified by the target type `{1}'",
+ elementSrcName, namedTarget.GetSignatureForErrorWithNames ());
+ }
+ }
+ } else {
+ elementSrc = new MemberAccess (instance, NamedTupleSpec.GetElementPropertyName (i)).Resolve (rc);
+ }
+
+ var res = ImplicitConversionStandard (rc, elementSrc, targetTypeArgument [i], loc);
+ if (res == null)
+ return null;
+
+ converted.Add (res);
+ }
+
+ return new TupleLiteralConversion (source, targetType, converted, loc);
+ }
+
+ static bool ImplicitTupleLiteralConversionExists (Expression source, TypeSpec targetType)
+ {
+ if (source.Type.Arity != targetType.Arity)
+ return false;
+
+ var srcTypeArgument = source.Type.TypeArguments;
+ var targetTypeArgument = targetType.TypeArguments;
+
+ var tupleLiteralElements = (source as TupleLiteral)?.Elements;
+
+ for (int i = 0; i < targetType.Arity; ++i) {
+ if (tupleLiteralElements != null) {
+ if (!ImplicitStandardConversionExists (tupleLiteralElements[i].Expr, targetTypeArgument [i])) {
+ return false;
+ }
+ } else {
+ if (!ImplicitStandardConversionExists (new EmptyExpression (srcTypeArgument [i]), targetTypeArgument [i])) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
//
// Full version of implicit conversion
//
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;
+ var interpolated_string = expr as InterpolatedString;
+ if (interpolated_string != null) {
+ if (target_type == rc.Module.PredefinedTypes.IFormattable.TypeSpec || target_type == rc.Module.PredefinedTypes.FormattableString.TypeSpec)
+ return true;
+ }
- return UserDefinedConversion (ec, expr, target_type, true, true, Location.Null) != null;
+ return ImplicitStandardConversionExists (expr, target_type);
}
//
if (expr_type == target_type)
return true;
+ if (expr_type == InternalType.ThrowExpr)
+ return target_type.Kind != MemberKind.InternalCompilerType;
+
if (target_type.IsNullableType)
return ImplicitNulableConversion (null, expr, target_type) != null;
if (ImplicitBoxingConversion (null, expr_type, target_type) != null)
return true;
-
+
+ if (expr_type.IsTupleType && target_type.IsTupleType)
+ return ImplicitTupleLiteralConversionExists (expr, target_type);
+
//
// Implicit Constant Expression Conversions
//
// 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
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;
if (target.IsNullableType)
- source = new Nullable.LiftedConversion (source, unwrap, target).Resolve (ec);
+ 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
Expression e;
if (expr_type == target_type) {
- if (expr_type != InternalType.NullLiteral && expr_type != InternalType.AnonymousMethod)
+ if (expr_type != InternalType.NullLiteral && expr_type != InternalType.AnonymousMethod && expr_type != InternalType.ThrowExpr)
return expr;
return null;
}
return null;
}
+ if (expr_type == InternalType.ThrowExpr) {
+ return target_type.Kind == MemberKind.InternalCompilerType ? null : EmptyCast.Create (expr, target_type);
+ }
+
if (target_type.IsNullableType)
return ImplicitNulableConversion (ec, expr, target_type);
return c;
}
+ if (expr_type.IsTupleType) {
+ if (target_type.IsTupleType)
+ return ImplicitTupleLiteralConversion (ec, expr, target_type, loc);
+
+ if (expr is TupleLiteral && TupleLiteral.ContainsNoTypeElement (expr_type))
+ return null;
+ }
+
e = ImplicitNumericConversion (expr, expr_type, target_type);
if (e != null)
return e;
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;
}
/// ImplicitConversion. If there is no implicit conversion, then
/// an error is signaled
/// </summary>
- static public Expression ImplicitConversionRequired (ResolveContext ec, Expression source,
+ public static Expression ImplicitConversionRequired (ResolveContext ec, Expression source,
TypeSpec target_type, Location loc)
{
Expression e = ImplicitConversion (ec, source, target_type, loc);
if (e != null)
return e;
- source.Error_ValueCannotBeConverted (ec, target_type, false);
+ if (target_type != InternalType.ErrorType)
+ 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;
}
}
e = ExplicitUserConversion (ec, expr, target_type, loc);
+
if (e != null)
return e;