* mcs/statement.cs (Foreach.ArrayForeach.Resolve): Set barrier after
[mono.git] / mcs / mcs / anonymous.cs
index 3de2649d9dc2079badf0f8c3c01b74c5d2314ee8..e4822e289163de6f252dae4892961d1a77e8f670 100644 (file)
@@ -47,21 +47,24 @@ namespace Mono.CSharp {
                
                // The emit context for the anonymous method
                public EmitContext aec;
-               public InternalParameters amp;
                protected bool unreachable;
 
+               // The method scope
+               ScopeInfo method_scope;
+               bool computed_method_scope = false;
+               
                //
                // The modifiers applied to the method, we aggregate them
                //
                protected int method_modifiers = Modifiers.PRIVATE;
                
                //
-               // During the resolve stage of the anonymous method body,
-               // we discover the actual scope where we are hosted, or
-               // null to host the method in the same class
+               // Track the scopes that this method has used.  At the
+               // end this is used to determine the ScopeInfo that will
+               // host the method
                //
-               public ScopeInfo Scope;
-
+               ArrayList scopes_used = new ArrayList ();
+               
                //
                // Points to our container anonymous method if its present
                //
@@ -77,22 +80,17 @@ namespace Mono.CSharp {
                        //
                        // The order is important: this setups the CaptureContext tree hierarchy.
                        //
+                       if (container == null) {
+                               return;
+                       }
                        container.SetHaveAnonymousMethods (l, this);
                        block.SetHaveAnonymousMethods (l, this);
                }
 
                protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
-                                             Location l)
+                                             Location l):
+                       this (parameters, container, new ToplevelBlock (container, parameters, l), l)
                {
-                       Parameters = parameters;
-                       Block = new ToplevelBlock (container, Parameters, l);
-                       loc = l;
-
-                       //
-                       // The order is important: this setups the CaptureContext tree hierarchy.
-                       //
-                       container.SetHaveAnonymousMethods (loc, this);
-                       Block.SetHaveAnonymousMethods (loc, this);
                }
 
                public override Expression DoResolve (EmitContext ec)
@@ -113,6 +111,89 @@ namespace Mono.CSharp {
                        return this;
                }
 
+               public void RegisterScope (ScopeInfo scope)
+               {
+                       if (scopes_used.Contains (scope))
+                               return;
+                       scopes_used.Add (scope);
+               }
+
+               // Returns the deepest of two scopes
+               ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
+               {
+                       ScopeInfo p;
+
+                       if (a == null)
+                               return b;
+                       if (b == null)
+                               return a;
+                       if (a == b)
+                               return a;
+
+                       //
+                       // If they Scopes are on the same CaptureContext, we do the double
+                       // checks just so if there is an invariant change in the future,
+                       // we get the exception at the end
+                       //
+                       for (p = a; p != null; p = p.ParentScope)
+                               if (p == b)
+                                       return a;
+                       
+                       for (p = b; p != null; p = p.ParentScope)
+                               if (p == a)
+                                       return b;
+
+                       CaptureContext ca = a.CaptureContext;
+                       CaptureContext cb = b.CaptureContext;
+
+                       for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
+                               if (c == cb)
+                                       return a;
+
+                       for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
+                               if (c == ca)
+                                       return b;
+                       throw new Exception ("Should never be reached");
+               }
+
+               //
+               // Determines the proper host for a method considering the
+               // scopes it references
+               //
+               public void ComputeMethodHost ()
+               {
+                       if (computed_method_scope)
+                               return;
+                       
+                       method_scope = null;
+                       int top = scopes_used.Count;
+                       computed_method_scope = true;
+
+                       if (top == 0)
+                               return;
+                       
+                       method_scope = (ScopeInfo) scopes_used [0];
+                       if (top == 1)
+                               return;
+                       
+                       for (int i = 1; i < top; i++)
+                               method_scope = Deepest (method_scope, (ScopeInfo) scopes_used [i]);
+               }
+
+               public ScopeInfo Scope {
+                       get {
+                               if (computed_method_scope)
+                                       return method_scope;
+
+                               //
+                               // This means that ComputeMethodHost is not being called, most
+                               // likely by someone who overwrote the CreateMethodHost method
+                               //
+                               throw new Exception ("Internal error, AnonymousContainer.Scope is being used before its container is computed");
+                       }
+               }
+               
+               
                protected abstract bool CreateMethodHost (EmitContext ec);
 
                public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
@@ -124,10 +205,13 @@ namespace Mono.CSharp {
 
        public class AnonymousMethod : AnonymousContainer
        {
-               public AnonymousMethod (Parameters parameters, ToplevelBlock container,
+               TypeContainer host;
+
+               public AnonymousMethod (TypeContainer host, Parameters parameters, ToplevelBlock container,
                                        ToplevelBlock block, Location l)
                        : base (parameters, container, block, l)
                {
+                       this.host = host;
                }
 
                public override bool IsIterator {
@@ -144,33 +228,34 @@ namespace Mono.CSharp {
                //
                protected override bool CreateMethodHost (EmitContext ec)
                {
+                       ComputeMethodHost ();
+
                        //
                        // Crude hack follows: we replace the TypeBuilder during the
                        // definition to get the method hosted in the right class
                        //
-                       
                        TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
-                       TypeBuilder type_host = (Scope == null ) // || Scope.ScopeTypeBuilder == null)
-                               ? current_type : Scope.ScopeTypeBuilder;
+                       TypeBuilder type_host = (Scope == null ) ? current_type : Scope.ScopeTypeBuilder;
 
                        if (current_type == null)
                                throw new Exception ("The current_type is null");
                        
                        if (type_host == null)
-                               throw new Exception (String.Format ("Type host is null, Scope is {0}", Scope == null ? "null" : "Not null"));
-                       
+                               throw new Exception (String.Format ("Type host is null, method_host is {0}", Scope == null ? "null" : "Not null"));
+
+                       if (current_type != type_host)
+                               method_modifiers = Modifiers.INTERNAL;
+
                        if (current_type == type_host && ec.IsStatic){
-                               if (ec.IsStatic){
-                                       method_modifiers |= Modifiers.STATIC;
-                               }
+                               method_modifiers |= Modifiers.STATIC;
                                current_type = null;
                        } 
 
                        method = new Method (
                                (TypeContainer) ec.TypeContainer,
                                new TypeExpression (invoke_mb.ReturnType, loc),
-                               method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++),
-                               Parameters, null, loc);
+                               method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++, loc),
+                               Parameters, null);
                        method.Block = Block;
                        
                        //
@@ -183,109 +268,90 @@ namespace Mono.CSharp {
                                ec.TypeContainer.TypeBuilder = current_type;
                        return res;
                }
-               
+
                void Error_ParameterMismatch (Type t)
                {
                        Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
-                                     "{0}' since there is a parameter mismatch", t);
+                                     "{0}' since there is a parameter mismatch", TypeManager.CSharpName (t));
+               }
+
+               public bool ImplicitStandardConversionExists (Type delegate_type)
+               {
+                       if (Parameters == null)
+                               return true;
+
+                       invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (host.TypeBuilder, delegate_type, loc);
+                       ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
+
+                       if (Parameters.Count != invoke_pd.Count)
+                               return false;
+
+                       for (int i = 0; i < Parameters.Count; ++i) {
+                               if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i))
+                                       return false;
+                       }
+                       return true;
                }
 
                //
                // Returns true if this anonymous method can be implicitly
                // converted to the delegate type `delegate_type'
                //
-               public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
+               public Expression Compatible (EmitContext ec, Type delegate_type)
                {
                        //
                        // At this point its the first time we know the return type that is 
                        // needed for the anonymous method.  We create the method here.
                        //
 
-                       invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec, delegate_type, loc);
+                       invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec.ContainerType, delegate_type, loc);
                        ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
 
-                       if (Parameters == null){
-                               int i, j;
-                               
+                       if (Parameters == null) {
                                //
                                // We provide a set of inaccessible parameters
                                //
-                               int params_idx = -1;
-                               for (i = 0; i < invoke_pd.Count; i++){
-                                       if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
-                                               params_idx = i;
+                               Parameter [] fixedpars = new Parameter [invoke_pd.Count];
+                                                               
+                               for (int i = 0; i < invoke_pd.Count; i++){
+                                       fixedpars [i] = new Parameter (
+                                               invoke_pd.ParameterType (i),
+                                               "+" + i, invoke_pd.ParameterModifier (i), null, loc);
                                }
-                               int n = invoke_pd.Count - (params_idx != -1 ? 1 : 0);
-                               Parameter [] fixedpars = new Parameter [n];
-                               
-                               for (i =  j = 0; i < invoke_pd.Count; i++){
-                                       if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
-                                               continue;
-                                       fixedpars [j] = new Parameter (
-                                               new TypeExpression (invoke_pd.ParameterType (i), loc),
-                                               "+" + j, invoke_pd.ParameterModifier (i), null, loc);
-                                       j++;
-                               }
-                               
-                               Parameter variable = null;
-                               if (params_idx != -1){
-                                       variable = new Parameter (
-                                               new TypeExpression (invoke_pd.ParameterType (params_idx), loc),
-                                               "+" + params_idx, invoke_pd.ParameterModifier (params_idx), null, loc);
-                               }
-
-                               Parameters = new Parameters (fixedpars, variable);
-                       }
-                       
-                       //
-                       // First, parameter types of `delegate_type' must be compatible
-                       // with the anonymous method.
-                       //
-                       amp = new InternalParameters (Parameters.GetParameterInfo (ec), Parameters);
-                       
-                       if (amp.Count != invoke_pd.Count){
-                               if (!probe){
+                                                               
+                               Parameters = new Parameters (fixedpars);
+                       } else {
+                               if (Parameters.Count != invoke_pd.Count) {
+                                       Report.SymbolRelatedToPreviousError (delegate_type);
                                        Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
-                                               TypeManager.CSharpName (delegate_type), amp.Count);
+                                               TypeManager.CSharpName (delegate_type), Parameters.Count.ToString ());
                                        Error_ParameterMismatch (delegate_type);
+                                       return null;
                                }
-                               return null;
-                       }
-                       
-                       for (int i = 0; i < amp.Count; i++){
-                               Parameter.Modifier amp_mod = amp.ParameterModifier (i);
 
-                               if (!probe) {
-                                       if ((amp_mod & (Parameter.Modifier.OUT | Parameter.Modifier.REF)) != 0){
-                                               Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword", 
-                                                       i+1, amp.ModifierDesc (i));
+                               for (int i = 0; i < Parameters.Count; ++i) {
+                                       Parameter.Modifier p_mod = invoke_pd.ParameterModifier (i);
+                                       if (Parameters.ParameterModifier (i) != p_mod && p_mod != Parameter.Modifier.PARAMS) {
+                                               if (p_mod == Parameter.Modifier.NONE)
+                                                       Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
+                                                               (i + 1).ToString (), Parameter.GetModifierSignature (Parameters.ParameterModifier (i)));
+                                               else
+                                                       Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
+                                                               (i+1).ToString (), Parameter.GetModifierSignature (p_mod));
                                                Error_ParameterMismatch (delegate_type);
                                                return null;
                                        }
 
-                                       if (amp_mod != invoke_pd.ParameterModifier (i)){
-                                               Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
-                                                       i+1, Parameter.GetModifierSignature (invoke_pd.ParameterModifier (i)));
-                                               Error_ParameterMismatch (delegate_type);
-                                               return null;
-                                       }
-                               
-                                       if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
+                                       if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i)) {
                                                Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
-                                                       i+1,
-                                                       TypeManager.CSharpName (amp.ParameterType (i)),
+                                                       (i+1).ToString (),
+                                                       TypeManager.CSharpName (Parameters.ParameterType (i)),
                                                        TypeManager.CSharpName (invoke_pd.ParameterType (i)));
                                                Error_ParameterMismatch (delegate_type);
                                                return null;
                                        }
                                }
                        }
-
-                       //
-                       // If we are only probing, return ourselves
-                       //
-                       if (probe)
-                               return this;
                        
                        //
                        // Second: the return type of the delegate must be compatible with 
@@ -297,9 +363,8 @@ namespace Mono.CSharp {
                        //MethodBuilder builder = method_data.MethodBuilder;
                        //ILGenerator ig = builder.GetILGenerator ();
 
-                       
-                       aec = new EmitContext (
-                               ec.TypeContainer, ec.DeclSpace, loc, null,
+                       aec = new EmitContext (ec.ResolveContext,
+                               ec.TypeContainer, ec.DeclContainer, loc, null,
                                invoke_mb.ReturnType,
                                /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
                                (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
@@ -310,15 +375,36 @@ namespace Mono.CSharp {
                        ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
                        ContainingBlock = ec.CurrentBlock;
 
-                       if (aec.ResolveTopBlock (ec, Block, amp, null, out unreachable))
+                       if (aec.ResolveTopBlock (ec, Block, Parameters, null, out unreachable))
                                return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
 
                        return null;
                }
 
+               public override Expression DoResolve (EmitContext ec)
+               {
+                       if (!ec.IsAnonymousMethodAllowed) {
+                               Report.Error (1706, loc, "Anonymous methods are not allowed in the attribute declaration");
+                               return null;
+                       }
+
+                       if (Parameters != null && !Parameters.Resolve (ec)) {
+                               return null;
+                       }
+
+                       return base.DoResolve (ec);
+               }
+
+
+               public override string ExprClassName {
+                       get {
+                               return "anonymous method";
+                       }
+               }
+
                public MethodBuilder GetMethodBuilder ()
                {
-                       return method.MethodData.MethodBuilder;
+                       return method.MethodBuilder;
                }
 
                public override string GetSignatureForError ()
@@ -336,7 +422,7 @@ namespace Mono.CSharp {
                        ILGenerator ig = builder.GetILGenerator ();
                        aec.ig = ig;
                        
-                       Parameters.LabelParameters (aec, builder);
+                       Parameters.ApplyAttributes (builder);
 
                        //
                        // Adjust based on the computed state of the
@@ -344,7 +430,7 @@ namespace Mono.CSharp {
                        
                        aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
                        
-                       aec.EmitMeta (Block, amp);
+                       aec.EmitMeta (Block);
                        aec.EmitResolvedTopBlock (Block, unreachable);
                        return true;
                }
@@ -358,14 +444,13 @@ namespace Mono.CSharp {
                                name, TypeAttributes.AutoLayout | TypeAttributes.Class |
                                TypeAttributes.NestedAssembly, TypeManager.object_type, null);
 
-                       Type [] constructor_types = TypeManager.NoTypes;
-                       Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
+                       Type [] constructor_types = Type.EmptyTypes;
                        scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
                                MethodAttributes.Public | MethodAttributes.HideBySig |
                                MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
                                CallingConventions.HasThis, constructor_types);
-                       InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
-                       TypeManager.RegisterMethod (scope.ScopeConstructor, parameter_info, constructor_types);
+
+                       TypeManager.RegisterMethod (scope.ScopeConstructor, Parameters.EmptyReadOnlyParameters);
 
                        ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
                        cig.Emit (OpCodes.Ldarg_0);
@@ -412,7 +497,7 @@ namespace Mono.CSharp {
                        if ((am.method.ModFlags & Modifiers.STATIC) == 0)
                                delegate_instance_expression = new AnonymousInstance (am);
                        
-                       Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
+                       Expression ml = Expression.MemberLookup (ec.ContainerType, type, ".ctor", loc);
                        constructor_method = ((MethodGroupExpr) ml).Methods [0];
                        delegate_method = am.GetMethodBuilder ();
                        base.Emit (ec);
@@ -490,7 +575,7 @@ namespace Mono.CSharp {
                        ScopeBlock = b;
                        id = count++;
 
-                       cc.AddScope (this);
+                       cc.RegisterCaptureContext ();
                }
 
                public void AddLocal (LocalInfo li)
@@ -552,7 +637,7 @@ namespace Mono.CSharp {
                        Console.WriteLine ("NeedThis=" + NeedThis);
                        foreach (LocalInfo li in locals){
                                Pad ();
-                               Console.WriteLine ("var {0}", li.Name);
+                               Console.WriteLine ("var {0}", MakeFieldName (li.Name));
                        }
                        
                        foreach (ScopeInfo si in children)
@@ -567,6 +652,11 @@ namespace Mono.CSharp {
                        return String.Format ("<>AnonHelp<{0}>", id);
                }
 
+               private string MakeFieldName (string local_name)
+               {
+                       return "<" + id + ":" + local_name + ">";
+               }
+
                public void EmitScopeType (EmitContext ec)
                {
                        // EmitDebug ();
@@ -596,7 +686,7 @@ namespace Mono.CSharp {
 
                        foreach (LocalInfo info in locals)
                                info.FieldBuilder = ScopeTypeBuilder.DefineField (
-                                       info.Name, info.VariableType, FieldAttributes.Assembly);
+                                       MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
 
                        if (HostsParameters){
                                Hashtable captured_parameters = CaptureContext.captured_parameters;
@@ -697,12 +787,47 @@ namespace Mono.CSharp {
                {
                        if (CaptureContext.Host.IsIterator)
                                ig.Emit (OpCodes.Ldarg_0);
-                       else
+                       else {
+                               if (scope_instance == null){
+                                       //
+                                       // This is needed if someone overwrites the Emit method
+                                       // of Statement and manually calls Block.Emit without
+                                       // this snippet first:
+                                       // 
+                                       //   ec.EmitScopeInitFromBlock (The_Block);
+                                       //   The_Block.Emit (ec);
+                                       // 
+
+                                       Console.WriteLine (
+                                               "The scope_instance has not been emitted, this typically means\n" +
+                                               "that inside the compiler someone is calling Block.Emit without\n" +
+                                               "first calling EmitScopeInitFromBlock for the block.  See compiler" +
+                                               "source code for an explanation");
+                                       throw new Exception ("Internal compiler error");
+                                       
+                               }
                                ig.Emit (OpCodes.Ldloc, scope_instance);
+                       }
                }
 
+               public static void CheckCycles (string msg, ScopeInfo s)
+               {
+                       ArrayList l = new ArrayList ();
+                       int n = 0;
+                       
+                       for (ScopeInfo p = s; p != null; p = p.ParentScope,n++){
+                               if (l.Contains (p)){
+                                       Console.WriteLine ("Loop detected {0} in {1}", n, msg);
+                                       throw new Exception ();
+                               }
+                               l.Add (p);
+                       }
+               }
+               
                static void DoPath (StringBuilder sb, ScopeInfo start)
                {
+                       CheckCycles ("print", start);
+                       
                        if (start.ParentScope != null){
                                DoPath (sb, start.ParentScope);
                                sb.Append (", ");
@@ -743,10 +868,19 @@ namespace Mono.CSharp {
                // Points to the toplevel block that owns this CaptureContext
                //
                ToplevelBlock toplevel_owner;
+
+               //
+               // All the scopes we capture
+               //
                Hashtable scopes = new Hashtable ();
+
+               //
+               // All the root scopes
+               //
+               ArrayList roots = new ArrayList ();
+               
                bool have_captured_vars = false;
                bool referenced_this = false;
-               ScopeInfo topmost = null;
 
                //
                // Captured fields
@@ -810,57 +944,14 @@ namespace Mono.CSharp {
                        }
                }
 
-               // Returns the deepest of two scopes
-               public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
+               ScopeInfo GetScopeForBlock (Block block)
                {
-                       ScopeInfo p;
-
-                       if (a == null)
-                               return b;
-                       if (b == null)
-                               return a;
-                       if (a == b)
-                               return a;
-
-                       //
-                       // If they Scopes are on the same CaptureContext, we do the double
-                       // checks just so if there is an invariant change in the future,
-                       // we get the exception at the end
-                       //
-                       for (p = a; p != null; p = p.ParentScope)
-                               if (p == b)
-                                       return a;
-                       
-                       for (p = b; p != null; p = p.ParentScope)
-                               if (p == a)
-                                       return b;
-
-                       CaptureContext ca = a.CaptureContext;
-                       CaptureContext cb = b.CaptureContext;
-
-                       for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
-                               if (c == cb)
-                                       return a;
-
-                       for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
-                               if (c == ca)
-                                       return b;
-                       throw new Exception ("Should never be reached");
-               }
-
-               void AdjustMethodScope (AnonymousContainer am, ScopeInfo scope)
-               {
-                       am.Scope = Deepest (am.Scope, scope);
-               }
-
-               void LinkScope (ScopeInfo scope, int id)
-               {
-                       ScopeInfo parent = (ScopeInfo) scopes [id];
-                       scope.ParentScope = parent;
-                       parent.AddChild (scope);
-
-                       if (scope == topmost)
-                               topmost = parent;
+                       ScopeInfo si = (ScopeInfo) scopes [block.ID];
+                       if (si != null)
+                               return si;
+                       si = new ScopeInfo (this, block);
+                       scopes [block.ID] = si;
+                       return si;
                }
                
                public void AddLocal (AnonymousContainer am, LocalInfo li)
@@ -869,48 +960,18 @@ namespace Mono.CSharp {
                                ParentCaptureContext.AddLocal (am, li);
                                return;
                        }
-                       int block_id = li.Block.ID;
-                       ScopeInfo scope;
-                       if (scopes [block_id] == null){
-                               scope = new ScopeInfo (this, li.Block);
-                               scopes [block_id] = scope;
-                       } else
-                               scope = (ScopeInfo) scopes [block_id];
-
-                       if (topmost == null){
-                               topmost = scope;
-                       } else {
-                               // Link to parent
-
-                               for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
-                                       if (scopes [b.ID] != null){
-                                               LinkScope (scope, b.ID);
-                                               break;
-                                       }
-                               }
-
-                               if (scope.ParentScope == null && ParentCaptureContext != null){
-                                       CaptureContext pcc = ParentCaptureContext;
-                                       
-                                       for (Block b = am.ContainingBlock; b != null; b = b.Parent){
-                                               if (pcc.scopes [b.ID] != null){
-                                                       pcc.LinkScope (scope, b.ID);
-                                                       break;
-                                               }
-                                       }
-                               }
-                       }
+                       ScopeInfo scope = GetScopeForBlock (li.Block);
 
                        //
                        // Adjust the owner
                        //
                        if (Host != null)
-                               AdjustMethodScope (Host, topmost);
+                               Host.RegisterScope (scope);
 
                        //
                        // Adjust the user
                        //
-                       AdjustMethodScope (am, scope);
+                       am.RegisterScope (scope);
                        
                        if (captured_variables [li] != null)
                                return;
@@ -962,60 +1023,45 @@ namespace Mono.CSharp {
                {
                        if (captured_parameters == null)
                                captured_parameters = new Hashtable ();
-                       if (captured_parameters [name] != null)
-                               return;
-                       captured_parameters [name] = new CapturedParameter (t, idx);
+                       if (captured_parameters [name] == null)
+                               captured_parameters [name] = new CapturedParameter (t, idx);
 
-                       if (topmost == null){
-                               //
-                               // Create one ScopeInfo, if there are none.
-                               //
-                               topmost = new ScopeInfo (this, toplevel_owner);
-                               scopes [toplevel_owner.ID] = topmost;
-                       } else {
-                               //
-                               // If the topmost ScopeInfo is not at the topblock level, insert
-                               // a new ScopeInfo there.
-                               //
-                               // FIXME: This code probably should be evolved to be like the code
-                               // in AddLocal
-                               //
-                               if (topmost.ScopeBlock != toplevel_owner){
-                                       ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
-                                       ScopeInfo old_top = topmost;
-                                       scopes [toplevel_owner.ID] = topmost;
-                                       topmost.ParentScope = par_si;
-                                       topmost = par_si;
-                                       topmost.AddChild (old_top);
-                               }
-                       }
-                       
-                       topmost.HostsParameters = true;
-                       AdjustMethodScope (am, topmost);
+                       ScopeInfo scope = GetScopeForBlock (toplevel_owner);
+                       scope.HostsParameters = true;
+                       am.RegisterScope (scope);
                }
 
                //
                // Captured fields are only recorded on the topmost CaptureContext, because that
                // one is the one linked to the owner of instance fields
                //
-               public void AddField (FieldExpr fe)
+               public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
                {
                        if (fe.FieldInfo.IsStatic)
                                throw new Exception ("Attempt to register a static field as a captured field");
-                       
                        CaptureContext parent = ParentCaptureContext;
-                       if (parent != null)
-                               parent.AddField (fe);
-                       else
-                               captured_fields [fe] = fe;
+                       if (parent != null) {
+                               parent.AddField (ec, am, fe);
+                               return;
+                       }
+
+                       ScopeInfo scope = GetScopeForBlock (toplevel_owner);
+                       am.RegisterScope (scope);
                }
 
-               public void CaptureThis ()
+               public void CaptureThis (AnonymousContainer am)
                {
+                       if (am == null)
+                               throw new Exception ("Internal Compiler error: Capturethis called with a null method");
                        CaptureContext parent = ParentCaptureContext;
-                       if (parent != null)
-                               parent.CaptureThis ();
+                       if (parent != null) {
+                               parent.CaptureThis (am);
+                               return;
+                       }
                        referenced_this = true;
+
+                       ScopeInfo scope = GetScopeForBlock (toplevel_owner);
+                       am.RegisterScope (scope);
                }
 
                public bool HaveCapturedVariables {
@@ -1057,37 +1103,47 @@ namespace Mono.CSharp {
 
                public void EmitAnonymousHelperClasses (EmitContext ec)
                {
-                       if (topmost != null){
-                               topmost.NeedThis = HaveCapturedFields || referenced_this;
-                               topmost.EmitScopeType (ec);
+                       if (roots.Count != 0){
+                               foreach (ScopeInfo root in roots){
+                                       //
+                                       // FIXME: We really should do this in a per-ScopeInfo
+                                       // basis, instead of having the NeedThis applied to
+                                       // all of the roots.
+                                       //
+                                       root.NeedThis = HaveCapturedFields || referenced_this;
+                                       
+                                       root.EmitScopeType (ec);
+                               }
                        } 
                }
 
                public void CloseAnonymousHelperClasses ()
                {
-                       if (topmost != null)
-                               topmost.CloseTypes ();
+                       if (roots.Count != 0)
+                               foreach (ScopeInfo root in roots)
+                                       root.CloseTypes ();
                }
 
                public void EmitInitScope (EmitContext ec)
                {
                        EmitAnonymousHelperClasses (ec);
-                       if (topmost != null)
-                               topmost.EmitInitScope (ec);
-               }
+                       if (roots.Count != 0)
+                               foreach (ScopeInfo root in roots)
+                                       root.EmitInitScope (ec);                }
 
-               ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
+               //
+               // This is called externally when we start emitting code for a block
+               // if the block has a ScopeInfo associated, emit the init code
+               //
+               public void EmitScopeInitFromBlock (EmitContext ec, Block b)
                {
-                       ScopeInfo si;
-                       
-                       si = (ScopeInfo) scopes [b.ID];
+                       ScopeInfo si = (ScopeInfo) scopes [b.ID];
                        if (si == null)
-                               throw new Exception ("Si is null for block " + b.ID);
-                       si.EmitInitScope (ec);
+                               return;
 
-                       return si;
+                       si.EmitInitScope (ec);
                }
-
+               
                //
                // Emits the opcodes necessary to load the instance of the captured
                // variable in `li'
@@ -1099,7 +1155,7 @@ namespace Mono.CSharp {
                        ScopeInfo si;
 
                        if (li.Block.Toplevel == toplevel_owner){
-                               si = GetScopeFromBlock (ec, li.Block);
+                               si = (ScopeInfo) scopes [li.Block.ID];
                                si.EmitScopeInstance (ig);
                                return;
                        }
@@ -1153,7 +1209,7 @@ namespace Mono.CSharp {
                        ScopeInfo si;
 
                        if (ec.CurrentBlock.Toplevel == toplevel_owner) {
-                               si = GetScopeFromBlock (ec, toplevel_owner);
+                               si = (ScopeInfo) scopes [toplevel_owner.ID];
                                si.EmitScopeInstance (ig);
                        } else {
                                si = ec.CurrentAnonymousMethod.Scope;
@@ -1172,14 +1228,15 @@ namespace Mono.CSharp {
                // Emits the code necessary to load the parameter named `name' within
                // an anonymous method.
                //
-               public void EmitParameter (EmitContext ec, string name)
+               public void EmitParameter (EmitContext ec, string name, bool leave_copy, bool prepared, ref LocalTemporary temp)
                {
                        CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
                        if (cc != this){
-                               cc.EmitParameter (ec, name);
+                               cc.EmitParameter (ec, name, leave_copy, prepared, ref temp);
                                return;
                        }
-                       EmitParameterInstance (ec, name);
+                       if (!prepared)
+                               EmitParameterInstance (ec, name);
                        CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
                        if (par_info != null){
                                // 
@@ -1187,27 +1244,40 @@ namespace Mono.CSharp {
                                //
                        }
                        ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
+
+                       if (leave_copy){
+                               ec.ig.Emit (OpCodes.Dup);
+                               temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
+                               temp.Store (ec);
+                       }
                }
 
                //
                // Implements the assignment of `source' to the paramenter named `name' within
                // an anonymous method.
                //
-               public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
+               public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load, ref LocalTemporary temp)
                {
                        CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
                        if (cc != this){
-                               cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
+                               cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load, ref temp);
                                return;
                        }
                        ILGenerator ig = ec.ig;
                        CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
 
                        EmitParameterInstance (ec, name);
+                       if (prepare_for_load)
+                               ig.Emit (OpCodes.Dup);
                        source.Emit (ec);
-                       if (leave_copy)
+                       if (leave_copy){
                                ig.Emit (OpCodes.Dup);
+                               temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
+                               temp.Store (ec);
+                       }
                        ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
+                       if (temp != null)
+                               temp.Emit (ec);
                }
 
                //
@@ -1235,7 +1305,9 @@ namespace Mono.CSharp {
                        ILGenerator ig = target.ig;
                        ScopeInfo si = am.Scope;
 
-                       if (si == null){
+                       AnonymousContainer container = am.ContainerAnonymousMethod;
+
+                       if ((si == null) || ((container != null) && (si == container.Scope))) {
                                ig.Emit (OpCodes.Ldarg_0);
                                return;
                        }
@@ -1244,39 +1316,136 @@ namespace Mono.CSharp {
                        si.EmitScopeInstance (ig);
                }
 
-               ArrayList all_scopes = new ArrayList ();
-               
-               public void AddScope (ScopeInfo si)
+               public void RegisterCaptureContext ()
                {
-                       all_scopes.Add (si);
                        toplevel_owner.RegisterCaptureContext (this);
                }
 
                //
-               // Links any scopes that were not linked previously
+               // Returs true if `probe' is an ancestor of `scope' in the 
+               // scope chain
                //
-               public void AdjustScopes ()
+               bool IsAncestor (ScopeInfo probe, ScopeInfo scope)
                {
-                       foreach (ScopeInfo scope in all_scopes){
-                               if (scope.ParentScope != null)
-                                       continue;
+                       for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
+                               if (probe.ScopeBlock == b)
+                                       return true;
+                       }
+                       return false;
+               }
 
-                               for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
-                                       if (scopes [b.ID] != null){
-                                               LinkScope (scope, b.ID);
-                                               break;
+               //
+               // Returns an ArrayList of ScopeInfos that enumerates all the ancestors
+               // of `scope' found in `scope_list'.
+               //
+               // The value returned is either a ScopeInfo or an Arraylist of ScopeInfos
+               //
+               object GetAncestorScopes (ScopeInfo scope, ScopeInfo [] scope_list)
+               {
+                       object ancestors = null;
+                       
+                       for (int i = 0; i < scope_list.Length; i++){
+                               // Ignore the same scope
+                               if (scope_list [i] == scope)
+                                       continue;
+                               
+                               if (IsAncestor (scope_list [i], scope)){
+                                       if (ancestors == null){
+                                               ancestors = scope_list [i];
+                                               continue;
                                        }
+                                       
+                                       if (ancestors is ScopeInfo){
+                                               object old = ancestors;
+                                               ancestors = new ArrayList (4);
+                                               ((ArrayList)ancestors).Add (old);
+                                       } 
+                                       
+                                       ((ArrayList)ancestors).Add (scope_list [i]);
+                               }
+                       }
+                       return ancestors;
+               }
+
+               //
+               // Returns the immediate parent of `scope' from all the captured
+               // scopes found in `scope_list', or null if this is a toplevel scope.
+               //
+               ScopeInfo GetParentScope (ScopeInfo scope, ScopeInfo [] scope_list)
+               {
+                       object ancestors = GetAncestorScopes (scope, scope_list);
+                       if (ancestors == null)
+                               return null;
+
+                       // Single match, thats the parent.
+                       if (ancestors is ScopeInfo)
+                               return (ScopeInfo) ancestors;
+
+                       ArrayList candidates = (ArrayList) ancestors;
+                       ScopeInfo parent = (ScopeInfo) candidates [0];
+                       for (int i = 1; i < candidates.Count; i++){
+                               if (IsAncestor (parent, (ScopeInfo) candidates [i]))
+                                       parent = (ScopeInfo) candidates [i];
+                       }
+                       return parent;
+               }
+               
+               //
+               // Links all the scopes
+               //
+               bool linked;
+               public void LinkScopes ()
+               {
+                       if (linked)
+                               return;
+                       
+                       linked = true;
+                       if (ParentCaptureContext != null)
+                               ParentCaptureContext.LinkScopes ();
+
+                       int scope_count = scopes.Keys.Count;
+                       ScopeInfo [] scope_list = new ScopeInfo [scope_count];
+                       scopes.Values.CopyTo (scope_list, 0);
+
+                       for (int i = 0; i < scope_count; i++){
+                               ScopeInfo parent = GetParentScope (scope_list [i], scope_list);
+
+                               if (parent == null){
+                                       roots.Add (scope_list [i]);
+                                       continue;
                                }
 
-                               if (scope.ParentScope == null && ParentCaptureContext != null){
-                                       CaptureContext pcc = ParentCaptureContext;
+                               scope_list [i].ParentScope = parent;
+                               parent.AddChild (scope_list [i]);
+                       }
+
+                       //
+                       // Link the roots to their parent containers if any.
+                       //
+                       if (ParentCaptureContext != null && roots.Count != 0){
+                               ScopeInfo one_root = (ScopeInfo) roots [0];
+                               bool found = false;
+                               
+                               foreach (ScopeInfo a_parent_root in ParentCaptureContext.roots){
+                                       if (!IsAncestor (a_parent_root, one_root))
+                                               continue;
+
+                                       found = true;
                                        
-                                       for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
-                                               if (pcc.scopes [b.ID] != null){
-                                                       pcc.LinkScope (scope, b.ID);
-                                                       break;
-                                               }
+                                       // Found, link all the roots to this root
+                                       foreach (ScopeInfo root in roots){
+                                               root.ParentScope = a_parent_root;
+                                               a_parent_root.AddChild (root);
                                        }
+                                       break;
+                               }
+                               if (!found){
+                                       //
+                                       // This is to catch a condition in which it is
+                                       // not possible to determine the containing ScopeInfo
+                                       // from an encapsulating CaptureContext
+                                       //
+                                       throw new Exception ("Internal compiler error: Did not find the parent for the root in the chain");
                                }
                        }
                }