[mcs] Initial by ref returns and variables support
[mono.git] / mcs / mcs / convert.cs
index 00be96b40a2d03c5bec51f020e2b51f8c087942b..b11477c104394b7bc77075d6e4fa02827797529e 100644 (file)
@@ -27,6 +27,15 @@ namespace Mono.CSharp {
        //
        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
@@ -336,7 +345,7 @@ namespace Mono.CSharp {
                                        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;
                                }
 
                                //
@@ -670,12 +679,84 @@ namespace Mono.CSharp {
                        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) {
@@ -686,21 +767,33 @@ namespace Mono.CSharp {
                                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);
                }
 
                //
@@ -723,6 +816,9 @@ namespace Mono.CSharp {
                        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;
 
@@ -734,7 +830,10 @@ namespace Mono.CSharp {
 
                        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
                        //
@@ -911,7 +1010,7 @@ namespace Mono.CSharp {
                // 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;
 
@@ -937,12 +1036,16 @@ namespace Mono.CSharp {
                                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);
+                               }
                        }
 
                        //
@@ -1010,7 +1113,7 @@ namespace Mono.CSharp {
                /// </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>
@@ -1018,10 +1121,10 @@ namespace Mono.CSharp {
                /// </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
@@ -1043,14 +1146,17 @@ namespace Mono.CSharp {
                                        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)
@@ -1061,7 +1167,7 @@ namespace Mono.CSharp {
                                                t = Nullable.NullableInfo.GetUnderlyingType (t);
 
                                        if (!ImplicitStandardConversionExists (new EmptyExpression (t), target)) {
-                                               if (implicitOnly)
+                                               if ((restr & UserConversionRestriction.ImplicitOnly) != 0)
                                                        continue;
 
                                                if (texpr == null)
@@ -1082,7 +1188,7 @@ namespace Mono.CSharp {
                //
                // 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;
 
@@ -1094,6 +1200,7 @@ namespace Mono.CSharp {
                        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
@@ -1119,13 +1226,13 @@ namespace Mono.CSharp {
 
                                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);
                                        }
                                }
                        }
@@ -1135,13 +1242,13 @@ namespace Mono.CSharp {
 
                                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);
                                        }
                                }
                        }
@@ -1163,7 +1270,7 @@ namespace Mono.CSharp {
                                // 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;
 
@@ -1183,16 +1290,18 @@ namespace Mono.CSharp {
                                        //
                                        // 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 ());
@@ -1208,21 +1317,21 @@ namespace Mono.CSharp {
                        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
@@ -1241,19 +1350,19 @@ namespace Mono.CSharp {
                                                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;
@@ -1266,7 +1375,7 @@ namespace Mono.CSharp {
                        // 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
@@ -1339,7 +1448,7 @@ namespace Mono.CSharp {
                        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;
                        }
@@ -1365,6 +1474,10 @@ namespace Mono.CSharp {
                                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);
 
@@ -1382,6 +1495,19 @@ namespace Mono.CSharp {
                                        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;
+                       }
+
+                       if (expr is ReferenceExpression) {
+                               // Only identify conversion is allowed
+                               return null;
+                       }
+
                        e = ImplicitNumericConversion (expr, expr_type, target_type);
                        if (e != null)
                                return e;
@@ -1447,6 +1573,12 @@ namespace Mono.CSharp {
                        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;
                }
 
@@ -1455,14 +1587,15 @@ namespace Mono.CSharp {
                ///   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;
                }
@@ -2230,6 +2363,7 @@ namespace Mono.CSharp {
                        }
                        
                        e = ExplicitUserConversion (ec, expr, target_type, loc);
+
                        if (e != null)
                                return e;