2007-08-14 Marek Safar <marek.safar@gmail.com>
authorMarek Safar <marek.safar@gmail.com>
Tue, 14 Aug 2007 15:42:24 +0000 (15:42 -0000)
committerMarek Safar <marek.safar@gmail.com>
Tue, 14 Aug 2007 15:42:24 +0000 (15:42 -0000)
** C# 3.0 Object and collection initializers (major re-write)

* assign.cs (DoResolve): Initializers are not assign related.

* codegen.cs (EmitContext.CurrentInitializerVariable): Holds a varible
used during collection or object initialization.

* expression.cs (Error_InvalidArguments): Add initializers specific
messages. More will come later because it requires some general
refactoring.
(New.DoResolve): Better error handling for unsafe types.
(EmptyExpressionStatement): New class.
(ElementInitializer): An object initializer expression.
(CollectionElementInitializer): A collection initializer expression.
(CollectionOrObjectInitializers): A block of object or collection
initializers.
(NewInitialize): New expression with element/object initializers.

* statement.cs: Reverted object/collection initializer hacks.

* typemanager.cs (CSharpName): Filter __arglist type.

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

mcs/mcs/ChangeLog
mcs/mcs/assign.cs
mcs/mcs/codegen.cs
mcs/mcs/expression.cs
mcs/mcs/statement.cs
mcs/mcs/typemanager.cs

index 888dfd95ff408b9152b8ec8db0ad7cef8a3c3640..897da83895da4e0568dcafe5ff424c814676b431 100644 (file)
@@ -1,3 +1,27 @@
+2007-08-14  Marek Safar  <marek.safar@gmail.com>
+
+       ** C# 3.0 Object and collection initializers (major re-write)
+       
+       * assign.cs (DoResolve): Initializers are not assign related.
+       
+       * codegen.cs (EmitContext.CurrentInitializerVariable): Holds a varible
+       used during collection or object initialization.
+       
+       * expression.cs (Error_InvalidArguments): Add initializers specific
+       messages. More will come later because it requires some general
+       refactoring.
+       (New.DoResolve): Better error handling for unsafe types.
+       (EmptyExpressionStatement): New class.
+       (ElementInitializer): An object initializer expression.
+       (CollectionElementInitializer): A collection initializer expression.
+       (CollectionOrObjectInitializers): A block of object or collection
+       initializers.
+       (NewInitialize): New expression with element/object initializers.
+       
+       * statement.cs: Reverted object/collection initializer hacks.
+       
+       * typemanager.cs (CSharpName): Filter __arglist type.
+       
 2007-08-09  Marek Safar  <marek.safar@gmail.com>
 
        ** C# 3.0 Anonymous Types (update to the latest standard)
index 4e0c493754c5fd1c052b10e3614bab811f868e30..8bfba9d69d79f25b3da2e7a7f8bd9b27be0bb3cc 100644 (file)
@@ -366,11 +366,6 @@ namespace Mono.CSharp {
                        if (target == null)
                                return null;
                        
-                       // Handle initializations e.g. Person p = new Person () { Name = "Scott" };
-                       IInitializable initializer = source as IInitializable;
-                       if (initializer != null && !initializer.Initialize (ec, target))
-                               return null;
-
                        bool same_assignment = (embedded != null) ? embedded.Target.Equals(target) : source.Equals (target);
                        if (same_assignment) {
                                Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
index 355a4ef70733465d5a6a773e19fa33ed111cc045..56a2667c3e1312e94cc64f3ed34681fab76940fe 100644 (file)
@@ -220,6 +220,11 @@ namespace Mono.CSharp {
        /// </summary>
        public class EmitContext : IResolveContext {
 
+               //
+               // Holds a varible used during collection or object initialization.
+               //
+               public Expression CurrentInitializerVariable;
+
                DeclSpace decl_space;
                
                public DeclSpace TypeContainer;
@@ -685,6 +690,9 @@ namespace Mono.CSharp {
                        bool unreachable;
                        
                        if (ResolveTopBlock (null, block, md.ParameterInfo, md, out unreachable)){
+                               if (Report.Errors > 0)
+                                       return;
+
                                EmitMeta (block);
 
                                current_phase = Phase.Emitting;
index ed84394d381d69b860c7d44bbbdb19fb2673fbd0..03eb10eb6d8646a61f4dc487fd64279d51cc0f9b 100644 (file)
@@ -4286,10 +4286,20 @@ namespace Mono.CSharp {
                static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
                                                     Type delegate_type, Argument a, ParameterData expected_par)
                {
-                       if (delegate_type == null) 
+                       if (a is CollectionElementInitializer.ElementInitializerArgument) {
+                               Report.SymbolRelatedToPreviousError (method);
+                               if ((expected_par.ParameterModifier (idx) & Parameter.Modifier.ISBYREF) != 0) {
+                                       Report.Error (1954, loc, "The best overloaded collection initalizer method `{0}' cannot have 'ref', or `out' modifier",
+                                               TypeManager.CSharpSignature (method));
+                                       return;
+                               }
+                               Report.Error (1950, loc, "The best overloaded collection initalizer method `{0}' has some invalid arguments",
+                                         TypeManager.CSharpSignature (method));
+                       } else if (delegate_type == null) {
+                               Report.SymbolRelatedToPreviousError (method);
                                Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
-                                             TypeManager.CSharpSignature (method));
-                       else
+                                                 TypeManager.CSharpSignature (method));
+                       else
                                Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
                                        TypeManager.CSharpName (delegate_type));
 
@@ -5119,6 +5129,12 @@ namespace Mono.CSharp {
                                return null;
                        }
 
+                       if (type.IsPointer) {
+                               Report.Error (1919, loc, "Unsafe type `{0}' cannot be used in an object creation expression",
+                                       TypeManager.CSharpName (type));
+                               return null;
+                       }
+
                        if (Arguments == null) {
                                Expression c = Constantify (type);
                                if (c != null)
@@ -8199,6 +8215,36 @@ namespace Mono.CSharp {
                        type = t;
                }
        }
+       
+       //
+       // Empty statement expression
+       //
+       public sealed class EmptyExpressionStatement : ExpressionStatement
+       {
+               public static readonly EmptyExpressionStatement Instance = new EmptyExpressionStatement ();
+
+               private EmptyExpressionStatement ()
+               {
+                       type = TypeManager.object_type;
+                       eclass = ExprClass.Value;
+                       loc = Location.Null;
+               }
+
+               public override void EmitStatement (EmitContext ec)
+               {
+                       // Do nothing
+               }
+
+               public override Expression DoResolve (EmitContext ec)
+               {
+                       return this;
+               }
+
+               public override void Emit (EmitContext ec)
+               {
+                       // Do nothing
+               }
+       }       
 
        public class UserCast : Expression {
                MethodBase method;
@@ -8502,137 +8548,262 @@ namespace Mono.CSharp {
                        target.t = t.Clone (clonectx);
                }
        }
-       
-       public interface IInitializable
+
+       //
+       // An object initializer expression
+       //
+       public class ElementInitializer : Expression
        {
-               bool Initialize (EmitContext ec, Expression target);
+               Expression initializer;
+               public readonly string Name;
+
+               public ElementInitializer (string name, Expression initializer, Location loc)
+               {
+                       this.Name = name;
+                       this.initializer = initializer;
+                       this.loc = loc;
+               }
+
+               protected override void CloneTo (CloneContext clonectx, Expression t)
+               {
+                       if (initializer == null)
+                               return;
+                       
+                       ElementInitializer target = (ElementInitializer) t;
+                       target.initializer = initializer.Clone (clonectx);
+               }
+
+               public override Expression DoResolve (EmitContext ec)
+               {
+                       if (initializer == null)
+                               return EmptyExpressionStatement.Instance;
+                       
+                       MemberExpr element_member = MemberLookupFinal (ec, ec.CurrentInitializerVariable.Type, ec.CurrentInitializerVariable.Type,
+                               Name, MemberTypes.Field | MemberTypes.Property, BindingFlags.Public | BindingFlags.Instance, loc) as MemberExpr;
+
+                       if (element_member == null)
+                               return null;
+
+                       element_member.InstanceExpression = ec.CurrentInitializerVariable;
+
+                       if (initializer is CollectionOrObjectInitializers) {
+                               Expression previous = ec.CurrentInitializerVariable;
+                               ec.CurrentInitializerVariable = element_member;
+                               initializer = initializer.Resolve (ec);
+                               ec.CurrentInitializerVariable = previous;
+                               return initializer;
+                       }
+
+                       return new Assign (element_member, initializer, loc).Resolve (ec);
+               }
+
+               public override void Emit (EmitContext ec)
+               {
+                       throw new NotSupportedException ("Should not be reached");
+               }
        }
        
-       public class Initializer
+       //
+       // A collection initializer expression
+       //
+       public class CollectionElementInitializer : Expression
        {
-               public readonly string Name;
-               public readonly object Value;
+               public class ElementInitializerArgument : Argument
+               {
+                       public ElementInitializerArgument (Expression e)
+                               : base (e)
+                       {
+                       }
+               }
+
+               ArrayList arguments;
 
-               public Initializer (string name, Expression value)
+               public CollectionElementInitializer (Expression argument)
                {
-                       Name = name;
-                       Value = value;
+                       arguments = new ArrayList (1);
+                       arguments.Add (argument);
+                       this.loc = argument.Location;
                }
 
-               public Initializer (string name, IInitializable value)
+               public CollectionElementInitializer (ArrayList arguments, Location loc)
                {
-                       Name = name;
-                       Value = value;
+                       this.arguments = arguments;
+                       this.loc = loc;
+               }
+
+               protected override void CloneTo (CloneContext clonectx, Expression t)
+               {
+                       CollectionElementInitializer target = (CollectionElementInitializer) t;
+                       ArrayList t_arguments = target.arguments = new ArrayList (arguments.Count);
+                       foreach (Expression e in arguments)
+                               t_arguments.Add (e.Clone (clonectx));
+               }
+
+               public override Expression DoResolve (EmitContext ec)
+               {
+                       // TODO: We should call a constructor which takes element counts argument,
+                       // for know types like List<T>, Dictionary<T, U>
+                       
+                       for (int i = 0; i < arguments.Count; ++i)
+                               arguments [i] = new ElementInitializerArgument ((Expression)arguments [i]);
+
+                       Expression add_method = new Invocation (
+                               new MemberAccess (ec.CurrentInitializerVariable, "Add", loc),
+                               arguments);
+
+                       add_method = add_method.Resolve (ec);
+
+                       return add_method;
+               }
+               
+               public override void Emit (EmitContext ec)
+               {
+                       throw new NotSupportedException ("Should not be reached");
                }
        }
        
-       public class ObjectInitializer : IInitializable
+       //
+       // A block of object or collection initializers
+       //
+       public class CollectionOrObjectInitializers : ExpressionStatement
        {
-               readonly ArrayList initializers;
-               public ObjectInitializer (ArrayList initializers)
+               ArrayList initializers;
+               
+               public static readonly CollectionOrObjectInitializers Empty = 
+                       new CollectionOrObjectInitializers (new ArrayList (0), Location.Null);
+
+               public CollectionOrObjectInitializers (ArrayList initializers, Location loc)
                {
                        this.initializers = initializers;
+                       this.loc = loc;
                }
                
-               public bool Initialize (EmitContext ec, Expression target)
-               {
-                       ArrayList initialized = new ArrayList (initializers.Count);
-                       for (int i = initializers.Count - 1; i >= 0; i--) {
-                               Initializer initializer = initializers[i] as Initializer;
-                               if (initialized.Contains (initializer.Name)) {
-                                       //FIXME proper error
-                                       Console.WriteLine ("Object member can only be initialized once");
-                                       return false;
-                               }
-                               
-                               MemberAccess ma = new MemberAccess (target, initializer.Name);
-                               Expression expr = initializer.Value as Expression;
-                               // If it's an expresison, append the assign.
-                               if (expr != null) {
-                                       Assign a = new Assign (ma, expr);
-                                       ec.CurrentBlock.InsertStatementAfterCurrent (new StatementExpression (a));
-                               }
-                               // If it's another initializer (object or collection), initialize it.
-                               else if (!((IInitializable)initializer.Value).Initialize (ec, ma))
-                                                       return false;
-                               
-                               initialized.Add (initializer.Name);
+               public bool IsEmpty {
+                       get {
+                               return initializers.Count == 0;
                        }
-                       return true;
                }
-       }
-       
-       public class CollectionInitializer : IInitializable
-       {
-               readonly ArrayList items;
-               public CollectionInitializer (ArrayList items)
+
+               protected override void CloneTo (CloneContext clonectx, Expression target)
                {
-                       this.items = items;
+                       CollectionOrObjectInitializers t = (CollectionOrObjectInitializers) target;
+
+                       t.initializers = new ArrayList (initializers.Count);
+                       foreach (Expression e in initializers)
+                               t.initializers.Add (e.Clone (clonectx));
                }
                
-               bool CheckCollection (EmitContext ec, Expression e)
+               public override Expression DoResolve (EmitContext ec)
                {
-                       if (e == null || e.Type == null)
-                               return false;
-                       bool is_ienumerable = false;
-                       foreach (Type t in TypeManager.GetInterfaces (e.Type))
-                               if (t == typeof (IEnumerable)) {
-                                       is_ienumerable = true;
-                                       break;
+                       bool is_elements_initialization = false;
+                       ArrayList element_names = null;
+                       for (int i = 0; i < initializers.Count; ++i) {
+                               Expression initializer = (Expression) initializers [i];
+                               ElementInitializer element_initializer = initializer as ElementInitializer;
+
+                               if (i == 0) {
+                                       if (element_initializer != null) {
+                                               is_elements_initialization = true;
+                                               element_names = new ArrayList (initializers.Count);
+                                               element_names.Add (element_initializer.Name);
+                                       } else {
+                                               if (!TypeManager.ImplementsInterface (ec.CurrentInitializerVariable.Type,
+                                                       TypeManager.ienumerable_type)) {
+                                                       Report.Error (1922, loc, "A field or property `{0}' cannot be initialized with a collection " +
+                                                               "object initializer because type `{1}' does not implement `{2}' interface",
+                                                               ec.CurrentInitializerVariable.GetSignatureForError (),
+                                                               TypeManager.CSharpName (ec.CurrentInitializerVariable.Type),
+                                                               TypeManager.CSharpName (TypeManager.ienumerable_type));
+                                                       return null;
+                                               }
+                                       }
+                               } else {
+                                       if (is_elements_initialization == (element_initializer == null)) {
+                                               Report.Error (747, initializer.Location, "Inconsistent `{0}' member declaration",
+                                                       is_elements_initialization ? "object initializer" : "collection initializer");
+                                               continue;
+                                       }
+                                       
+                                       if (is_elements_initialization) {
+                                               if (element_names.Contains (element_initializer.Name)) {
+                                                       Report.Error (1912, element_initializer.Location,
+                                                               "An object initializer includes more than one member `{0}' initialization",
+                                                               element_initializer.Name);
+                                               } else {
+                                                       element_names.Add (element_initializer.Name);
+                                               }
+                                       }
                                }
-                       
-                       if (!is_ienumerable)
-                               return false;
-                       
-                       MethodGroupExpr mg = Expression.MemberLookup (
-                               ec.ContainerType, e.Type, "Add", MemberTypes.Method,
-                               Expression.AllBindingFlags, Location.Null) as MethodGroupExpr;
 
-                       if (mg == null)
-                                       return false;
-                       
-                       foreach (MethodInfo mi in mg.Methods) {
-                               if (TypeManager.GetParameterData (mi).Count != 1)
-                                       continue;
-                               if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
-                                       continue;
-                               return true;
+                               initializers [i] = initializer.Resolve (ec);
                        }
-                       return false;
+
+                       type = typeof (CollectionOrObjectInitializers);
+                       eclass = ExprClass.Variable;
+                       return this;
                }
-               
-               public bool Initialize (EmitContext ec, Expression target)
+
+               public override void Emit (EmitContext ec)
                {
-                       if (!CheckCollection (ec, target.Resolve (ec))) {
-                               // FIXME throw proper error
-                               Console.WriteLine ("Error: This is not a collection");
-                               return false;
-                       }
-                       
-                       for (int i = items.Count - 1; i >= 0; i--) {
-                               MemberAccess ma = new MemberAccess (target, "Add");
-                               ArrayList array = new ArrayList ();
-                               array.Add (new Argument ((Expression)items[i]));
-                               Invocation invoke = new Invocation (ma, array);
-                               ec.CurrentBlock.InsertStatementAfterCurrent (new StatementExpression (invoke));
-                       }
-                       return true;
+                       EmitStatement (ec);
+               }
+
+               public override void EmitStatement (EmitContext ec)
+               {
+                       foreach (ExpressionStatement e in initializers)
+                               e.EmitStatement (ec);
                }
        }
        
-       public class NewInitialize : New, IInitializable
+       //
+       // New expression with element/object initializers
+       //
+       public class NewInitialize : New
        {
-               IInitializable initializer;
+               CollectionOrObjectInitializers initializers;
+               TemporaryVariable type_instance;
 
-               public bool Initialize (EmitContext ec, Expression target)
+               public NewInitialize (Expression requested_type, ArrayList arguments, CollectionOrObjectInitializers initializers, Location l)
+                       : base (requested_type, arguments, l)
                {
-                       return initializer.Initialize (ec, target);
+                       this.initializers = initializers;
                }
 
-               public NewInitialize (Expression requested_type, ArrayList arguments, IInitializable initializer, Location l)
-                       : base (requested_type, arguments, l)
+               protected override void CloneTo (CloneContext clonectx, Expression t)
                {
-                       this.initializer = initializer;
+                       base.CloneTo (clonectx, t);
+
+                       NewInitialize target = (NewInitialize) t;
+                       target.initializers = (CollectionOrObjectInitializers)initializers.Clone (clonectx);
+               }
+
+               public override Expression DoResolve (EmitContext ec)
+               {
+                       Expression e = base.DoResolve (ec);
+                       if (type == null)
+                               return null;
+
+                       // Empty initializer can be optimized to simple new
+                       if (initializers.IsEmpty)
+                               return e;
+
+                       type_instance = new TemporaryVariable (type, loc);
+                       type_instance = (TemporaryVariable)type_instance.Resolve (ec);
+
+                       Expression previous = ec.CurrentInitializerVariable;
+                       ec.CurrentInitializerVariable = type_instance;
+                       initializers.Resolve (ec);
+                       ec.CurrentInitializerVariable = previous;
+                       return this;
+               }
+
+               public override void Emit (EmitContext ec)
+               {
+                       base.Emit (ec);
+
+                       type_instance.EmitStore (ec);
+                       initializers.Emit (ec);
+                       type_instance.Emit (ec);
                }
        }
 
index 6519a0094a941c7b7e8662e12b8d66cac08e6af1..83ee638cc010245b80b227eabec3001c54ff2d80 100644 (file)
@@ -1485,7 +1485,6 @@ namespace Mono.CSharp {
                // The statements in this block
                //
                protected ArrayList statements;
-               protected int current_statement;
                int num_statements;
 
                //
@@ -1865,12 +1864,6 @@ namespace Mono.CSharp {
                        statements.Add (s);
                        flags |= Flags.BlockUsed;
                }
-               
-               public void InsertStatementAfterCurrent (Statement statement)
-               {
-                       statements.Insert (current_statement + 1, statement);
-                       flags |= Flags.BlockUsed;
-               }
 
                public bool Used {
                        get { return (flags & Flags.BlockUsed) != 0; }
@@ -2111,12 +2104,13 @@ namespace Mono.CSharp {
                        // from the beginning of the function.  The outer Resolve() that detected the unreachability is
                        // responsible for handling the situation.
                        //
-                       for (current_statement = 0; current_statement < statements.Count; current_statement++) {
-                               Statement s = (Statement) statements [current_statement];
+                       int statement_count = statements.Count;
+                       for (int ix = 0; ix < statement_count; ix++){
+                               Statement s = (Statement) statements [ix];
                                // Check possible empty statement (CS0642)
                                if (Report.WarningLevel >= 3 &&
-                                       current_statement + 1 < statements.Count &&
-                                               statements [current_statement + 1] is Block)
+                                       ix + 1 < statement_count &&
+                                               statements [ix + 1] is Block)
                                        CheckPossibleMistakenEmptyStatement (s);
 
                                //
@@ -2148,14 +2142,14 @@ namespace Mono.CSharp {
                                                return false;
 
                                        ok = false;
-                                       statements [current_statement] = EmptyStatement.Value;
+                                       statements [ix] = EmptyStatement.Value;
                                        continue;
                                }
 
                                if (unreachable && !(s is LabeledStatement) && !(s is Block))
-                                       statements [current_statement] = EmptyStatement.Value;
+                                       statements [ix] = EmptyStatement.Value;
 
-                               num_statements = current_statement + 1;
+                               num_statements = ix + 1;
 
                                unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
                                if (unreachable && s is LabeledStatement)
@@ -2163,7 +2157,7 @@ namespace Mono.CSharp {
                        }
 
                        Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
-                                     ec.CurrentBranching, statements.Count, num_statements);
+                                     ec.CurrentBranching, statement_count, num_statements);
 
                        if (!ok)
                                return false;
@@ -5112,7 +5106,7 @@ namespace Mono.CSharp {
                                        TypeManager.CSharpName (expr.Type));
                        }
 
-                       public static bool IsOverride (MethodInfo m)
+                       bool IsOverride (MethodInfo m)
                        {
                                m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
 
index b5f710c40f7a8a00e574dcb6b8babab336a49d06..928e343e9e08a13aa514d3ea4d8ade703bc4ad7d 100644 (file)
@@ -622,6 +622,9 @@ namespace Mono.CSharp {
                if (t == typeof(NullType))
                        return "null";
 
+               if (t == typeof (ArglistParameter))
+                       return "__arglist";
+
                return CSharpName (GetFullName (t));
     }