X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fgmcs%2Fstatement.cs;h=5fe7e02470cb4aaf593ddac59a3412f867db8158;hb=bd316288ae3fa0cb6f03b367716d04d5244c5d04;hp=11d9149f30cb07e3bf4eb5264224a6455e225569;hpb=5854de44376caef66925c72a3f6631c248964842;p=mono.git diff --git a/mcs/gmcs/statement.cs b/mcs/gmcs/statement.cs old mode 100755 new mode 100644 index 11d9149f30c..5fe7e02470c --- a/mcs/gmcs/statement.cs +++ b/mcs/gmcs/statement.cs @@ -3,7 +3,7 @@ // // Author: // Miguel de Icaza (miguel@ximian.com) -// Martin Baulig (martin@gnome.org) +// Martin Baulig (martin@ximian.com) // // (C) 2001, 2002, 2003 Ximian, Inc. // (C) 2003, 2004 Novell, Inc. @@ -14,10 +14,10 @@ using System.Text; using System.Reflection; using System.Reflection.Emit; using System.Diagnostics; +using System.Collections; +using System.Collections.Specialized; namespace Mono.CSharp { - - using System.Collections; public abstract class Statement { public Location loc; @@ -46,16 +46,14 @@ namespace Mono.CSharp { // in unreachable code, for instance. // + if (warn && (RootContext.WarningLevel >= 2)) + Report.Warning (162, loc, "Unreachable code detected"); + ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc); bool ok = Resolve (ec); ec.KillFlowBranching (); - if (!ok) - return false; - - if (warn && (RootContext.WarningLevel >= 2)) - Report.Warning (162, loc, "Unreachable code detected"); - return true; + return ok; } protected void CheckObsolete (Type type) @@ -82,7 +80,7 @@ namespace Mono.CSharp { public void Error (int error, string s) { - if (!Location.IsNull (loc)) + if (!loc.IsNull) Report.Error (error, loc, s); else Report.Error (error, s); @@ -141,13 +139,21 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { + bool ok = true; + Report.Debug (1, "START IF BLOCK", loc); expr = Expression.ResolveBoolean (ec, expr, loc); if (expr == null){ - return false; + ok = false; + goto skip; } + Assign ass = expr as Assign; + if (ass != null && ass.Source is Constant) { + Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?"); + } + // // Dead code elimination // @@ -169,22 +175,22 @@ namespace Mono.CSharp { if ((FalseStatement != null) && !FalseStatement.Resolve (ec)) - return false; - } + return false; + } return true; } - + skip: ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc); - bool ok = TrueStatement.Resolve (ec); + ok &= TrueStatement.Resolve (ec); is_true_ret = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable; ec.CurrentBranching.CreateSibling (); - if ((FalseStatement != null) && !FalseStatement.Resolve (ec)) - ok = false; + if (FalseStatement != null) + ok &= FalseStatement.Resolve (ec); ec.EndFlowBranching (); @@ -343,12 +349,14 @@ namespace Mono.CSharp { } ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc); + if (!infinite) + ec.CurrentBranching.CreateSibling (); if (!Statement.Resolve (ec)) ok = false; - ec.CurrentBranching.Infinite = infinite; - ec.EndFlowBranching (); + ec.CurrentBranching.Infinite = infinite; + ec.EndFlowBranching (); return ok; } @@ -402,7 +410,7 @@ namespace Mono.CSharp { Expression Test; readonly Statement InitStatement; readonly Statement Increment; - readonly Statement Statement; + public readonly Statement Statement; bool infinite, empty; public For (Statement initStatement, @@ -519,15 +527,16 @@ namespace Mono.CSharp { public class StatementExpression : Statement { ExpressionStatement expr; - public StatementExpression (ExpressionStatement expr, Location l) + public StatementExpression (ExpressionStatement expr) { this.expr = expr; - loc = l; + loc = expr.Location; } public override bool Resolve (EmitContext ec) { - expr = expr.ResolveStatement (ec); + if (expr != null) + expr = expr.ResolveStatement (ec); return expr != null; } @@ -558,19 +567,26 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { + AnonymousContainer am = ec.CurrentAnonymousMethod; + if ((am != null) && am.IsIterator && ec.InIterator) { + Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " + + "statement to return a value, or yield break to end the iteration"); + return false; + } + if (ec.ReturnType == null){ - if (Expr != null){ + if (Expr != null){ if (ec.CurrentAnonymousMethod != null){ - Report.Error (1662, loc, String.Format ( - "Anonymous method could not be converted to delegate " + - "since the return value does not match the delegate value")); + Report.Error (1662, loc, + "Cannot convert anonymous method block to delegate type `{0}' because some of the return types in the block are not implicitly convertible to the delegate return type", + ec.CurrentAnonymousMethod.GetSignatureForError ()); } - Error (127, "Return with a value not allowed here"); + Error (127, "A return keyword must not be followed by any expression when method returns void"); return false; } } else { if (Expr == null){ - Error (126, "An object of type `{0}' is expected " + + Error (126, "An object of a type convertible to `{0}' is required " + "for the return statement", TypeManager.CSharpName (ec.ReturnType)); return false; @@ -588,18 +604,13 @@ namespace Mono.CSharp { } } - if (ec.InIterator){ - Error (-206, "Return statement not allowed inside iterators"); - return false; - } - FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector; if (ec.CurrentBranching.InTryOrCatch (true)) { ec.CurrentBranching.AddFinallyVector (vector); in_exc = true; - } else if (ec.CurrentBranching.InFinally (true)) { - Error (157, "Control can not leave the body of the finally block"); + } else if (ec.InFinally) { + Error (157, "Control cannot leave the body of a finally clause"); return false; } else vector.CheckOutParameters (ec.CurrentBranching); @@ -629,7 +640,6 @@ namespace Mono.CSharp { public class Goto : Statement { string target; - Block block; LabeledStatement label; public override bool Resolve (EmitContext ec) @@ -643,13 +653,13 @@ namespace Mono.CSharp { label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector); ec.CurrentBranching.CurrentUsageVector.Goto (); + label.AddReference (); return true; } - public Goto (Block parent_block, string label, Location l) + public Goto (string label, Location l) { - block = parent_block; loc = l; target = label; } @@ -668,22 +678,23 @@ namespace Mono.CSharp { } public class LabeledStatement : Statement { - public readonly Location Location; bool defined; bool referenced; Label label; + ILGenerator ig; FlowBranching.UsageVector vectors; public LabeledStatement (string label_name, Location l) { - this.Location = l; + this.loc = l; } public Label LabelTarget (EmitContext ec) { if (defined) return label; + ig = ec.ig; label = ec.ig.DefineLabel (); defined = true; @@ -713,16 +724,24 @@ namespace Mono.CSharp { { ec.CurrentBranching.Label (vectors); - referenced = true; - return true; } protected override void DoEmit (EmitContext ec) { + if (ig != null && ig != ec.ig) { + // TODO: location is wrong + Report.Error (1632, loc, "Control cannot leave the body of an anonymous method"); + return; + } LabelTarget (ec); ec.ig.MarkLabel (label); } + + public void AddReference () + { + referenced = true; + } } @@ -745,12 +764,12 @@ namespace Mono.CSharp { protected override void DoEmit (EmitContext ec) { if (ec.Switch == null){ - Report.Error (153, loc, "goto default is only valid in a switch statement"); + Report.Error (153, loc, "A goto case is only valid inside a switch statement"); return; } if (!ec.Switch.GotDefault){ - Report.Error (159, loc, "No default target on switch statement"); + Report.Error (159, loc, "No such label `default:' within the scope of the goto statement"); return; } ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget); @@ -773,7 +792,7 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { if (ec.Switch == null){ - Report.Error (153, loc, "goto case is only valid in a switch statement"); + Report.Error (153, loc, "A goto case is only valid inside a switch statement"); return false; } @@ -781,23 +800,24 @@ namespace Mono.CSharp { if (expr == null) return false; - if (!(expr is Constant)){ - Report.Error (159, loc, "Target expression for goto case is not constant"); + Constant c = expr as Constant; + if (c == null) { + Error (150, "A constant value is expected"); return false; } - object val = Expression.ConvertIntLiteral ( - (Constant) expr, ec.Switch.SwitchType, loc); + c = c.ToType (ec.Switch.SwitchType, loc); + if (c == null) + return false; + object val = c.GetValue (); if (val == null) - return false; + val = c; sl = (SwitchLabel) ec.Switch.Elements [val]; if (sl == null){ - Report.Error ( - 159, loc, - "No such label 'case " + val + "': for the goto case"); + Report.Error (159, loc, "No such label `case {0}:' within the scope of the goto statement", c.GetValue () == null ? "null" : val); return false; } @@ -822,7 +842,6 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { - bool in_catch = ec.CurrentBranching.InCatch (); ec.CurrentBranching.CurrentUsageVector.Throw (); if (expr != null){ @@ -833,28 +852,33 @@ namespace Mono.CSharp { ExprClass eclass = expr.eclass; if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess || - eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) { - expr.Error_UnexpectedKind ("value, variable, property or indexer access ", loc); + eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) { + expr.Error_UnexpectedKind (ec, "value, variable, property or indexer access ", loc); return false; } Type t = expr.Type; - + if ((t != TypeManager.exception_type) && - !t.IsSubclassOf (TypeManager.exception_type) && + !TypeManager.IsSubclassOf (t, TypeManager.exception_type) && !(expr is NullLiteral)) { Error (155, - "The type caught or thrown must be derived " + - "from System.Exception"); + "The type caught or thrown must be derived " + + "from System.Exception"); return false; } - } else if (!in_catch) { - Error (156, - "A throw statement with no argument is only " + - "allowed in a catch clause"); + return true; + } + + if (!ec.InCatch) { + Error (156, "A throw statement with no arguments is not allowed outside of a catch clause"); return false; } + if (ec.InFinally) { + Error (724, "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause"); + return false; + } return true; } @@ -882,15 +906,15 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){ - Error (139, "No enclosing loop or switch to continue to"); + Error (139, "No enclosing loop out of which to break or continue"); return false; - } else if (ec.CurrentBranching.InFinally (false)) { - Error (157, "Control can not leave the body of the finally block"); + } else if (ec.InFinally && ec.CurrentBranching.BreakCrossesTryCatchBoundary()) { + Error (157, "Control cannot leave the body of a finally clause"); return false; } else if (ec.CurrentBranching.InTryOrCatch (false)) ec.CurrentBranching.AddFinallyVector ( ec.CurrentBranching.CurrentUsageVector); - else if (ec.CurrentBranching.InLoop ()) + else if (ec.CurrentBranching.InLoop () || ec.CurrentBranching.InSwitch ()) ec.CurrentBranching.AddBreakVector ( ec.CurrentBranching.CurrentUsageVector); @@ -926,11 +950,11 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { - if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){ - Error (139, "No enclosing loop to continue to"); + if (!ec.CurrentBranching.InLoop ()){ + Error (139, "No enclosing loop out of which to break or continue"); return false; - } else if (ec.CurrentBranching.InFinally (false)) { - Error (157, "Control can not leave the body of the finally block"); + } else if (ec.InFinally) { + Error (157, "Control cannot leave the body of a finally clause"); return false; } else if (ec.CurrentBranching.InTryOrCatch (false)) ec.CurrentBranching.AddFinallyVector (ec.CurrentBranching.CurrentUsageVector); @@ -982,11 +1006,19 @@ namespace Mono.CSharp { Pinned = 4, IsThis = 8, Captured = 16, - AddressTaken = 32 + AddressTaken = 32, + CompilerGenerated = 64 } - Flags flags; + public enum ReadOnlyContext: byte { + Using, + Foreach, + Fixed + } + Flags flags; + ReadOnlyContext ro_context; + public LocalInfo (Expression type, string name, Block block, Location l) { Type = type; @@ -1038,28 +1070,16 @@ namespace Mono.CSharp { } if (VariableType.IsAbstract && VariableType.IsSealed) { - Report.Error (723, Location, "Cannot declare variable of static type '{0}'", TypeManager.CSharpName (VariableType)); + Report.Error (723, Location, "Cannot declare variable of static type `{0}'", TypeManager.CSharpName (VariableType)); return false; } -// TODO: breaks the build -// if (VariableType.IsPointer && !ec.InUnsafe) -// Expression.UnsafeError (Location); + + if (VariableType.IsPointer && !ec.InUnsafe) + Expression.UnsafeError (Location); return true; } - // - // Whether the variable is Fixed (because its Pinned or its a value type) - // - public bool IsFixed { - get { - if (((flags & Flags.Pinned) != 0) || TypeManager.IsValueType (VariableType)) - return true; - - return false; - } - } - public bool IsCaptured { get { return (flags & Flags.Captured) != 0; @@ -1080,6 +1100,16 @@ namespace Mono.CSharp { } } + public bool CompilerGenerated { + get { + return (flags & Flags.CompilerGenerated) != 0; + } + + set { + flags |= Flags.CompilerGenerated; + } + } + public override string ToString () { return String.Format ("LocalInfo ({0},{1},{2},{3})", @@ -1099,9 +1129,28 @@ namespace Mono.CSharp { get { return (flags & Flags.ReadOnly) != 0; } - set { - flags = value ? (flags | Flags.ReadOnly) : (unchecked (flags & ~Flags.ReadOnly)); + } + + public void SetReadOnlyContext (ReadOnlyContext context) + { + flags |= Flags.ReadOnly; + ro_context = context; + } + + public string GetReadOnlyContext () + { + if (!ReadOnly) + throw new InternalErrorException ("Variable is not readonly"); + + switch (ro_context) { + case ReadOnlyContext.Fixed: + return "fixed variable"; + case ReadOnlyContext.Foreach: + return "foreach iteration variable"; + case ReadOnlyContext.Using: + return "using variable"; } + throw new NotImplementedException (); } // @@ -1146,54 +1195,34 @@ namespace Mono.CSharp { public readonly Location StartLocation; public Location EndLocation = Location.Null; + public readonly ToplevelBlock Toplevel; + [Flags] - public enum Flags { + public enum Flags : ushort { Implicit = 1, Unchecked = 2, BlockUsed = 4, VariablesInitialized = 8, HasRet = 16, IsDestructor = 32, - HasVarargs = 64, - IsToplevel = 128, - Unsafe = 256 + IsToplevel = 64, + Unsafe = 128, + HasVarargs = 256 // Used in ToplevelBlock } - Flags flags; + protected Flags flags; public bool Implicit { - get { - return (flags & Flags.Implicit) != 0; - } + get { return (flags & Flags.Implicit) != 0; } } public bool Unchecked { - get { - return (flags & Flags.Unchecked) != 0; - } - set { - flags |= Flags.Unchecked; - } + get { return (flags & Flags.Unchecked) != 0; } + set { flags |= Flags.Unchecked; } } public bool Unsafe { - get { - return (flags & Flags.Unsafe) != 0; - } - set { - flags |= Flags.Unsafe; - } - } - - public bool HasVarargs { - get { - if (Parent != null) - return Parent.HasVarargs; - else - return (flags & Flags.HasVarargs) != 0; - } - set { - flags |= Flags.HasVarargs; - } + get { return (flags & Flags.Unsafe) != 0; } + set { flags |= Flags.Unsafe; } } // @@ -1210,7 +1239,7 @@ namespace Mono.CSharp { // statements. // ArrayList children; - + // // Labels. (label, block) pairs. // @@ -1226,9 +1255,9 @@ namespace Mono.CSharp { Hashtable constants; // - // The parameters for the block, this is only needed on the toplevel block really - // TODO: move `parameters' into ToplevelBlock - Parameters parameters; + // Temporary variables. + // + ArrayList temporary_variables; // // If this is a switch section, the enclosing switch block. @@ -1247,42 +1276,33 @@ namespace Mono.CSharp { : this (parent, flags, Location.Null, Location.Null) { } - public Block (Block parent, Flags flags, Parameters parameters) - : this (parent, flags, parameters, Location.Null, Location.Null) - { } - public Block (Block parent, Location start, Location end) : this (parent, (Flags) 0, start, end) { } - public Block (Block parent, Parameters parameters, Location start, Location end) - : this (parent, (Flags) 0, parameters, start, end) - { } - public Block (Block parent, Flags flags, Location start, Location end) - : this (parent, flags, Parameters.EmptyReadOnlyParameters, start, end) - { } - - public Block (Block parent, Flags flags, Parameters parameters, - Location start, Location end) { if (parent != null) parent.AddChild (this); this.Parent = parent; this.flags = flags; - this.parameters = parameters; this.StartLocation = start; this.EndLocation = end; this.loc = start; this_id = id++; statements = new ArrayList (); + if ((flags & Flags.IsToplevel) != 0) + Toplevel = (ToplevelBlock) this; + else + Toplevel = parent.Toplevel; + if (parent != null && Implicit) { - if (parent.child_variable_names == null) - parent.child_variable_names = new Hashtable(); + if (parent.known_variables == null) + parent.known_variables = new Hashtable (); // share with parent - child_variable_names = parent.child_variable_names; + known_variables = parent.known_variables; } } @@ -1295,8 +1315,14 @@ namespace Mono.CSharp { } public int ID { + get { return this_id; } + } + + protected Hashtable Variables { get { - return this_id; + if (variables == null) + variables = new Hashtable (); + return variables; } } @@ -1331,7 +1357,7 @@ namespace Mono.CSharp { while (cur != null) { if (cur.DoLookupLabel (name) != null) { Report.Error ( - 140, loc, "The label '{0}' is a duplicate", + 140, loc, "The label `{0}' is a duplicate", name); return false; } @@ -1346,8 +1372,8 @@ namespace Mono.CSharp { if (cur.DoLookupLabel (name) != null) { Report.Error ( 158, loc, - "The label '{0}' shadows another label " + - "by the same name in a containing scope.", + "The label `{0}' shadows another label " + + "by the same name in a contained scope.", name); return false; } @@ -1359,10 +1385,10 @@ namespace Mono.CSharp { continue; Report.Error ( - 158, s.Location, - "The label '{0}' shadows another " + + 158, s.loc, + "The label `{0}' shadows another " + "label by the same name in a " + - "containing scope.", + "contained scope.", name); return false; } @@ -1412,136 +1438,134 @@ namespace Mono.CSharp { return null; } - LocalInfo this_variable = null; - - // - // Returns the "this" instance variable of this block. - // See AddThisVariable() for more information. - // - public LocalInfo ThisVariable { - get { - if (this_variable != null) - return this_variable; - else if (Parent != null) - return Parent.ThisVariable; - else - return null; - } - } - - Hashtable child_variable_names; + Hashtable known_variables; // - // Marks a variable with name @name as being used in a child block. + // Marks a variable with name @name as being used in this or a child block. // If a variable name has been used in a child block, it's illegal to // declare a variable with the same name in the current block. // - public void AddChildVariableName (string name) + void AddKnownVariable (string name, LocalInfo info) { - if (child_variable_names == null) - child_variable_names = new Hashtable (); + if (known_variables == null) + known_variables = new Hashtable (); - child_variable_names [name] = null; + known_variables [name] = info; } - // - // Checks whether a variable name has already been used in a child block. - // - public bool IsVariableNameUsedInChildBlock (string name) + LocalInfo GetKnownVariableInfo (string name) { - if (child_variable_names == null) - return false; - - return child_variable_names.Contains (name); + if (known_variables == null) + return null; + return (LocalInfo) known_variables [name]; } - // - // This is used by non-static `struct' constructors which do not have an - // initializer - in this case, the constructor must initialize all of the - // struct's fields. To do this, we add a "this" variable and use the flow - // analysis code to ensure that it's been fully initialized before control - // leaves the constructor. - // - public LocalInfo AddThisVariable (TypeContainer tc, Location l) + public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc) { - if (this_variable != null) - return this_variable; + Block b = this; + LocalInfo kvi = b.GetKnownVariableInfo (name); + while (kvi == null) { + while (b.Implicit) + b = b.Parent; + b = b.Parent; + if (b == null) + return true; + kvi = b.GetKnownVariableInfo (name); + } - if (variables == null) - variables = new Hashtable (); + if (kvi.Block == b) + return true; - this_variable = new LocalInfo (tc, this, l); - this_variable.Used = true; - this_variable.IsThis = true; + // Is kvi.Block nested inside 'b' + if (b.known_variables != kvi.Block.known_variables) { + // + // If a variable by the same name it defined in a nested block of this + // block, we violate the invariant meaning in a block. + // + if (b == this) { + Report.SymbolRelatedToPreviousError (kvi.Location, name); + Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name); + return false; + } - variables.Add ("this", this_variable); + // + // It's ok if the definition is in a nested subblock of b, but not + // nested inside this block -- a definition in a sibling block + // should not affect us. + // + return true; + } - return this_variable; + // + // Block 'b' and kvi.Block are the same textual block. + // However, different variables are extant. + // + // Check if the variable is in scope in both blocks. We use + // an indirect check that depends on AddVariable doing its + // part in maintaining the invariant-meaning-in-block property. + // + if (e is LocalVariableReference || (e is Constant && b.GetLocalInfo (name) != null)) + return true; + + // + // Even though we detected the error when the name is used, we + // treat it as if the variable declaration was in error. + // + Report.SymbolRelatedToPreviousError (loc, name); + Error_AlreadyDeclared (kvi.Location, name, "parent or current"); + return false; } - public LocalInfo AddVariable (Expression type, string name, Parameters pars, Location l) + public LocalInfo AddVariable (Expression type, string name, Location l) { - if (variables == null) - variables = new Hashtable (); - LocalInfo vi = GetLocalInfo (name); if (vi != null) { - if (vi.Block != this) - Report.Error (136, l, "A local variable named `" + name + "' " + - "cannot be declared in this scope since it would " + - "give a different meaning to `" + name + "', which " + - "is already used in a `parent or current' scope to " + - "denote something else"); + Report.SymbolRelatedToPreviousError (vi.Location, name); + if (known_variables == vi.Block.known_variables) + Report.Error (128, l, + "A local variable named `{0}' is already defined in this scope", name); else - Report.Error (128, l, "A local variable `" + name + "' is already " + - "defined in this scope"); + Error_AlreadyDeclared (l, name, "parent"); return null; } - if (IsVariableNameUsedInChildBlock (name)) { - Report.Error (136, l, "A local variable named `" + name + "' " + - "cannot be declared in this scope since it would " + - "give a different meaning to `" + name + "', which " + - "is already used in a `child' scope to denote something " + - "else"); + vi = GetKnownVariableInfo (name); + if (vi != null) { + Report.SymbolRelatedToPreviousError (vi.Location, name); + Error_AlreadyDeclared (l, name, "child"); return null; } - if (pars != null) { - int idx; - Parameter p = pars.GetParameterByName (name, out idx); - if (p != null) { - Report.Error (136, l, "A local variable named `" + name + "' " + - "cannot be declared in this scope since it would " + - "give a different meaning to `" + name + "', which " + - "is already used in a `parent or current' scope to " + - "denote something else"); - return null; - } + int idx; + Parameter p = Toplevel.Parameters.GetParameterByName (name, out idx); + if (p != null) { + Report.SymbolRelatedToPreviousError (p.Location, name); + Error_AlreadyDeclared (l, name, "method argument"); + return null; } vi = new LocalInfo (type, name, this, l); - variables.Add (name, vi); + Variables.Add (name, vi); - // Mark 'name' as "used by a child block" in every surrounding block - Block cur = this; - while (cur != null && cur.Implicit) - cur = cur.Parent; - if (cur != null) - for (Block par = cur.Parent; par != null; par = par.Parent) - par.AddChildVariableName (name); + for (Block b = this; b != null; b = b.Parent) + b.AddKnownVariable (name, vi); if ((flags & Flags.VariablesInitialized) != 0) throw new Exception (); - // Console.WriteLine ("Adding {0} to {1}", name, ID); return vi; } - public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l) + void Error_AlreadyDeclared (Location loc, string var, string reason) + { + Report.Error (136, loc, "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning to `{0}', " + + "which is already used in a `{1}' scope", var, reason); + } + + public bool AddConstant (Expression type, string name, Expression value, Location l) { - if (AddVariable (type, name, pars, l) == null) + if (AddVariable (type, name, l) == null) return false; if (constants == null) @@ -1551,10 +1575,20 @@ namespace Mono.CSharp { return true; } - public Hashtable Variables { - get { - return variables; - } + static int next_temp_id = 0; + + public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc) + { + if (temporary_variables == null) + temporary_variables = new ArrayList (); + + int id = ++next_temp_id; + string name = "$s_" + id.ToString (); + + LocalInfo li = new LocalInfo (te, name, this, loc); + li.CompilerGenerated = true; + temporary_variables.Add (li); + return li; } public LocalInfo GetLocalInfo (string name) @@ -1572,11 +1606,7 @@ namespace Mono.CSharp { public Expression GetVariableType (string name) { LocalInfo vi = GetLocalInfo (name); - - if (vi != null) - return vi.Type; - - return null; + return vi == null ? null : vi.Type; } public Expression GetConstantExpression (string name) @@ -1596,94 +1626,10 @@ namespace Mono.CSharp { /// public bool IsConstant (string name) { - Expression e = null; - - e = GetConstantExpression (name); - + Expression e = GetConstantExpression (name); return e != null; } - // - // Returns a `ParameterReference' for the given name, or null if there - // is no such parameter - // - public ParameterReference GetParameterReference (string name, Location loc) - { - Block b = this; - - do { - Parameters pars = b.parameters; - - if (pars != null){ - Parameter par; - int idx; - - par = pars.GetParameterByName (name, out idx); - if (par != null){ - ParameterReference pr; - - pr = new ParameterReference (pars, this, idx, name, loc); - return pr; - } - } - b = b.Parent; - } while (b != null); - return null; - } - - // - // Whether the parameter named `name' is local to this block, - // or false, if the parameter belongs to an encompassing block. - // - public bool IsLocalParameter (string name) - { - Block b = this; - int toplevel_count = 0; - - do { - if (this is ToplevelBlock) - toplevel_count++; - - Parameters pars = b.parameters; - if (pars != null){ - if (pars.GetParameterByName (name) != null) - return true; - return false; - } - if (toplevel_count > 0) - return false; - b = b.Parent; - } while (b != null); - return false; - } - - // - // Whether the `name' is a parameter reference - // - public bool IsParameterReference (string name) - { - Block b = this; - - do { - Parameters pars = b.parameters; - - if (pars != null) - if (pars.GetParameterByName (name) != null) - return true; - b = b.Parent; - } while (b != null); - return false; - } - - /// - /// A list of labels that were not used within this block - /// - public string [] GetUnreferenced () - { - // FIXME: Implement me - return null; - } - public void AddStatement (Statement s) { statements.Add (s); @@ -1691,9 +1637,7 @@ namespace Mono.CSharp { } public bool Used { - get { - return (flags & Flags.BlockUsed) != 0; - } + get { return (flags & Flags.BlockUsed) != 0; } } public void Use () @@ -1702,15 +1646,11 @@ namespace Mono.CSharp { } public bool HasRet { - get { - return (flags & Flags.HasRet) != 0; - } + get { return (flags & Flags.HasRet) != 0; } } public bool IsDestructor { - get { - return (flags & Flags.IsDestructor) != 0; - } + get { return (flags & Flags.IsDestructor) != 0; } } public void SetDestructor () @@ -1747,8 +1687,6 @@ namespace Mono.CSharp { /// public void ResolveMeta (ToplevelBlock toplevel, EmitContext ec, InternalParameters ip) { - ILGenerator ig = ec.ig; - bool old_unsafe = ec.InUnsafe; // If some parent block was unsafe, we remain unsafe even if this block @@ -1807,19 +1745,6 @@ namespace Mono.CSharp { continue; } -#if false - if (remap_locals) - vi.FieldBuilder = ec.MapVariable (name, vi.VariableType); - else if (vi.Pinned) - // - // This is needed to compile on both .NET 1.x and .NET 2.x - // the later introduced `DeclareLocal (Type t, bool pinned)' - // - vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType); - else if (!vi.IsThis) - vi.LocalBuilder = ig.DeclareLocal (vi.VariableType); -#endif - if (constants == null) continue; @@ -1829,22 +1754,16 @@ namespace Mono.CSharp { ec.CurrentBlock = this; Expression e = cv.Resolve (ec); - if (e == null) - continue; Constant ce = e as Constant; if (ce == null){ - Report.Error (133, vi.Location, - "The expression being assigned to `" + - name + "' must be constant (" + e + ")"); + Const.Error_ExpressionMustBeConstant (vi.Location, name); continue; } - if (e.Type != variable_type){ - e = Const.ChangeType (vi.Location, ce, variable_type); - if (e == null) - continue; - } + e = ce.ToType (variable_type, vi.Location); + if (e == null) + continue; constants.Remove (name); constants.Add (name, e); @@ -1871,7 +1790,6 @@ namespace Mono.CSharp { if (variables != null){ bool have_captured_vars = ec.HaveCapturedVariables (); - bool remap_locals = ec.RemapToProxy; foreach (DictionaryEntry de in variables){ LocalInfo vi = (LocalInfo) de.Value; @@ -1879,18 +1797,32 @@ namespace Mono.CSharp { if (have_captured_vars && ec.IsCaptured (vi)) continue; - if (remap_locals){ - vi.FieldBuilder = ec.MapVariable (vi.Name, vi.VariableType); - } else { - if (vi.Pinned) - // - // This is needed to compile on both .NET 1.x and .NET 2.x - // the later introduced `DeclareLocal (Type t, bool pinned)' - // - vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType); - else if (!vi.IsThis) - vi.LocalBuilder = ig.DeclareLocal (vi.VariableType); - } + if (vi.Pinned) + // + // This is needed to compile on both .NET 1.x and .NET 2.x + // the later introduced `DeclareLocal (Type t, bool pinned)' + // + vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType); + else if (!vi.IsThis) + vi.LocalBuilder = ig.DeclareLocal (vi.VariableType); + } + } + + if (temporary_variables != null) { + AnonymousContainer am = ec.CurrentAnonymousMethod; + TypeBuilder scope = null; + if ((am != null) && am.IsIterator) { + scope = am.Scope.ScopeTypeBuilder; + if (scope == null) + throw new InternalErrorException (); + } + foreach (LocalInfo vi in temporary_variables) { + if (scope != null) { + if (vi.FieldBuilder == null) + vi.FieldBuilder = scope.DefineField ( + vi.Name, vi.VariableType, FieldAttributes.Assembly); + } else + vi.LocalBuilder = ig.DeclareLocal (vi.VariableType); } } @@ -1914,15 +1846,46 @@ namespace Mono.CSharp { name = (string) de.Key; if (vector.IsAssigned (vi.VariableInfo)){ - Report.Warning (219, vi.Location, "The variable '{0}' is assigned but its value is never used", name); + Report.Warning (219, vi.Location, "The variable `{0}' is assigned but its value is never used", name); } else { - Report.Warning (168, vi.Location, "The variable '{0}' is declared but never used", name); + Report.Warning (168, vi.Location, "The variable `{0}' is declared but never used", name); } } } } bool unreachable_shown; + bool unreachable; + + private void CheckPossibleMistakenEmptyStatement (Statement s) + { + Statement body; + + // Some statements are wrapped by a Block. Since + // others' internal could be changed, here I treat + // them as possibly wrapped by Block equally. + Block b = s as Block; + if (b != null && b.statements.Count == 1) + s = (Statement) b.statements [0]; + + if (s is Lock) + body = ((Lock) s).Statement; + else if (s is For) + body = ((For) s).Statement; + else if (s is Foreach) + body = ((Foreach) s).Statement; + else if (s is While) + body = ((While) s).Statement; + else if (s is Using) + body = ((Using) s).Statement; + else if (s is Fixed) + body = ((Fixed) s).Statement; + else + return; + + if (body == null || body is EmptyStatement) + Report.Warning (642, 3, s.loc, "Possible mistaken empty statement"); + } public override bool Resolve (EmitContext ec) { @@ -1936,36 +1899,42 @@ namespace Mono.CSharp { Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching); - bool unreachable = false; - + // + // This flag is used to notate nested statements as unreachable from the beginning of this block. + // For the purposes of this resolution, it doesn't matter that the whole block is unreachable + // from the beginning of the function. The outer Resolve() that detected the unreachability is + // responsible for handling the situation. + // int statement_count = statements.Count; for (int ix = 0; ix < statement_count; ix++){ Statement s = (Statement) statements [ix]; - - if (unreachable && !(s is LabeledStatement)) { - if (s == EmptyStatement.Value) - s.loc = EndLocation; - - if (!s.ResolveUnreachable (ec, !unreachable_shown)) - ok = false; - - if (s != EmptyStatement.Value) + // Check possible empty statement (CS0642) + if (RootContext.WarningLevel >= 3 && + ix + 1 < statement_count && + statements [ix + 1] is Block) + CheckPossibleMistakenEmptyStatement (s); + + if (unreachable) { + if (s is Block) + ((Block) s).unreachable = true; + + if (!unreachable_shown && (RootContext.WarningLevel >= 2)) { + Report.Warning ( + 162, loc, "Unreachable code detected"); unreachable_shown = true; - else - s.loc = Location.Null; + } + } + if (!s.Resolve (ec)) { + ok = false; statements [ix] = EmptyStatement.Value; continue; } - if (s.Resolve (ec) == false) { - ok = false; + if (unreachable && !(s is LabeledStatement) && !(s is Block)) statements [ix] = EmptyStatement.Value; - continue; - } num_statements = ix + 1; - if (s is LabeledStatement) unreachable = false; else @@ -1975,22 +1944,21 @@ namespace Mono.CSharp { Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching, statement_count, num_statements); - FlowBranching.UsageVector vector = ec.DoEndFlowBranching (); ec.CurrentBlock = prev_block; // If we're a non-static `struct' constructor which doesn't have an // initializer, then we must initialize all of the struct's fields. - if ((this_variable != null) && - (vector.Reachability.Throws != FlowBranching.FlowReturns.Always) && - !this_variable.IsThisAssigned (ec, loc)) + if ((flags & Flags.IsToplevel) != 0 && + !Toplevel.IsThisAssigned (ec) && + vector.Reachability.Throws != FlowBranching.FlowReturns.Always) ok = false; if ((labels != null) && (RootContext.WarningLevel >= 2)) { foreach (LabeledStatement label in labels.Values) if (!label.HasBeenReferenced) - Report.Warning (164, label.Location, + Report.Warning (164, label.loc, "This label has not been referenced"); } @@ -2012,7 +1980,16 @@ namespace Mono.CSharp { public override bool ResolveUnreachable (EmitContext ec, bool warn) { unreachable_shown = true; - return base.ResolveUnreachable (ec, warn); + unreachable = true; + + if (warn && (RootContext.WarningLevel >= 2)) + Report.Warning (162, loc, "Unreachable code detected"); + + ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc); + bool ok = Resolve (ec); + ec.KillFlowBranching (); + + return ok; } protected override void DoEmit (EmitContext ec) @@ -2023,7 +2000,7 @@ namespace Mono.CSharp { // Check whether we are the last statement in a // top-level block. - if ((Parent == null) && (ix+1 == num_statements)) + if (((Parent == null) || Implicit) && (ix+1 == num_statements) && !(s is Block)) ec.IsLastStatement = true; else ec.IsLastStatement = false; @@ -2043,7 +2020,7 @@ namespace Mono.CSharp { if (emit_debug_info) { if (is_lexical_block) - ec.ig.BeginScope (); + ec.BeginScope (); if (variables != null) { foreach (DictionaryEntry de in variables) { @@ -2063,25 +2040,12 @@ namespace Mono.CSharp { ec.Mark (EndLocation, true); if (emit_debug_info && is_lexical_block) - ec.ig.EndScope (); + ec.EndScope (); ec.CurrentBlock = prev_block; } - public ToplevelBlock Toplevel { - get { - Block b = this; - while (b.Parent != null){ - if ((b.flags & Flags.IsToplevel) != 0) - break; - b = b.Parent; - } - - return (ToplevelBlock) b; - } - } - - // + // // Returns true if we ar ea child of `b'. // public bool IsChildOf (Block b) @@ -2095,6 +2059,11 @@ namespace Mono.CSharp { } while (current != null); return false; } + + public override string ToString () + { + return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation); + } } // @@ -2110,16 +2079,26 @@ namespace Mono.CSharp { // Pointer to the host of this anonymous method, or null // if we are the topmost block // - public ToplevelBlock Container; + ToplevelBlock container; CaptureContext capture_context; + FlowBranching top_level_branching; Hashtable capture_contexts; + ArrayList children; - static int did = 0; - - int my_id = did++; + public bool HasVarargs { + get { return (flags & Flags.HasVarargs) != 0; } + set { flags |= Flags.HasVarargs; } + } + + // + // The parameters for the block. + // + Parameters parameters; + public Parameters Parameters { + get { return parameters; } + } - public void RegisterCaptureContext (CaptureContext cc) { if (capture_contexts == null) @@ -2136,54 +2115,187 @@ namespace Mono.CSharp { cc.AdjustScopes (); } } - + public CaptureContext ToplevelBlockCaptureContext { - get { - return capture_context; - } + get { return capture_context; } } - + + public ToplevelBlock Container { + get { return container; } + } + + protected void AddChild (ToplevelBlock block) + { + if (children == null) + children = new ArrayList (); + + children.Add (block); + } + // // Parent is only used by anonymous blocks to link back to their // parents // public ToplevelBlock (ToplevelBlock container, Parameters parameters, Location start) : - base (null, Flags.IsToplevel, parameters, start, Location.Null) + this (container, (Flags) 0, parameters, start) { - Container = container; } public ToplevelBlock (Parameters parameters, Location start) : - base (null, Flags.IsToplevel, parameters, start, Location.Null) + this (null, (Flags) 0, parameters, start) { } public ToplevelBlock (Flags flags, Parameters parameters, Location start) : - base (null, flags | Flags.IsToplevel, parameters, start, Location.Null) + this (null, flags, parameters, start) { } - public ToplevelBlock (Location loc) : base (null, Flags.IsToplevel, loc, loc) + public ToplevelBlock (ToplevelBlock container, Flags flags, Parameters parameters, Location start) : + base (null, flags | Flags.IsToplevel, start, Location.Null) + { + this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters; + this.container = container; + + if (container != null) + container.AddChild (this); + } + + public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc) { } - public void SetHaveAnonymousMethods (Location loc, AnonymousMethod host) + public void SetHaveAnonymousMethods (Location loc, AnonymousContainer host) { if (capture_context == null) capture_context = new CaptureContext (this, loc, host); } public CaptureContext CaptureContext { - get { - return capture_context; + get { return capture_context; } + } + + public FlowBranching TopLevelBranching { + get { return top_level_branching; } + } + + // + // This is used if anonymous methods are used inside an iterator + // (see 2test-22.cs for an example). + // + // The AnonymousMethod is created while parsing - at a time when we don't + // know yet that we're inside an iterator, so it's `Container' is initially + // null. Later on, when resolving the iterator, we need to move the + // anonymous method into that iterator. + // + public void ReParent (ToplevelBlock new_parent, AnonymousContainer new_host) + { + foreach (ToplevelBlock block in children) { + if (block.CaptureContext == null) + continue; + + block.container = new_parent; + block.CaptureContext.ReParent (new_parent, new_host); + } + } + + // + // Returns a `ParameterReference' for the given name, or null if there + // is no such parameter + // + public ParameterReference GetParameterReference (string name, Location loc) + { + Parameter par; + int idx; + + for (ToplevelBlock t = this; t != null; t = t.Container) { + Parameters pars = t.Parameters; + par = pars.GetParameterByName (name, out idx); + if (par != null) + return new ParameterReference (pars, this, idx, name, loc); + } + return null; + } + + // + // Whether the parameter named `name' is local to this block, + // or false, if the parameter belongs to an encompassing block. + // + public bool IsLocalParameter (string name) + { + return Parameters.GetParameterByName (name) != null; + } + + // + // Whether the `name' is a parameter reference + // + public bool IsParameterReference (string name) + { + for (ToplevelBlock t = this; t != null; t = t.Container) { + if (t.IsLocalParameter (name)) + return true; + } + return false; + } + + LocalInfo this_variable = null; + + // + // Returns the "this" instance variable of this block. + // See AddThisVariable() for more information. + // + public LocalInfo ThisVariable { + get { return this_variable; } + } + + + // + // This is used by non-static `struct' constructors which do not have an + // initializer - in this case, the constructor must initialize all of the + // struct's fields. To do this, we add a "this" variable and use the flow + // analysis code to ensure that it's been fully initialized before control + // leaves the constructor. + // + public LocalInfo AddThisVariable (TypeContainer tc, Location l) + { + if (this_variable == null) { + this_variable = new LocalInfo (tc, this, l); + this_variable.Used = true; + this_variable.IsThis = true; + + Variables.Add ("this", this_variable); } + + return this_variable; + } + + public bool IsThisAssigned (EmitContext ec) + { + return this_variable == null || this_variable.IsThisAssigned (ec, loc); + } + + public bool ResolveMeta (EmitContext ec, InternalParameters ip) + { + int errors = Report.Errors; + + if (top_level_branching != null) + return true; + + if (ip != null) + parameters = ip.Parameters; + + ResolveMeta (this, ec, ip); + + top_level_branching = ec.StartFlowBranching (this); + + return Report.Errors == errors; } } public class SwitchLabel { Expression label; object converted; - public Location loc; + Location loc; Label il_label; bool il_label_set; @@ -2234,33 +2346,43 @@ namespace Mono.CSharp { // and then converts it to the requested type. // public bool ResolveAndReduce (EmitContext ec, Type required_type) - { - if (label == null) - return true; - + { Expression e = label.Resolve (ec); if (e == null) return false; - if (!(e is Constant)){ - Report.Error (150, loc, "A constant value is expected, got: " + e); + Constant c = e as Constant; + if (c == null){ + Report.Error (150, loc, "A constant value is expected"); return false; } - if (e is StringConstant || e is NullLiteral){ - if (required_type == TypeManager.string_type){ - converted = e; - return true; - } + if (required_type == TypeManager.string_type && e is NullLiteral) { + converted = e; + return true; } - converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc); - if (converted == null) + c = c.ToType (required_type, loc); + if (c == null) return false; + converted = c.GetValue (); return true; } + + public void Erorr_AlreadyOccurs () + { + string label; + if (converted == null) + label = "default"; + else if (converted is NullLiteral) + label = "null"; + else + label = converted.ToString (); + + Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label); + } } public class SwitchSection { @@ -2282,7 +2404,7 @@ namespace Mono.CSharp { /// /// Maps constants whose type type SwitchType to their SwitchLabels. /// - public Hashtable Elements; + public IDictionary Elements; /// /// The governing switch type @@ -2292,11 +2414,11 @@ namespace Mono.CSharp { // // Computed // - bool got_default; Label default_target; Expression new_expr; bool is_constant; SwitchSection constant_section; + SwitchSection default_section; // // The types allowed to be implicitly cast from @@ -2313,7 +2435,7 @@ namespace Mono.CSharp { public bool GotDefault { get { - return got_default; + return default_section != null; } } @@ -2331,33 +2453,33 @@ namespace Mono.CSharp { // Expression SwitchGoverningType (EmitContext ec, Type t) { - if (t == TypeManager.int32_type || - t == TypeManager.uint32_type || - t == TypeManager.char_type || - t == TypeManager.byte_type || + if (t == TypeManager.byte_type || t == TypeManager.sbyte_type || t == TypeManager.ushort_type || t == TypeManager.short_type || + t == TypeManager.uint32_type || + t == TypeManager.int32_type || t == TypeManager.uint64_type || t == TypeManager.int64_type || + t == TypeManager.char_type || t == TypeManager.string_type || - t == TypeManager.bool_type || - t.IsSubclassOf (TypeManager.enum_type)) + t == TypeManager.bool_type || + t.IsSubclassOf (TypeManager.enum_type)) return Expr; if (allowed_types == null){ allowed_types = new Type [] { - TypeManager.int32_type, - TypeManager.uint32_type, TypeManager.sbyte_type, TypeManager.byte_type, TypeManager.short_type, TypeManager.ushort_type, + TypeManager.int32_type, + TypeManager.uint32_type, TypeManager.int64_type, TypeManager.uint64_type, TypeManager.char_type, - TypeManager.bool_type, - TypeManager.string_type + TypeManager.string_type, + TypeManager.bool_type }; } @@ -2379,32 +2501,22 @@ namespace Mono.CSharp { // Ignore over-worked ImplicitUserConversions that do // an implicit conversion in addition to the user conversion. // - if (e is UserCast){ - UserCast ue = e as UserCast; + if (!(e is UserCast)) + continue; - if (ue.Source != Expr) - e = null; - } - if (converted != null){ Report.ExtraInformation ( loc, String.Format ("reason: more than one conversion to an integral type exist for type {0}", TypeManager.CSharpName (Expr.Type))); return null; - } else { - converted = e; } + + converted = e; } return converted; } - static string Error152 { - get { - return "The label '{0}:' already occurs in this switch statement"; - } - } - // // Performs the basic sanity checks on the switch statement // (looks for duplicate keys and non-constant expressions). @@ -2414,143 +2526,38 @@ namespace Mono.CSharp { // bool CheckSwitch (EmitContext ec) { - Type compare_type; bool error = false; - Elements = new Hashtable (); + Elements = Sections.Count > 10 ? + (IDictionary)new Hashtable () : + (IDictionary)new ListDictionary (); - got_default = false; - - if (TypeManager.IsEnumType (SwitchType)){ - compare_type = TypeManager.EnumToUnderlying (SwitchType); - } else - compare_type = SwitchType; - foreach (SwitchSection ss in Sections){ foreach (SwitchLabel sl in ss.Labels){ - if (!sl.ResolveAndReduce (ec, SwitchType)){ - error = true; - continue; - } - if (sl.Label == null){ - if (got_default){ - Report.Error (152, sl.loc, Error152, "default"); + if (default_section != null){ + sl.Erorr_AlreadyOccurs (); error = true; } - got_default = true; + default_section = ss; continue; } - - object key = sl.Converted; - - if (key is Constant) - key = ((Constant) key).GetValue (); - - if (key == null) - key = NullLiteral.Null; - - string lname = null; - if (compare_type == TypeManager.uint64_type){ - ulong v = (ulong) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.int64_type){ - long v = (long) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.uint32_type){ - uint v = (uint) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.char_type){ - char v = (char) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.byte_type){ - byte v = (byte) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.sbyte_type){ - sbyte v = (sbyte) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.short_type){ - short v = (short) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.ushort_type){ - ushort v = (ushort) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.string_type){ - if (key is NullLiteral){ - if (Elements.Contains (NullLiteral.Null)) - lname = "null"; - else - Elements.Add (NullLiteral.Null, null); - } else { - string s = (string) key; - - if (Elements.Contains (s)) - lname = s; - else - Elements.Add (s, sl); - } - } else if (compare_type == TypeManager.int32_type) { - int v = (int) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } else if (compare_type == TypeManager.bool_type) { - bool v = (bool) key; - - if (Elements.Contains (v)) - lname = v.ToString (); - else - Elements.Add (v, sl); - } - else - { - throw new Exception ("Unknown switch type!" + - SwitchType + " " + compare_type); - } - if (lname != null){ - Report.Error (152, sl.loc, Error152, "case " + lname); + if (!sl.ResolveAndReduce (ec, SwitchType)){ error = true; + continue; } + + object key = sl.Converted; + try { + Elements.Add (key, sl); + } + catch (ArgumentException) { + sl.Erorr_AlreadyOccurs (); + error = true; + } } } - if (error) - return false; - - return true; + return !error; } void EmitObjectInteger (ILGenerator ig, object k) @@ -2843,12 +2850,11 @@ namespace Mono.CSharp { Label end_of_switch = ig.DefineLabel (); Label next_test = ig.DefineLabel (); Label null_target = ig.DefineLabel (); - bool default_found = false; bool first_test = true; bool pending_goto_end = false; + bool null_marked = false; bool null_found; - bool default_at_end = false; - + ig.Emit (OpCodes.Ldloc, val); if (Elements.Contains (NullLiteral.Null)){ @@ -2859,17 +2865,22 @@ namespace Mono.CSharp { ig.Emit (OpCodes.Ldloc, val); ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string); ig.Emit (OpCodes.Stloc, val); - + int section_count = Sections.Count; for (int section = 0; section < section_count; section++){ SwitchSection ss = (SwitchSection) Sections [section]; + + if (ss == default_section) + continue; + Label sec_begin = ig.DefineLabel (); + ig.Emit (OpCodes.Nop); + if (pending_goto_end) ig.Emit (OpCodes.Br, end_of_switch); int label_count = ss.Labels.Count; - bool mark_default = false; null_found = false; for (int label = 0; label < label_count; label++){ SwitchLabel sl = (SwitchLabel) ss.Labels [label]; @@ -2882,12 +2893,7 @@ namespace Mono.CSharp { // // If we are the default target // - if (sl.Label == null){ - if (label+1 == label_count) - default_at_end = true; - mark_default = true; - default_found = true; - } else { + if (sl.Label != null){ object lit = sl.Converted; if (lit is NullLiteral){ @@ -2895,12 +2901,10 @@ namespace Mono.CSharp { if (label_count == 1) ig.Emit (OpCodes.Br, next_test); continue; - } - StringConstant str = (StringConstant) lit; ig.Emit (OpCodes.Ldloc, val); - ig.Emit (OpCodes.Ldstr, str.Value); + ig.Emit (OpCodes.Ldstr, (string)lit); if (label_count == 1) ig.Emit (OpCodes.Bne_Un, next_test); else { @@ -2911,24 +2915,24 @@ namespace Mono.CSharp { } } } - if (null_found) + if (null_found) { ig.MarkLabel (null_target); + null_marked = true; + } ig.MarkLabel (sec_begin); foreach (SwitchLabel sl in ss.Labels) ig.MarkLabel (sl.GetILLabelCode (ec)); - if (mark_default) - ig.MarkLabel (default_target); ss.Block.Emit (ec); pending_goto_end = !ss.Block.HasRet; first_test = false; } ig.MarkLabel (next_test); - if (default_found){ - if (!default_at_end) - ig.Emit (OpCodes.Br, default_target); - } else - ig.MarkLabel (default_target); + ig.MarkLabel (default_target); + if (!null_marked) + ig.MarkLabel (null_target); + if (default_section != null) + default_section.Block.Emit (ec); ig.MarkLabel (end_of_switch); } @@ -2944,24 +2948,6 @@ namespace Mono.CSharp { return null; } - bool ResolveConstantSwitch (EmitContext ec) - { - object key = ((Constant) new_expr).GetValue (); - SwitchLabel label = (SwitchLabel) Elements [key]; - - if (label == null) - return true; - - constant_section = FindSection (label); - if (constant_section == null) - return true; - - if (constant_section.Block.Resolve (ec) != true) - return false; - - return true; - } - public override bool Resolve (EmitContext ec) { Expr = Expr.Resolve (ec); @@ -2970,7 +2956,7 @@ namespace Mono.CSharp { new_expr = SwitchGoverningType (ec, Expr.Type); if (new_expr == null){ - Report.Error (151, loc, "An integer type or string was expected for switch"); + Report.Error (151, loc, "A value of an integral type or string expected for switch"); return false; } @@ -2993,6 +2979,8 @@ namespace Mono.CSharp { SwitchLabel label = (SwitchLabel) Elements [key]; constant_section = FindSection (label); + if (constant_section == null) + constant_section = default_section; } bool first = true; @@ -3012,11 +3000,11 @@ namespace Mono.CSharp { return false; } else { if (!ss.Block.Resolve (ec)) - return false; + return false; } } - if (!got_default) + if (default_section == null) ec.CurrentBranching.CreateSibling ( null, FlowBranching.SiblingType.SwitchSection); @@ -3084,7 +3072,7 @@ namespace Mono.CSharp { { if (emit_finally) ec.ig.BeginFinallyBlock (); - else + else if (ec.InIterator) ec.CurrentIterator.MarkFinally (ec, parent_vectors); EmitFinally (ec); } @@ -3099,7 +3087,7 @@ namespace Mono.CSharp { public class Lock : ExceptionStatement { Expression expr; - Statement Statement; + public Statement Statement; LocalBuilder temp; public Lock (Expression expr, Statement stmt, Location l) @@ -3116,9 +3104,9 @@ namespace Mono.CSharp { return false; if (expr.Type.IsValueType){ - Error (185, "lock statement requires the expression to be " + - " a reference type (type is: `{0}'", - TypeManager.CSharpName (expr.Type)); + Report.Error (185, loc, + "`{0}' is not a reference type as required by the lock statement", + TypeManager.CSharpName (expr.Type)); return false; } @@ -3286,15 +3274,81 @@ namespace Mono.CSharp { ArrayList declarators; Statement statement; Type expr_type; - FixedData[] data; + Emitter[] data; bool has_ret; - struct FixedData { - public bool is_object; - public LocalInfo vi; - public Expression expr; - public Expression converted; - } + abstract class Emitter + { + protected LocalInfo vi; + protected Expression converted; + + protected Emitter (Expression expr, LocalInfo li) + { + converted = expr; + vi = li; + } + + public abstract void Emit (EmitContext ec); + public abstract void EmitExit (ILGenerator ig); + } + + class ExpressionEmitter: Emitter { + public ExpressionEmitter (Expression converted, LocalInfo li) : + base (converted, li) + { + } + + public override void Emit (EmitContext ec) { + // + // Store pointer in pinned location + // + converted.Emit (ec); + ec.ig.Emit (OpCodes.Stloc, vi.LocalBuilder); + } + + public override void EmitExit (ILGenerator ig) + { + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Conv_U); + ig.Emit (OpCodes.Stloc, vi.LocalBuilder); + } + } + + class StringEmitter: Emitter { + LocalBuilder pinned_string; + Location loc; + + public StringEmitter (Expression expr, LocalInfo li, Location loc): + base (expr, li) + { + this.loc = loc; + } + + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type); + + converted.Emit (ec); + ig.Emit (OpCodes.Stloc, pinned_string); + + Expression sptr = new StringPtr (pinned_string, loc); + converted = Convert.ImplicitConversionRequired ( + ec, sptr, vi.VariableType, loc); + + if (converted == null) + return; + + converted.Emit (ec); + ig.Emit (OpCodes.Stloc, vi.LocalBuilder); + } + + public override void EmitExit(ILGenerator ig) + { + ig.Emit (OpCodes.Ldnull); + ig.Emit (OpCodes.Stloc, pinned_string); + } + } public Fixed (Expression type, ArrayList decls, Statement stmt, Location l) { @@ -3304,6 +3358,10 @@ namespace Mono.CSharp { loc = l; } + public Statement Statement { + get { return statement; } + } + public override bool Resolve (EmitContext ec) { if (!ec.InUnsafe){ @@ -3319,15 +3377,10 @@ namespace Mono.CSharp { CheckObsolete (expr_type); - if (ec.RemapToProxy){ - Report.Error (-210, loc, "Fixed statement not allowed in iterators"); - return false; - } - - data = new FixedData [declarators.Count]; + data = new Emitter [declarators.Count]; if (!expr_type.IsPointer){ - Report.Error (209, loc, "Variables in a fixed statement must be pointers"); + Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type"); return false; } @@ -3337,7 +3390,7 @@ namespace Mono.CSharp { Expression e = (Expression) p.Second; vi.VariableInfo.SetAssigned (ec); - vi.ReadOnly = true; + vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed); // // The rules for the possible declarators are pretty wise, @@ -3351,7 +3404,7 @@ namespace Mono.CSharp { // if (e is Cast){ - Report.Error (254, loc, "Cast expression not allowed as right hand expression in fixed statement"); + Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression"); return false; } @@ -3381,10 +3434,7 @@ namespace Mono.CSharp { if (!TypeManager.VerifyUnManaged (child.Type, loc)) return false; - data [i].is_object = true; - data [i].expr = e; - data [i].converted = null; - data [i].vi = vi; + data [i] = new ExpressionEmitter (e, vi); i++; continue; @@ -3412,17 +3462,14 @@ namespace Mono.CSharp { // and T* is implicitly convertible to the // pointer type given in the fixed statement. // - ArrayPtr array_ptr = new ArrayPtr (e, loc); + ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc); Expression converted = Convert.ImplicitConversionRequired ( ec, array_ptr, vi.VariableType, loc); if (converted == null) return false; - data [i].is_object = false; - data [i].expr = e; - data [i].converted = converted; - data [i].vi = vi; + data [i] = new ExpressionEmitter (converted, vi); i++; continue; @@ -3432,14 +3479,30 @@ namespace Mono.CSharp { // Case 3: string // if (e.Type == TypeManager.string_type){ - data [i].is_object = false; - data [i].expr = e; - data [i].converted = null; - data [i].vi = vi; + data [i] = new StringEmitter (e, vi, loc); i++; continue; } + // Case 4: fixed buffer + FieldExpr fe = e as FieldExpr; + if (fe != null) { + IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo); + if (ff != null) { + Expression fixed_buffer_ptr = new FixedBufferPtr (fe, ff.ElementType, loc); + + Expression converted = Convert.ImplicitConversionRequired ( + ec, fixed_buffer_ptr, vi.VariableType, loc); + if (converted == null) + return false; + + data [i] = new ExpressionEmitter (converted, vi); + i++; + + continue; + } + } + // // For other cases, flag a `this is already fixed expression' // @@ -3469,60 +3532,8 @@ namespace Mono.CSharp { protected override void DoEmit (EmitContext ec) { - ILGenerator ig = ec.ig; - - LocalBuilder [] clear_list = new LocalBuilder [data.Length]; - for (int i = 0; i < data.Length; i++) { - LocalInfo vi = data [i].vi; - - // - // Case 1: & object. - // - if (data [i].is_object) { - // - // Store pointer in pinned location - // - data [i].expr.Emit (ec); - ig.Emit (OpCodes.Stloc, vi.LocalBuilder); - clear_list [i] = vi.LocalBuilder; - continue; - } - - // - // Case 2: Array - // - if (data [i].expr.Type.IsArray){ - // - // Store pointer in pinned location - // - data [i].converted.Emit (ec); - - ig.Emit (OpCodes.Stloc, vi.LocalBuilder); - clear_list [i] = vi.LocalBuilder; - continue; - } - - // - // Case 3: string - // - if (data [i].expr.Type == TypeManager.string_type){ - LocalBuilder pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type); - clear_list [i] = pinned_string; - - data [i].expr.Emit (ec); - ig.Emit (OpCodes.Stloc, pinned_string); - - Expression sptr = new StringPtr (pinned_string, loc); - Expression converted = Convert.ImplicitConversionRequired ( - ec, sptr, vi.VariableType, loc); - - if (converted == null) - continue; - - converted.Emit (ec); - ig.Emit (OpCodes.Stloc, vi.LocalBuilder); - } + data [i].Emit (ec); } statement.Emit (ec); @@ -3530,18 +3541,13 @@ namespace Mono.CSharp { if (has_ret) return; + ILGenerator ig = ec.ig; + // // Clear the pinned variable // for (int i = 0; i < data.Length; i++) { - if (data [i].is_object || data [i].expr.Type.IsArray) { - ig.Emit (OpCodes.Ldc_I4_0); - ig.Emit (OpCodes.Conv_U); - ig.Emit (OpCodes.Stloc, clear_list [i]); - } else if (data [i].expr.Type == TypeManager.string_type){ - ig.Emit (OpCodes.Ldnull); - ig.Emit (OpCodes.Stloc, clear_list [i]); - } + data [i].EmitExit (ig); } } } @@ -3579,23 +3585,30 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { - if (type_expr != null) { - TypeExpr te = type_expr.ResolveAsTypeTerminal (ec); - if (te == null) - return false; + bool was_catch = ec.InCatch; + ec.InCatch = true; + try { + if (type_expr != null) { + TypeExpr te = type_expr.ResolveAsTypeTerminal (ec); + if (te == null) + return false; - type = te.Type; + type = te.ResolveType (ec); - CheckObsolete (type); + CheckObsolete (type); - if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){ - Error (155, "The type caught or thrown must be derived from System.Exception"); - return false; - } - } else - type = null; + if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){ + Error (155, "The type caught or thrown must be derived from System.Exception"); + return false; + } + } else + type = null; - return Block.Resolve (ec); + return Block.Resolve (ec); + } + finally { + ec.InCatch = was_catch; + } } } @@ -3659,7 +3672,7 @@ namespace Mono.CSharp { Type resolvedType = c.CatchType; for (int ii = 0; ii < last_index; ++ii) { if (resolvedType == prevCatches [ii] || resolvedType.IsSubclassOf (prevCatches [ii])) { - Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type '{0}'", prevCatches [ii].FullName); + Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prevCatches [ii].FullName); return false; } } @@ -3690,13 +3703,21 @@ namespace Mono.CSharp { Fini, FlowBranching.SiblingType.Finally); Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector); - + bool was_finally = ec.InFinally; + ec.InFinally = true; if (!Fini.Resolve (ec)) ok = false; + ec.InFinally = was_finally; + + if (!ec.InIterator) + need_exc_block = true; } - ResolveFinally (branching); - need_exc_block |= emit_finally; + if (ec.InIterator) { + ResolveFinally (branching); + need_exc_block |= emit_finally; + } else + emit_finally = Fini != null; FlowBranching.Reachability reachability = ec.EndFlowBranching (); @@ -3735,6 +3756,11 @@ namespace Mono.CSharp { throw new Exception ("Variable does not exist in this block"); ig.Emit (OpCodes.Stloc, vi.LocalBuilder); + if (vi.IsCaptured){ + ec.EmitCapturedVariableInstance (vi); + ig.Emit (OpCodes.Ldloc, vi.LocalBuilder); + ig.Emit (OpCodes.Stfld, vi.FieldBuilder); + } } else ig.Emit (OpCodes.Pop); @@ -3754,15 +3780,21 @@ namespace Mono.CSharp { public override void EmitFinally (EmitContext ec) { - if (Fini != null){ + if (Fini != null) Fini.Emit (ec); + } + + public bool HasCatch + { + get { + return General != null || Specific.Count > 0; } } } public class Using : ExceptionStatement { object expression_or_block; - Statement Statement; + public Statement Statement; ArrayList var_list; Expression expr; Type expr_type; @@ -3806,7 +3838,7 @@ namespace Mono.CSharp { foreach (DictionaryEntry e in var_list){ Expression var = (Expression) e.Key; - var = var.ResolveLValue (ec, new EmptyExpression ()); + var = var.ResolveLValue (ec, new EmptyExpression (), loc); if (var == null) return false; @@ -3849,11 +3881,11 @@ namespace Mono.CSharp { bool ResolveExpression (EmitContext ec) { if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){ - conv = Convert.ImplicitConversionRequired ( - ec, expr, TypeManager.idisposable_type, loc); - - if (conv == null) + if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) { + Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to 'System.IDisposable'", + TypeManager.CSharpName (expr_type)); return false; + } } return true; @@ -3885,7 +3917,7 @@ namespace Mono.CSharp { ILGenerator ig = ec.ig; int i = assign.Length; - foreach (DictionaryEntry e in var_list){ + for (int ii = 0; ii < var_list.Count; ++ii){ Expression var = resolved_vars [--i]; Label skip = ig.DefineLabel (); @@ -4061,16 +4093,13 @@ namespace Mono.CSharp { /// /// Implementation of the foreach C# statement /// - public class Foreach : ExceptionStatement { + public class Foreach : Statement { Expression type; Expression variable; Expression expr; Statement statement; - ForeachHelperMethods hm; - Expression empty, conv; - Type array_type, element_type; - Type var_type; - VariableStorage enumerator; + ArrayForeach array; + CollectionForeach collection; public Foreach (Expression type, LocalVariableReference var, Expression expr, Statement stmt, Location l) @@ -4081,18 +4110,27 @@ namespace Mono.CSharp { statement = stmt; loc = l; } - + + public Statement Statement { + get { return statement; } + } + public override bool Resolve (EmitContext ec) { expr = expr.Resolve (ec); if (expr == null) return false; + if (expr is NullLiteral) { + Report.Error (186, loc, "Use of null is not valid in this context"); + return false; + } + TypeExpr texpr = type.ResolveAsTypeTerminal (ec); if (texpr == null) return false; - - var_type = texpr.Type; + + Type var_type = texpr.Type; // // We need an instance variable. Not sure this is the best @@ -4103,628 +4141,696 @@ namespace Mono.CSharp { // if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value || expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){ - error1579 (expr.Type); + collection.error1579 (); return false; } if (expr.Type.IsArray) { - array_type = expr.Type; - element_type = TypeManager.GetElementType (array_type); - - empty = new EmptyExpression (element_type); + array = new ArrayForeach (var_type, variable, expr, statement, loc); + return array.Resolve (ec); } else { - hm = ProbeCollectionType (ec, expr.Type); - if (hm == null){ - error1579 (expr.Type); - return false; - } + collection = new CollectionForeach ( + var_type, variable, expr, statement, loc); + return collection.Resolve (ec); + } + } - array_type = expr.Type; - element_type = hm.element_type; + protected override void DoEmit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd; + ec.LoopBegin = ig.DefineLabel (); + ec.LoopEnd = ig.DefineLabel (); + + if (collection != null) + collection.Emit (ec); + else + array.Emit (ec); + + ec.LoopBegin = old_begin; + ec.LoopEnd = old_end; + } - empty = new EmptyExpression (hm.element_type); + protected class TemporaryVariable : Expression, IMemoryLocation + { + LocalInfo li; + + public TemporaryVariable (Type type, Location loc) + { + this.type = type; + this.loc = loc; + eclass = ExprClass.Value; } - bool ok = true; + public override Expression DoResolve (EmitContext ec) + { + if (li != null) + return this; - ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc); - ec.CurrentBranching.CreateSibling (); + TypeExpr te = new TypeExpression (type, loc); + li = ec.CurrentBlock.AddTemporaryVariable (te, loc); + if (!li.Resolve (ec)) + return null; - // - // - // FIXME: maybe we can apply the same trick we do in the - // array handling to avoid creating empty and conv in some cases. - // - // Although it is not as important in this case, as the type - // will not likely be object (what the enumerator will return). - // - conv = Convert.ExplicitConversion (ec, empty, var_type, loc); - if (conv == null) - ok = false; + AnonymousContainer am = ec.CurrentAnonymousMethod; + if ((am != null) && am.IsIterator) + ec.CaptureVariable (li); - variable = variable.ResolveLValue (ec, empty); - if (variable == null) - ok = false; + return this; + } - bool disposable = (hm != null) && hm.is_disposable; - FlowBranchingException branching = null; - if (disposable) - branching = ec.StartFlowBranching (this); + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; - if (!statement.Resolve (ec)) - ok = false; + if (li.FieldBuilder != null) { + ig.Emit (OpCodes.Ldarg_0); + ig.Emit (OpCodes.Ldfld, li.FieldBuilder); + } else { + ig.Emit (OpCodes.Ldloc, li.LocalBuilder); + } + } - if (disposable) { - ResolveFinally (branching); - ec.EndFlowBranching (); - } else - emit_finally = true; + public void EmitLoadAddress (EmitContext ec) + { + ILGenerator ig = ec.ig; - ec.EndFlowBranching (); + if (li.FieldBuilder != null) { + ig.Emit (OpCodes.Ldarg_0); + ig.Emit (OpCodes.Ldflda, li.FieldBuilder); + } else { + ig.Emit (OpCodes.Ldloca, li.LocalBuilder); + } + } - return ok; - } - - // - // Retrieves a `public bool MoveNext ()' method from the Type `t' - // - static MethodInfo FetchMethodMoveNext (Type t) - { - MemberList move_next_list; - - move_next_list = TypeContainer.FindMembers ( - t, MemberTypes.Method, - BindingFlags.Public | BindingFlags.Instance, - Type.FilterName, "MoveNext"); - if (move_next_list.Count == 0) - return null; + public void Store (EmitContext ec, Expression right_side) + { + if (li.FieldBuilder != null) + ec.ig.Emit (OpCodes.Ldarg_0); - foreach (MemberInfo m in move_next_list){ - MethodInfo mi = (MethodInfo) m; - Type [] args; - - args = TypeManager.GetArgumentTypes (mi); - if (args != null && args.Length == 0){ - if (TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) - return mi; + right_side.Emit (ec); + if (li.FieldBuilder != null) { + ec.ig.Emit (OpCodes.Stfld, li.FieldBuilder); + } else { + ec.ig.Emit (OpCodes.Stloc, li.LocalBuilder); } } - return null; - } - - // - // Retrieves a `public T get_Current ()' method from the Type `t' - // - static MethodInfo FetchMethodGetCurrent (Type t) - { - MemberList get_current_list; - get_current_list = TypeContainer.FindMembers ( - t, MemberTypes.Method, - BindingFlags.Public | BindingFlags.Instance, - Type.FilterName, "get_Current"); - if (get_current_list.Count == 0) - return null; + public void EmitThis (EmitContext ec) + { + if (li.FieldBuilder != null) { + ec.ig.Emit (OpCodes.Ldarg_0); + } + } - foreach (MemberInfo m in get_current_list){ - MethodInfo mi = (MethodInfo) m; - Type [] args; + public void EmitStore (ILGenerator ig) + { + if (li.FieldBuilder != null) + ig.Emit (OpCodes.Stfld, li.FieldBuilder); + else + ig.Emit (OpCodes.Stloc, li.LocalBuilder); + } - args = TypeManager.GetArgumentTypes (mi); - if (args != null && args.Length == 0) - return mi; + public void AddressOf (EmitContext ec, AddressOp mode) + { + EmitLoadAddress (ec); } - return null; } - // - // Retrieves a `public void Dispose ()' method from the Type `t' - // - static MethodInfo FetchMethodDispose (Type t) + protected class ArrayCounter : TemporaryVariable { - MemberList dispose_list; - - dispose_list = TypeContainer.FindMembers ( - t, MemberTypes.Method, - BindingFlags.Public | BindingFlags.Instance, - Type.FilterName, "Dispose"); - if (dispose_list.Count == 0) - return null; + public ArrayCounter (Location loc) + : base (TypeManager.int32_type, loc) + { } - foreach (MemberInfo m in dispose_list){ - MethodInfo mi = (MethodInfo) m; - Type [] args; - - args = TypeManager.GetArgumentTypes (mi); - if (args != null && args.Length == 0){ - if (mi.ReturnType == TypeManager.void_type) - return mi; - } + public void Initialize (EmitContext ec) + { + EmitThis (ec); + ec.ig.Emit (OpCodes.Ldc_I4_0); + EmitStore (ec.ig); } - return null; - } - // - // This struct records the helper methods used by the Foreach construct - // - class ForeachHelperMethods { - public EmitContext ec; - public MethodInfo get_enumerator; - public MethodInfo move_next; - public MethodInfo get_current; - public Type element_type; - public Type enumerator_type; - public bool is_disposable; - - public ForeachHelperMethods (EmitContext ec) + public void Increment (EmitContext ec) { - this.ec = ec; - this.element_type = TypeManager.object_type; - this.enumerator_type = TypeManager.ienumerator_type; - this.is_disposable = true; + EmitThis (ec); + Emit (ec); + ec.ig.Emit (OpCodes.Ldc_I4_1); + ec.ig.Emit (OpCodes.Add); + EmitStore (ec.ig); } } - - static bool GetEnumeratorFilter (MemberInfo m, object criteria) + + protected class ArrayForeach : Statement { - if (m == null) - return false; + Expression variable, expr, conv; + Statement statement; + Type array_type; + Type var_type; + TemporaryVariable[] lengths; + ArrayCounter[] counter; + int rank; - if (!(m is MethodInfo)) - return false; - - if (m.Name != "GetEnumerator") - return false; + TemporaryVariable copy; + Expression access; - MethodInfo mi = (MethodInfo) m; - Type [] args = TypeManager.GetArgumentTypes (mi); - if (args != null){ - if (args.Length != 0) - return false; + public ArrayForeach (Type var_type, Expression var, + Expression expr, Statement stmt, Location l) + { + this.var_type = var_type; + this.variable = var; + this.expr = expr; + statement = stmt; + loc = l; } - ForeachHelperMethods hm = (ForeachHelperMethods) criteria; - EmitContext ec = hm.ec; - // Check whether GetEnumerator is public - if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public) - return false; + public override bool Resolve (EmitContext ec) + { + array_type = expr.Type; + rank = array_type.GetArrayRank (); - if ((mi.ReturnType == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type)) - // - // Apply the same optimization as MS: skip the GetEnumerator - // returning an IEnumerator, and use the one returning a - // CharEnumerator instead. This allows us to avoid the - // try-finally block and the boxing. - // - return false; + copy = new TemporaryVariable (array_type, loc); + copy.Resolve (ec); - // - // Ok, we can access it, now make sure that we can do something - // with this `GetEnumerator' - // + counter = new ArrayCounter [rank]; + lengths = new TemporaryVariable [rank]; - Type return_type = mi.ReturnType; - if (mi.ReturnType == TypeManager.ienumerator_type || - TypeManager.ienumerator_type.IsAssignableFrom (return_type) || - (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) { - - // - // If it is not an interface, lets try to find the methods ourselves. - // For example, if we have: - // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}} - // We can avoid the iface call. This is a runtime perf boost. - // even bigger if we have a ValueType, because we avoid the cost - // of boxing. - // - // We have to make sure that both methods exist for us to take - // this path. If one of the methods does not exist, we will just - // use the interface. Sadly, this complex if statement is the only - // way I could do this without a goto - // - - if (return_type.IsInterface || - (hm.move_next = FetchMethodMoveNext (return_type)) == null || - (hm.get_current = FetchMethodGetCurrent (return_type)) == null) { - - hm.move_next = TypeManager.bool_movenext_void; - hm.get_current = TypeManager.object_getcurrent_void; - return true; - } + ArrayList list = new ArrayList (); + for (int i = 0; i < rank; i++) { + counter [i] = new ArrayCounter (loc); + counter [i].Resolve (ec); - } else { + lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc); + lengths [i].Resolve (ec); - // - // Ok, so they dont return an IEnumerable, we will have to - // find if they support the GetEnumerator pattern. - // - - hm.move_next = FetchMethodMoveNext (return_type); - if (hm.move_next == null) + list.Add (counter [i]); + } + + access = new ElementAccess (copy, list).Resolve (ec); + if (access == null) return false; - - hm.get_current = FetchMethodGetCurrent (return_type); - if (hm.get_current == null) + + conv = Convert.ExplicitConversion (ec, access, var_type, loc); + if (conv == null) return false; - } - - hm.element_type = hm.get_current.ReturnType; - hm.enumerator_type = return_type; - hm.is_disposable = !hm.enumerator_type.IsSealed || - TypeManager.ImplementsInterface ( - hm.enumerator_type, TypeManager.idisposable_type); - return true; - } - - /// - /// This filter is used to find the GetEnumerator method - /// on which IEnumerator operates - /// - static MemberFilter FilterEnumerator; - - static Foreach () - { - FilterEnumerator = new MemberFilter (GetEnumeratorFilter); - } + bool ok = true; - void error1579 (Type t) - { - Report.Error (1579, loc, - "foreach statement cannot operate on variables of type `" + - t.FullName + "' because that class does not provide a " + - " GetEnumerator method or it is inaccessible"); - } + ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc); + ec.CurrentBranching.CreateSibling (); - static bool TryType (Type t, ForeachHelperMethods hm) - { - MemberList mi; - - mi = TypeContainer.FindMembers (t, MemberTypes.Method, - BindingFlags.Public | BindingFlags.NonPublic | - BindingFlags.Instance | BindingFlags.DeclaredOnly, - FilterEnumerator, hm); + variable = variable.ResolveLValue (ec, conv, loc); + if (variable == null) + ok = false; - if (mi.Count == 0) - return false; + if (!statement.Resolve (ec)) + ok = false; + + ec.EndFlowBranching (); + + return ok; + } + + protected override void DoEmit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + copy.Store (ec, expr); + + Label[] test = new Label [rank]; + Label[] loop = new Label [rank]; + + for (int i = 0; i < rank; i++) { + test [i] = ig.DefineLabel (); + loop [i] = ig.DefineLabel (); + + lengths [i].EmitThis (ec); + ((ArrayAccess) access).EmitGetLength (ec, i); + lengths [i].EmitStore (ig); + } + + for (int i = 0; i < rank; i++) { + counter [i].Initialize (ec); + + ig.Emit (OpCodes.Br, test [i]); + ig.MarkLabel (loop [i]); + } + + ((IAssignMethod) variable).EmitAssign (ec, conv, false, false); + + statement.Emit (ec); + + ig.MarkLabel (ec.LoopBegin); - hm.get_enumerator = (MethodInfo) mi [0]; - return true; + for (int i = rank - 1; i >= 0; i--){ + counter [i].Increment (ec); + + ig.MarkLabel (test [i]); + counter [i].Emit (ec); + lengths [i].Emit (ec); + ig.Emit (OpCodes.Blt, loop [i]); + } + + ig.MarkLabel (ec.LoopEnd); + } } - - // - // Looks for a usable GetEnumerator in the Type, and if found returns - // the three methods that participate: GetEnumerator, MoveNext and get_Current - // - ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t) + + protected class CollectionForeach : ExceptionStatement { - ForeachHelperMethods hm = new ForeachHelperMethods (ec); + Expression variable, expr; + Statement statement; - for (Type tt = t; tt != null && tt != TypeManager.object_type;){ - if (TryType (tt, hm)) - return hm; - tt = tt.BaseType; - } + TemporaryVariable enumerator; + Expression init; + Statement loop; - // - // Now try to find the method in the interfaces - // - while (t != null){ - Type [] ifaces = t.GetInterfaces (); + MethodGroupExpr get_enumerator; + PropertyExpr get_current; + MethodInfo move_next; + Type var_type, enumerator_type; + bool is_disposable; + + public CollectionForeach (Type var_type, Expression var, + Expression expr, Statement stmt, Location l) + { + this.var_type = var_type; + this.variable = var; + this.expr = expr; + statement = stmt; + loc = l; + } - foreach (Type i in ifaces){ - if (TryType (i, hm)) - return hm; + bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi) + { + Type [] args = TypeManager.GetArgumentTypes (mi); + if (args != null){ + if (args.Length != 0) + return false; } - + + if (TypeManager.IsOverride (mi)) + return false; + + // Check whether GetEnumerator is public + if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public) + return false; + + if ((mi.ReturnType == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type)) + // + // Apply the same optimization as MS: skip the GetEnumerator + // returning an IEnumerator, and use the one returning a + // CharEnumerator instead. This allows us to avoid the + // try-finally block and the boxing. + // + return false; + // - // Since TypeBuilder.GetInterfaces only returns the interface - // types for this type, we have to keep looping, but once - // we hit a non-TypeBuilder (ie, a Type), then we know we are - // done, because it returns all the types + // Ok, we can access it, now make sure that we can do something + // with this `GetEnumerator' // - if ((t is TypeBuilder)) - t = t.BaseType; - else - break; - } - return null; - } + Type return_type = mi.ReturnType; + if (mi.ReturnType == TypeManager.ienumerator_type || + TypeManager.ienumerator_type.IsAssignableFrom (return_type) || + (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) { + // + // If it is not an interface, lets try to find the methods ourselves. + // For example, if we have: + // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}} + // We can avoid the iface call. This is a runtime perf boost. + // even bigger if we have a ValueType, because we avoid the cost + // of boxing. + // + // We have to make sure that both methods exist for us to take + // this path. If one of the methods does not exist, we will just + // use the interface. Sadly, this complex if statement is the only + // way I could do this without a goto + // - // - // FIXME: possible optimization. - // We might be able to avoid creating `empty' if the type is the sam - // - bool EmitCollectionForeach (EmitContext ec) - { - ILGenerator ig = ec.ig; + if (return_type.IsInterface && return_type.IsGenericType) { + enumerator_type = return_type; + if (!FetchGetCurrent (ec, return_type)) + get_current = new PropertyExpr ( + ec, TypeManager.ienumerator_getcurrent, loc); + if (!FetchMoveNext (ec, return_type)) + move_next = TypeManager.bool_movenext_void; + return true; + } - enumerator = new VariableStorage (ec, hm.enumerator_type); - enumerator.EmitThis (ig); - // - // Instantiate the enumerator - // - if (expr.Type.IsValueType){ - IMemoryLocation ml = expr as IMemoryLocation; - // Load the address of the value type. - if (ml == null) { - // This happens if, for example, you have a property - // returning a struct which is IEnumerable - LocalBuilder t = ec.GetTemporaryLocal (expr.Type); - expr.Emit(ec); - ig.Emit (OpCodes.Stloc, t); - ig.Emit (OpCodes.Ldloca, t); - ec.FreeTemporaryLocal (t, expr.Type); - } else { - ml.AddressOf (ec, AddressOp.Load); + if (return_type.IsInterface || + !FetchMoveNext (ec, return_type) || + !FetchGetCurrent (ec, return_type)) { + enumerator_type = return_type; + move_next = TypeManager.bool_movenext_void; + get_current = new PropertyExpr ( + ec, TypeManager.ienumerator_getcurrent, loc); + return true; } - - // Emit the call. - if (hm.get_enumerator.DeclaringType.IsValueType) { - // the method is declared on the value type - ig.Emit (OpCodes.Call, hm.get_enumerator); } else { - // it is an interface method, so we must box - ig.Emit (OpCodes.Box, expr.Type); - ig.Emit (OpCodes.Callvirt, hm.get_enumerator); + // + // Ok, so they dont return an IEnumerable, we will have to + // find if they support the GetEnumerator pattern. + // + + if (!FetchMoveNext (ec, return_type)) + return false; + + if (!FetchGetCurrent (ec, return_type)) + return false; } - } else { - expr.Emit (ec); - ig.Emit (OpCodes.Callvirt, hm.get_enumerator); + + enumerator_type = return_type; + is_disposable = !enumerator_type.IsSealed || + TypeManager.ImplementsInterface ( + enumerator_type, TypeManager.idisposable_type); + + return true; } - enumerator.EmitStore (ig); // - // Protect the code in a try/finalize block, so that - // if the beast implement IDisposable, we get rid of it + // Retrieves a `public bool MoveNext ()' method from the Type `t' // - if (hm.is_disposable && emit_finally) - ig.BeginExceptionBlock (); - - Label end_try = ig.DefineLabel (); - - ig.MarkLabel (ec.LoopBegin); - - enumerator.EmitCall (ig, hm.move_next); - - ig.Emit (OpCodes.Brfalse, end_try); + bool FetchMoveNext (EmitContext ec, Type t) + { + MemberList move_next_list; - if (ec.InIterator) - enumerator.EmitThis (ig); - enumerator.EmitCall (ig, hm.get_current); + move_next_list = TypeContainer.FindMembers ( + t, MemberTypes.Method, + Expression.AllBindingFlags, + Type.FilterName, "MoveNext"); + if (move_next_list.Count == 0) + return false; - if (ec.InIterator){ - conv.Emit (ec); - ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder); - } else - ((IAssignMethod)variable).EmitAssign (ec, conv, false, false); + bool found = false; + foreach (MemberInfo m in move_next_list){ + MethodInfo mi = (MethodInfo) m; + Type [] args; - statement.Emit (ec); - ig.Emit (OpCodes.Br, ec.LoopBegin); - ig.MarkLabel (end_try); - - // The runtime provides this for us. - // ig.Emit (OpCodes.Leave, end); + args = TypeManager.GetArgumentTypes (mi); + if ((args != null) && (args.Length == 0) && + TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) { + move_next = mi; + if (mi.IsPublic) + return true; + found = true; + } + } + return found; + } + // - // Now the finally block + // Retrieves a `public T get_Current ()' method from the Type `t' // - if (hm.is_disposable) { - DoEmitFinally (ec); - if (emit_finally) - ig.EndExceptionBlock (); + bool FetchGetCurrent (EmitContext ec, Type t) + { + PropertyExpr pe = Expression.MemberLookup ( + ec, t, "Current", MemberTypes.Property, + Expression.AllBindingFlags, loc) as PropertyExpr; + if (pe == null) + return false; + + get_current = pe; + return true; } - ig.MarkLabel (ec.LoopEnd); - return false; - } + // + // Retrieves a `public void Dispose ()' method from the Type `t' + // + static MethodInfo FetchMethodDispose (Type t) + { + MemberList dispose_list; - public override void EmitFinally (EmitContext ec) - { - ILGenerator ig = ec.ig; + dispose_list = TypeContainer.FindMembers ( + t, MemberTypes.Method, + BindingFlags.Public | BindingFlags.Instance, + Type.FilterName, "Dispose"); + if (dispose_list.Count == 0) + return null; - if (hm.enumerator_type.IsValueType) { - enumerator.EmitThis (ig); + foreach (MemberInfo m in dispose_list){ + MethodInfo mi = (MethodInfo) m; + Type [] args; - MethodInfo mi = FetchMethodDispose (hm.enumerator_type); - if (mi != null) { - enumerator.EmitLoadAddress (ig); - ig.Emit (OpCodes.Call, mi); - } else { - enumerator.EmitLoad (ig); - ig.Emit (OpCodes.Box, hm.enumerator_type); - ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); + args = TypeManager.GetArgumentTypes (mi); + if (args != null && args.Length == 0){ + if (mi.ReturnType == TypeManager.void_type) + return mi; + } } - } else { - Label call_dispose = ig.DefineLabel (); - - enumerator.EmitThis (ig); - enumerator.EmitLoad (ig); - ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type); - ig.Emit (OpCodes.Dup); - ig.Emit (OpCodes.Brtrue_S, call_dispose); - ig.Emit (OpCodes.Pop); + return null; + } - Label end_finally = ig.DefineLabel (); - ig.Emit (OpCodes.Br, end_finally); + public void error1579 () + { + Report.Error (1579, loc, + "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible", + TypeManager.CSharpName (expr.Type)); + } - ig.MarkLabel (call_dispose); - ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); - ig.MarkLabel (end_finally); + bool TryType (EmitContext ec, Type t) + { + MethodGroupExpr mg = Expression.MemberLookup ( + ec, t, "GetEnumerator", MemberTypes.Method, + Expression.AllBindingFlags, loc) as MethodGroupExpr; + if (mg == null) + return false; - if (emit_finally) - ig.Emit (OpCodes.Endfinally); - } - } + MethodBase result = null; + MethodInfo tmp_move_next = null; + PropertyExpr tmp_get_cur = null; + Type tmp_enumerator_type = enumerator_type; + foreach (MethodInfo mi in mg.Methods) { + if (!GetEnumeratorFilter (ec, mi)) { + continue; + } - // - // FIXME: possible optimization. - // We might be able to avoid creating `empty' if the type is the sam - // - bool EmitArrayForeach (EmitContext ec) - { - int rank = array_type.GetArrayRank (); - ILGenerator ig = ec.ig; + result = mi; + tmp_move_next = move_next; + tmp_get_cur = get_current; + tmp_enumerator_type = enumerator_type; + if (mi.DeclaringType == t) + break; + } - VariableStorage copy = new VariableStorage (ec, array_type); - - // - // Make our copy of the array - // - copy.EmitThis (ig); - expr.Emit (ec); - copy.EmitStore (ig); - - if (rank == 1){ - VariableStorage counter = new VariableStorage (ec,TypeManager.int32_type); + if (result != null) { + move_next = tmp_move_next; + get_current = tmp_get_cur; + enumerator_type = tmp_enumerator_type; + MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result }; + get_enumerator = new MethodGroupExpr (mi, loc); + + if (t != expr.Type) { + expr = Convert.ExplicitConversion ( + ec, expr, t, loc); + if (expr == null) + throw new InternalErrorException (); + } - Label loop, test; + get_enumerator.InstanceExpression = expr; + get_enumerator.IsBase = t != expr.Type; - counter.EmitThis (ig); - ig.Emit (OpCodes.Ldc_I4_0); - counter.EmitStore (ig); - test = ig.DefineLabel (); - ig.Emit (OpCodes.Br, test); + return true; + } - loop = ig.DefineLabel (); - ig.MarkLabel (loop); + return false; + } - if (ec.InIterator) - ec.EmitThis (); - - copy.EmitThis (ig); - copy.EmitLoad (ig); - counter.EmitThis (ig); - counter.EmitLoad (ig); + bool ProbeCollectionType (EmitContext ec, Type t) + { + for (Type tt = t; tt != null && tt != TypeManager.object_type;){ + if (TryType (ec, tt)) + return true; + tt = tt.BaseType; + } // - // Load the value, we load the value using the underlying type, - // then we use the variable.EmitAssign to load using the proper cast. + // Now try to find the method in the interfaces // - ArrayAccess.EmitLoadOpcode (ig, element_type); - if (ec.InIterator){ - conv.Emit (ec); - ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder); - } else - ((IAssignMethod)variable).EmitAssign (ec, conv, false, false); + while (t != null){ + Type [] ifaces = t.GetInterfaces (); - statement.Emit (ec); - - ig.MarkLabel (ec.LoopBegin); - counter.EmitThis (ig); - counter.EmitThis (ig); - counter.EmitLoad (ig); - ig.Emit (OpCodes.Ldc_I4_1); - ig.Emit (OpCodes.Add); - counter.EmitStore (ig); - - ig.MarkLabel (test); - counter.EmitThis (ig); - counter.EmitLoad (ig); - copy.EmitThis (ig); - copy.EmitLoad (ig); - ig.Emit (OpCodes.Ldlen); - ig.Emit (OpCodes.Conv_I4); - ig.Emit (OpCodes.Blt, loop); - } else { - VariableStorage [] dim_len = new VariableStorage [rank]; - VariableStorage [] dim_count = new VariableStorage [rank]; - Label [] loop = new Label [rank]; - Label [] test = new Label [rank]; - int dim; + foreach (Type i in ifaces){ + if (TryType (ec, i)) + return true; + } - for (dim = 0; dim < rank; dim++){ - dim_len [dim] = new VariableStorage (ec, TypeManager.int32_type); - dim_count [dim] = new VariableStorage (ec, TypeManager.int32_type); - test [dim] = ig.DefineLabel (); - loop [dim] = ig.DefineLabel (); - } - - for (dim = 0; dim < rank; dim++){ - dim_len [dim].EmitThis (ig); - copy.EmitThis (ig); - copy.EmitLoad (ig); - IntLiteral.EmitInt (ig, dim); - ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int); - dim_len [dim].EmitStore (ig); - + // + // Since TypeBuilder.GetInterfaces only returns the interface + // types for this type, we have to keep looping, but once + // we hit a non-TypeBuilder (ie, a Type), then we know we are + // done, because it returns all the types + // + if ((t is TypeBuilder)) + t = t.BaseType; + else + break; } - for (dim = 0; dim < rank; dim++){ - dim_count [dim].EmitThis (ig); - ig.Emit (OpCodes.Ldc_I4_0); - dim_count [dim].EmitStore (ig); - ig.Emit (OpCodes.Br, test [dim]); - ig.MarkLabel (loop [dim]); + return false; + } + + public override bool Resolve (EmitContext ec) + { + enumerator_type = TypeManager.ienumerator_type; + is_disposable = true; + + if (!ProbeCollectionType (ec, expr.Type)) { + error1579 (); + return false; } - if (ec.InIterator) - ec.EmitThis (); - copy.EmitThis (ig); - copy.EmitLoad (ig); - for (dim = 0; dim < rank; dim++){ - dim_count [dim].EmitThis (ig); - dim_count [dim].EmitLoad (ig); + enumerator = new TemporaryVariable (enumerator_type, loc); + enumerator.Resolve (ec); + + init = new Invocation (get_enumerator, new ArrayList ()); + init = init.Resolve (ec); + if (init == null) + return false; + + Expression move_next_expr; + { + MemberInfo[] mi = new MemberInfo[] { move_next }; + MethodGroupExpr mg = new MethodGroupExpr (mi, loc); + mg.InstanceExpression = enumerator; + + move_next_expr = new Invocation (mg, new ArrayList ()); } + get_current.InstanceExpression = enumerator; + + Statement block = new CollectionForeachStatement ( + var_type, variable, get_current, statement, loc); + + loop = new While (move_next_expr, block, loc); + + bool ok = true; + + ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc); + ec.CurrentBranching.CreateSibling (); + + FlowBranchingException branching = null; + if (is_disposable) + branching = ec.StartFlowBranching (this); + + if (!loop.Resolve (ec)) + ok = false; + + if (is_disposable) { + ResolveFinally (branching); + ec.EndFlowBranching (); + } else + emit_finally = true; + + ec.EndFlowBranching (); + + return ok; + } + + protected override void DoEmit (EmitContext ec) + { + ILGenerator ig = ec.ig; + + enumerator.Store (ec, init); + // - // FIXME: Maybe we can cache the computation of `get'? + // Protect the code in a try/finalize block, so that + // if the beast implement IDisposable, we get rid of it // - Type [] args = new Type [rank]; - MethodInfo get; - - for (int i = 0; i < rank; i++) - args [i] = TypeManager.int32_type; - - ModuleBuilder mb = CodeGen.Module.Builder; - get = mb.GetArrayMethod ( - array_type, "Get", - CallingConventions.HasThis| CallingConventions.Standard, - var_type, args); - ig.Emit (OpCodes.Call, get); - if (ec.InIterator){ - conv.Emit (ec); - ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder); - } else - ((IAssignMethod)variable).EmitAssign (ec, conv, false, false); - statement.Emit (ec); - ig.MarkLabel (ec.LoopBegin); - for (dim = rank - 1; dim >= 0; dim--){ - dim_count [dim].EmitThis (ig); - dim_count [dim].EmitThis (ig); - dim_count [dim].EmitLoad (ig); - ig.Emit (OpCodes.Ldc_I4_1); - ig.Emit (OpCodes.Add); - dim_count [dim].EmitStore (ig); - - ig.MarkLabel (test [dim]); - dim_count [dim].EmitThis (ig); - dim_count [dim].EmitLoad (ig); - dim_len [dim].EmitThis (ig); - dim_len [dim].EmitLoad (ig); - ig.Emit (OpCodes.Blt, loop [dim]); + if (is_disposable && emit_finally) + ig.BeginExceptionBlock (); + + loop.Emit (ec); + + // + // Now the finally block + // + if (is_disposable) { + DoEmitFinally (ec); + if (emit_finally) + ig.EndExceptionBlock (); + } + } + + + public override void EmitFinally (EmitContext ec) + { + ILGenerator ig = ec.ig; + + if (enumerator_type.IsValueType) { + enumerator.Emit (ec); + + MethodInfo mi = FetchMethodDispose (enumerator_type); + if (mi != null) { + enumerator.EmitLoadAddress (ec); + ig.Emit (OpCodes.Call, mi); + } else { + enumerator.Emit (ec); + ig.Emit (OpCodes.Box, enumerator_type); + ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); + } + } else { + Label call_dispose = ig.DefineLabel (); + + enumerator.Emit (ec); + ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type); + ig.Emit (OpCodes.Dup); + ig.Emit (OpCodes.Brtrue_S, call_dispose); + ig.Emit (OpCodes.Pop); + + Label end_finally = ig.DefineLabel (); + ig.Emit (OpCodes.Br, end_finally); + + ig.MarkLabel (call_dispose); + ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); + ig.MarkLabel (end_finally); } } - ig.MarkLabel (ec.LoopEnd); - - return false; } - - protected override void DoEmit (EmitContext ec) + + protected class CollectionForeachStatement : Statement { - ILGenerator ig = ec.ig; - - Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd; - ec.LoopBegin = ig.DefineLabel (); - ec.LoopEnd = ig.DefineLabel (); - - if (hm != null) - EmitCollectionForeach (ec); - else - EmitArrayForeach (ec); - - ec.LoopBegin = old_begin; - ec.LoopEnd = old_end; + Type type; + Expression variable, current, conv; + Statement statement; + Assign assign; + + public CollectionForeachStatement (Type type, Expression variable, + Expression current, Statement statement, + Location loc) + { + this.type = type; + this.variable = variable; + this.current = current; + this.statement = statement; + this.loc = loc; + } + + public override bool Resolve (EmitContext ec) + { + current = current.Resolve (ec); + if (current == null) + return false; + + conv = Convert.ExplicitConversion (ec, current, type, loc); + if (conv == null) + return false; + + assign = new Assign (variable, conv, loc); + if (assign.Resolve (ec) == null) + return false; + + if (!statement.Resolve (ec)) + return false; + + return true; + } + + protected override void DoEmit (EmitContext ec) + { + assign.EmitStatement (ec); + statement.Emit (ec); + } } } }