2009-06-26 Marek Safar <marek.safar@gmail.com>
authorMarek Safar <marek.safar@gmail.com>
Fri, 26 Jun 2009 15:07:48 +0000 (15:07 -0000)
committerMarek Safar <marek.safar@gmail.com>
Fri, 26 Jun 2009 15:07:48 +0000 (15:07 -0000)
* generic.cs, argument.cs, expression.cs, ecore.cs, cs-parser.jay:
Implemented C# 4.0 named arguments.

svn path=/trunk/mcs/; revision=136971

mcs/mcs/ChangeLog
mcs/mcs/argument.cs
mcs/mcs/cs-parser.jay
mcs/mcs/ecore.cs
mcs/mcs/expression.cs
mcs/mcs/generic.cs

index e7c2fae5cd39d3c45bb8b29c8c9edd336984b9d8..7be283f30a15fac3285c6fe2afe3a2ad57f86331 100644 (file)
@@ -1,3 +1,8 @@
+2009-06-26  Marek Safar  <marek.safar@gmail.com>
+
+       * generic.cs, argument.cs, expression.cs, ecore.cs, cs-parser.jay:
+       Implemented C# 4.0 named arguments.
+
 2009-06-24  Marek Safar  <marek.safar@gmail.com>
 
        * typemanager.cs, parameter.cs, iterators.cs, convert.cs,
index 5088a814ddc830d6612f1376cffdec6de5769eaf..3ca17b359f2d3f3d431a454cf1623eda5b676534 100644 (file)
@@ -25,7 +25,7 @@ namespace Mono.CSharp
                {
                        Ref = 1,                // ref modifier used
                        Out = 2,                // out modifier used
-                       Default = 3             // argument created from default value
+                       Default = 3             // argument created from default parameter value
                }
 
                public readonly AType ArgType;
@@ -80,6 +80,10 @@ namespace Mono.CSharp
                        return TypeManager.CSharpName (Expr.Type);
                }
 
+               public bool IsDefaultArgument {
+                       get { return ArgType == AType.Default; }
+               }
+
                public bool ResolveMethodGroup (EmitContext ec)
                {
                        SimpleName sn = Expr as SimpleName;
@@ -114,7 +118,7 @@ namespace Mono.CSharp
                        }
                }
 
-               public void Emit (EmitContext ec)
+               public virtual void Emit (EmitContext ec)
                {
                        if (ArgType != AType.Ref && ArgType != AType.Out) {
                                Expr.Emit (ec);
@@ -146,9 +150,10 @@ namespace Mono.CSharp
                }
        }
 
-       class NamedArgument : Argument
+       public class NamedArgument : Argument
        {
                public readonly LocatedToken Name;
+               LocalTemporary variable;
 
                public NamedArgument (LocatedToken name, Expression expr)
                        : base (expr)
@@ -159,14 +164,33 @@ namespace Mono.CSharp
                public override Expression CreateExpressionTree (EmitContext ec)
                {
                        Report.Error (853, Name.Location, "An expression tree cannot contain named argument");
-                       return null;
+                       return base.CreateExpressionTree (ec);
+               }
+
+               public override void Emit (EmitContext ec)
+               {
+                       // TODO: Should guard against multiple emits
+                       base.Emit (ec);
+
+                       // Release temporary variable when used
+                       if (variable != null)
+                               variable.Release (ec);
+               }
+
+               public void EmitAssign (EmitContext ec)
+               {
+                       Expr.Emit (ec);
+                       variable = new LocalTemporary (Expr.Type);
+                       variable.Store (ec);
+
+                       Expr = variable;
                }
        }
 
        public class Arguments
        {
-               // TODO: This should really be linked list
-               ArrayList args;
+               ArrayList args;                 // TODO: This should really be linked list
+               ArrayList reordered;    // TODO: LinkedList
 
                public Arguments (int capacity)
                {
@@ -243,20 +267,22 @@ namespace Mono.CSharp
                //
                public void Emit (EmitContext ec, bool dup_args, LocalTemporary this_arg)
                {
-                       int top = Count;
                        LocalTemporary[] temps = null;
 
-                       if (dup_args && top != 0)
-                               temps = new LocalTemporary [top];
+                       if (dup_args && Count != 0)
+                               temps = new LocalTemporary [Count];
 
-                       int argument_index = 0;
-                       Argument a;
-                       for (int i = 0; i < top; i++) {
-                               a = this [argument_index++];
+                       if (reordered != null && Count > 1) {
+                               foreach (NamedArgument na in reordered)
+                                       na.EmitAssign (ec);
+                       }
+
+                       int i = 0;
+                       foreach (Argument a in args) {
                                a.Emit (ec);
                                if (dup_args) {
                                        ec.ig.Emit (OpCodes.Dup);
-                                       (temps [i] = new LocalTemporary (a.Type)).Store (ec);
+                                       (temps [i++] = new LocalTemporary (a.Type)).Store (ec);
                                }
                        }
 
@@ -264,7 +290,7 @@ namespace Mono.CSharp
                                if (this_arg != null)
                                        this_arg.Emit (ec);
 
-                               for (int i = 0; i < top; i++) {
+                               for (i = 0; i < temps.Length; i++) {
                                        temps[i].Emit (ec);
                                        temps[i].Release (ec);
                                }
@@ -293,6 +319,20 @@ namespace Mono.CSharp
                        args.Insert (index, arg);
                }
 
+               public void MarkReorderedArgument (NamedArgument a)
+               {
+                       //
+                       // Constant expression can have no effect on left-to-right execution
+                       //
+                       if (a.Expr is Constant)
+                               return;
+
+                       if (reordered == null)
+                               reordered = new ArrayList ();
+
+                       reordered.Add (a);
+               }
+
                public void Resolve (EmitContext ec)
                {
                        foreach (Argument a in args)
@@ -312,6 +352,7 @@ namespace Mono.CSharp
 
                public Argument this [int index] {
                        get { return (Argument) args [index]; }
+                       set { args [index] = value; }
                }
        }
 }
\ No newline at end of file
index 700046a3a2a59adea91ce70bd9ac23fca2cd4cb0..dcc46a11540da0ca4fbaa20b2ea8fe0d5311ed37 100644 (file)
@@ -3194,7 +3194,7 @@ opt_argument_list
        ;
 
 argument_list
-       : argument
+       : argument_or_named_argument
          { 
                Arguments list = new Arguments (4);
                list.Add ((Argument) $1);
@@ -3203,18 +3203,32 @@ argument_list
        | argument_list COMMA argument
          {
                Arguments list = (Arguments) $1;
-               if (!($3 is NamedArgument) && list [list.Count - 1] is NamedArgument)
+               if (list [list.Count - 1] is NamedArgument)
                        Error_NamedArgumentExpected ((NamedArgument) list [list.Count - 1]);
                
                list.Add ((Argument) $3);
                $$ = list;
          }
+       | argument_list COMMA named_argument
+         {
+               Arguments list = (Arguments) $1;
+               NamedArgument a = (NamedArgument) $3;
+               for (int i = 0; i < list.Count; ++i) {
+                       NamedArgument na = list [i] as NamedArgument;
+                       if (na != null && na.Name.Value == a.Name.Value)
+                               Report.Error (1740, na.Name.Location, "Named argument `{0}' specified multiple times",
+                                       na.Name.Value);
+               }
+               
+               list.Add (a);
+               $$ = list;
+         }
        | argument_list COMMA
          {
                Report.Error (839, GetLocation ($2), "An argument is missing");
                $$ = null;
          }
-       | COMMA argument
+       | COMMA argument_or_named_argument
          {
                Report.Error (839, GetLocation ($1), "An argument is missing");
                $$ = null;
@@ -3227,7 +3241,11 @@ argument
                $$ = new Argument ((Expression) $1);
          }
        | non_simple_argument
-       | named_argument        
+       ;
+
+argument_or_named_argument
+       : argument
+       | named_argument
        ;
 
 non_simple_argument
index 1f4fff5ba98ef99b3b126d940a0b59d9a2de4fde..cc6d1bdd23712d23aff6f9952fc7fea66767edfb 100644 (file)
@@ -3145,7 +3145,7 @@ namespace Mono.CSharp {
                                arguments = new Arguments (1);
 
                        arguments.Insert (0, new Argument (ExtensionExpression));
-                       MethodGroupExpr mg = ResolveOverloadExtensions (ec, arguments, namespace_entry, loc);
+                       MethodGroupExpr mg = ResolveOverloadExtensions (ec, ref arguments, namespace_entry, loc);
 
                        // Store resolved argument and restore original arguments
                        if (mg != null)
@@ -3156,7 +3156,7 @@ namespace Mono.CSharp {
                        return mg;
                }
 
-               MethodGroupExpr ResolveOverloadExtensions (EmitContext ec, Arguments arguments, NamespaceEntry ns, Location loc)
+               MethodGroupExpr ResolveOverloadExtensions (EmitContext ec, ref Arguments arguments, NamespaceEntry ns, Location loc)
                {
                        // Use normal resolve rules
                        MethodGroupExpr mg = base.OverloadResolve (ec, ref arguments, ns != null, loc);
@@ -3173,7 +3173,7 @@ namespace Mono.CSharp {
 
                        e.ExtensionExpression = ExtensionExpression;
                        e.SetTypeArguments (type_arguments);                    
-                       return e.ResolveOverloadExtensions (ec, arguments, e.namespace_entry, loc);
+                       return e.ResolveOverloadExtensions (ec, ref arguments, e.namespace_entry, loc);
                }               
        }
 
@@ -3421,6 +3421,10 @@ namespace Mono.CSharp {
                        {
                                Argument a = args [j];
 
+                               // Provided default argument value is never better
+                               if (a.IsDefaultArgument && candidate_params == best_params)
+                                       return false;
+
                                Type ct = candidate_pd.Types [c_idx];
                                Type bt = best_pd.Types [b_idx];
 
@@ -3718,15 +3722,85 @@ namespace Mono.CSharp {
                                        }
                                }
 
-                               if (arg_count + optional_count != param_count) {
+                               int args_gap = Math.Abs (arg_count - param_count);
+                               if (optional_count != 0) {
+                                       if (args_gap > optional_count)
+                                               return int.MaxValue - 10000 + args_gap - optional_count;
+
+                                       // Readjust expected number when params used
+                                       if (pd.HasParams) {
+                                               optional_count--;
+                                               if (arg_count < param_count)
+                                                       param_count--;
+                                       }
+                               } else if (arg_count != param_count) {
                                        if (!pd.HasParams)
-                                               return int.MaxValue - 10000 + Math.Abs (arg_count - param_count);
+                                               return int.MaxValue - 10000 + args_gap;
                                        if (arg_count < param_count - 1)
-                                               return int.MaxValue - 10000 + Math.Abs (arg_count - param_count);
+                                               return int.MaxValue - 10000 + args_gap;
+                               }
+
+                               // Initialize expanded form of a method with 1 params parameter
+                               params_expanded_form = param_count == 1 && pd.HasParams;
+
+                               // Resize to fit optional arguments
+                               if (optional_count != 0) {
+                                       Arguments resized;
+                                       if (arguments == null) {
+                                               resized = new Arguments (optional_count);
+                                       } else {
+                                               resized = new Arguments (param_count);
+                                               resized.AddRange (arguments);
+                                       }
+
+                                       for (int i = arg_count; i < param_count; ++i)
+                                               resized.Add (null);
+                                       arguments = resized;
+                               }
+                       }
+
+                       if (arg_count > 0) {
+                               //
+                               // Shuffle named arguments to the right positions if there are any
+                               //
+                               if (arguments [arg_count - 1] is NamedArgument) {
+                                       arg_count = arguments.Count;
+
+                                       for (int i = 0; i < arg_count; ++i) {
+                                               bool arg_moved = false;
+                                               while (true) {
+                                                       NamedArgument na = arguments[i] as NamedArgument;
+                                                       if (na == null)
+                                                               break;
+
+                                                       int index = pd.GetParameterIndexByName (na.Name.Value);
+
+                                                       // Named parameter not found or already reordered
+                                                       if (index <= i)
+                                                               break;
+
+                                                       // When using parameters which should not be available to the user
+                                                       if (index >= param_count)
+                                                               break;
 
-                                       // Initialize expanded form of a method with 1 params parameter
-                                       params_expanded_form = param_count == 1 && pd.HasParams;
+                                                       if (!arg_moved) {
+                                                               arguments.MarkReorderedArgument (na);
+                                                               arg_moved = true;
+                                                       }
+
+                                                       Argument temp = arguments[index];
+                                                       arguments[index] = arguments[i];
+                                                       arguments[i] = temp;
+
+                                                       if (temp == null)
+                                                               break;
+                                               }
+                                       }
+                               } else {
+                                       arg_count = arguments.Count;
                                }
+                       } else if (arguments != null) {
+                               arg_count = arguments.Count;
                        }
 
 #if GMCS_SOURCE
@@ -3760,25 +3834,6 @@ namespace Mono.CSharp {
                        }
 #endif
 
-                       if (optional_count != 0) {
-                               Arguments args = new Arguments (optional_count + arg_count);
-                               for (int i = 0; i < pd.Count; ++i) {
-                                       if (i < arg_count) {
-                                               args.Add (arguments[i]);
-                                               continue;
-                                       }
-
-                                       Expression e = pd.FixedParameters [i].DefaultValue as Constant;
-                                       if (e == null)
-                                               e = new DefaultValueExpression (new TypeExpression (pd.Types [i], loc), loc).Resolve (ec);
-
-                                       args.Add (new Argument (e, Argument.AType.Default));
-                               }
-
-                               arguments = args;
-                       }
-
-
                        //
                        // 2. Each argument has to be implicitly convertible to method parameter
                        //
@@ -3787,8 +3842,17 @@ namespace Mono.CSharp {
                        Type pt = null;
                        for (int i = 0; i < arg_count; i++) {
                                Argument a = arguments [i];
-                               Parameter.Modifier a_mod = a.Modifier &
-                                       ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
+                               if (a == null) {
+                                       if (!pd.FixedParameters [i].HasDefaultValue)
+                                               throw new InternalErrorException ();
+
+                                       Expression e = pd.FixedParameters [i].DefaultValue as Constant;
+                                       if (e == null)
+                                               e = new DefaultValueExpression (new TypeExpression (pd.Types [i], loc), loc).Resolve (ec);
+
+                                       arguments [i] = new Argument (e, Argument.AType.Default);
+                                       continue;
+                               }
 
                                if (p_mod != Parameter.Modifier.PARAMS) {
                                        p_mod = pd.FixedParameters [i].ModFlags & ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
@@ -3797,6 +3861,7 @@ namespace Mono.CSharp {
                                        params_expanded_form = true;
                                }
 
+                               Parameter.Modifier a_mod = a.Modifier & ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
                                int score = 1;
                                if (!params_expanded_form)
                                        score = IsArgumentCompatible (ec, a_mod, a, p_mod & ~Parameter.Modifier.PARAMS, pt);
@@ -3815,8 +3880,8 @@ namespace Mono.CSharp {
                                }
                        }
                        
-                       if (arg_count + optional_count != param_count)
-                               params_expanded_form = true;                    
+                       if (arg_count != param_count)
+                               params_expanded_form = true;    
                        
                        return 0;
                }
@@ -4056,9 +4121,11 @@ namespace Mono.CSharp {
                                                candidate_to_form = new PtrHashtable ();
                                        MethodBase candidate = Methods [i];
                                        candidate_to_form [candidate] = candidate;
-                               } else if (candidate_args != Arguments) {
+                               }
+                               
+                               if (candidate_args != Arguments) {
                                        if (candidates_expanded == null)
-                                               candidates_expanded = new Hashtable (4);
+                                               candidates_expanded = new Hashtable (2);
 
                                        candidates_expanded.Add (Methods [i], candidate_args);
                                        candidate_args = Arguments;
@@ -4241,6 +4308,15 @@ namespace Mono.CSharp {
                        best_candidate = (MethodBase) candidates [0];
                        method_params = candidate_to_form != null && candidate_to_form.Contains (best_candidate);
 
+                       //
+                       // TODO: Broken inverse order of candidates logic does not work with optional
+                       // parameters used for method overrides and I am not going to fix it for SRE
+                       //
+                       if (candidates_expanded != null && candidates_expanded.Contains (best_candidate)) {
+                               candidate_args = (Arguments) candidates_expanded [best_candidate];
+                               arg_count = candidate_args.Count;
+                       }
+
                        for (int ix = 1; ix < candidate_top; ix++) {
                                MethodBase candidate = (MethodBase) candidates [ix];
 
@@ -4249,7 +4325,7 @@ namespace Mono.CSharp {
 
                                bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
 
-                               if (BetterFunction (ec, Arguments, arg_count, 
+                               if (BetterFunction (ec, candidate_args, arg_count, 
                                        candidate, cand_params,
                                        best_candidate, method_params)) {
                                        best_candidate = candidate;
@@ -4268,7 +4344,7 @@ namespace Mono.CSharp {
                                        continue;
 
                                bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
-                               if (!BetterFunction (ec, Arguments, arg_count,
+                               if (!BetterFunction (ec, candidate_args, arg_count,
                                        best_candidate, method_params,
                                        candidate, cand_params)) 
                                {
@@ -4323,15 +4399,6 @@ namespace Mono.CSharp {
                                }
                        }
 
-                       //
-                       // TODO: Broken inverse order of candidates logic does not work with optional
-                       // parameters used for method overrides and I am not going to fix it for SRE
-                       //
-                       if (candidates_expanded != null && candidates_expanded.Contains (best_candidate)) {
-                               candidate_args = (Arguments) candidates_expanded [best_candidate];
-                               arg_count = candidate_args.Count;
-                       }
-
                        //
                        // And now check if the arguments are all
                        // compatible, perform conversions if
@@ -4376,6 +4443,7 @@ namespace Mono.CSharp {
                                                          bool may_fail, Location loc)
                {
                        AParametersCollection pd = TypeManager.GetParameterData (method);
+                       int param_count = GetApplicableParametersCount (method, pd);
 
                        int errors = Report.Errors;
                        Parameter.Modifier p_mod = 0;
@@ -4411,6 +4479,33 @@ namespace Mono.CSharp {
                                                break;
 
                                        continue;
+                               } else {
+                                       NamedArgument na = a as NamedArgument;
+                                       if (na != null) {
+                                               int name_index = pd.GetParameterIndexByName (na.Name.Value);
+                                               if (name_index < 0 || name_index >= param_count) {
+                                                       if (DeclaringType != null && TypeManager.IsDelegateType (DeclaringType)) {
+                                                               Report.SymbolRelatedToPreviousError (DeclaringType);
+                                                               Report.Error (1746, na.Name.Location,
+                                                                       "The delegate `{0}' does not contain a parameter named `{1}'",
+                                                                       TypeManager.CSharpName (DeclaringType), na.Name.Value);
+                                                       } else {
+                                                               Report.SymbolRelatedToPreviousError (best_candidate);
+                                                               Report.Error (1739, na.Name.Location,
+                                                                       "The best overloaded method match for `{0}' does not contain a parameter named `{1}'",
+                                                                       TypeManager.CSharpSignature (method), na.Name.Value);
+                                                       }
+                                               } else if (arguments[name_index] != a) {
+                                                       if (DeclaringType != null && TypeManager.IsDelegateType (DeclaringType))
+                                                               Report.SymbolRelatedToPreviousError (DeclaringType);
+                                                       else
+                                                               Report.SymbolRelatedToPreviousError (best_candidate);
+
+                                                       Report.Error (1744, na.Name.Location,
+                                                               "Named argument `{0}' cannot be used for a parameter which has positional argument specified",
+                                                               na.Name.Value);
+                                               }
+                                       }
                                }
 
                                if (delegate_type != null && !Delegate.IsTypeCovariant (a.Expr, pt))
@@ -4449,7 +4544,6 @@ namespace Mono.CSharp {
                        //
                        // Fill not provided arguments required by params modifier
                        //
-                       int param_count = GetApplicableParametersCount (method, pd);
                        if (params_initializers == null && pd.HasParams && arg_count + 1 == param_count) {
                                if (arguments == null)
                                        arguments = new Arguments (1);
index 45100ddabac57480c090878678750692997142b8..9e331bdd2c4c5767c0e32eb99be3326a7c99487b 100644 (file)
@@ -7539,6 +7539,9 @@ namespace Mono.CSharp {
                                return null;
                        }
 
+                       if (Arguments [0] is NamedArgument)
+                               Error_NamedArgument ((NamedArgument) Arguments[0]);
+
                        Expression p = new PointerArithmetic (Binary.Operator.Addition, Expr, Arguments [0].Expr, t, loc).Resolve (ec);
                        if (p == null)
                                return null;
@@ -7601,6 +7604,11 @@ namespace Mono.CSharp {
                        throw new Exception ("Should never be reached");
                }
 
+               public static void Error_NamedArgument (NamedArgument na)
+               {
+                       Report.Error (1742, na.Name.Location, "An element access expression cannot use named argument");
+               }
+
                public override string GetSignatureForError ()
                {
                        return Expr.GetSignatureForError ();
@@ -7676,6 +7684,9 @@ namespace Mono.CSharp {
                        }
 
                        foreach (Argument a in ea.Arguments) {
+                               if (a is NamedArgument)
+                                       ElementAccess.Error_NamedArgument ((NamedArgument) a);
+
                                a.Expr = ConvertExpressionToArrayIndex (ec, a.Expr);
                        }
                        
index e9b93efd6c724b42c93f5845e4f95498cbc19fe6..2c011b0dd80fb23f065af6962afbced1fcc52f91 100644 (file)
@@ -2219,6 +2219,8 @@ namespace Mono.CSharp {
                        Type method_parameter = null;
                        for (int i = 0; i < arg_count; i++) {
                                Argument a = arguments [i];
+                               if (a == null)
+                                       continue;
                                
                                if (i < params_arguments_start) {
                                        method_parameter = methodParameters.Types [i];