Emit correct sequence info for iterators
[mono.git] / mcs / mcs / async.cs
index 3531bf17d858da029a5e6b48eee6c76dde52fe35..138ed2f99bb2e5181dd752a34bd348a967419e7d 100644 (file)
@@ -1,4 +1,4 @@
-//
+//
 // async.cs: Asynchronous functions
 //
 // Author:
@@ -7,6 +7,7 @@
 // Dual licensed under the terms of the MIT X11 or GNU GPL
 //
 // Copyright 2011 Novell, Inc.
+// Copyright 2011 Xamarin Inc.
 //
 
 using System;
@@ -22,7 +23,7 @@ using System.Reflection.Emit;
 
 namespace Mono.CSharp
 {
-       class Await : ExpressionStatement
+       public class Await : ExpressionStatement
        {
                Expression expr;
                AwaitStatement stmt;
@@ -33,6 +34,12 @@ namespace Mono.CSharp
                        this.loc = loc;
                }
 
+               public Expression Expr {
+                       get {
+                               return expr;
+                       }
+               }
+
                protected override void CloneTo (CloneContext clonectx, Expression target)
                {
                        var t = (Await) target;
@@ -52,16 +59,6 @@ namespace Mono.CSharp
 
                protected override Expression DoResolve (ResolveContext rc)
                {
-                       if (rc.HasSet (ResolveContext.Options.FinallyScope)) {
-                               rc.Report.Error (1984, loc,
-                                       "The `await' operator cannot be used in the body of a finally clause");
-                       }
-
-                       if (rc.HasSet (ResolveContext.Options.CatchScope)) {
-                               rc.Report.Error (1985, loc,
-                                       "The `await' operator cannot be used in the body of a catch clause");
-                       }
-
                        if (rc.HasSet (ResolveContext.Options.LockScope)) {
                                rc.Report.Error (1996, loc,
                                        "The `await' operator cannot be used in the body of a lock statement");
@@ -73,18 +70,12 @@ namespace Mono.CSharp
                        }
 
                        if (rc.IsUnsafe) {
-                               // TODO: New error code
-                               rc.Report.Error (-1900, loc,
+                               rc.Report.Error (4004, loc,
                                        "The `await' operator cannot be used in an unsafe context");
                        }
 
                        var bc = (BlockContext) rc;
 
-                       if (!bc.CurrentBlock.ParametersBlock.IsAsync) {
-                               // TODO: Should check for existence of await type but
-                               // what to do with it
-                       }
-
                        stmt = new AwaitStatement (expr, loc);
                        if (!stmt.Resolve (bc))
                                return null;
@@ -117,6 +108,11 @@ namespace Mono.CSharp
                {
                        stmt.EmitStatement (ec);
                }
+
+               public override object Accept (StructuralVisitor visitor)
+               {
+                       return visitor.Visit (this);
+               }
        }
 
        class AwaitStatement : YieldStatement<AsyncInitializer>
@@ -130,20 +126,36 @@ namespace Mono.CSharp
 
                        protected override void Error_TypeDoesNotContainDefinition (ResolveContext rc, TypeSpec type, string name)
                        {
-                               Error_WrongGetAwaiter (rc, loc, type);
+                               Error_OperatorCannotBeApplied (rc, type);
                        }
 
                        protected override void Error_OperatorCannotBeApplied (ResolveContext rc, TypeSpec type)
                        {
-                               rc.Report.Error (1991, loc, "Cannot await `{0}' expression", type.GetSignatureForError ());
+                               rc.Report.Error (4001, loc, "Cannot await `{0}' expression", type.GetSignatureForError ());
+                       }
+               }
+
+               sealed class GetResultInvocation : Invocation
+               {
+                       public GetResultInvocation (MethodGroupExpr mge, Arguments arguments)
+                               : base (null, arguments)
+                       {
+                               mg = mge;
+                               type = mg.BestCandidateReturnType;
+                       }
+
+                       public override Expression EmitToField (EmitContext ec)
+                       {
+                               return this;
                        }
                }
 
                Field awaiter;
-               PropertyExpr is_completed;
+               PropertySpec is_completed;
                MethodSpec on_completed;
                MethodSpec get_result;
                TypeSpec type;
+               TypeSpec result_type;
 
                public AwaitStatement (Expression expr, Location loc)
                        : base (expr, loc)
@@ -152,6 +164,12 @@ namespace Mono.CSharp
 
                #region Properties
 
+               bool IsDynamic {
+                       get {
+                               return is_completed == null;
+                       }
+               }
+
                public TypeSpec Type {
                        get {
                                return type;
@@ -160,7 +178,7 @@ namespace Mono.CSharp
 
                public TypeSpec ResultType {
                        get {
-                               return get_result.ReturnType;
+                               return result_type;
                        }
                }
 
@@ -171,7 +189,7 @@ namespace Mono.CSharp
                        GetResultExpression (ec).Emit (ec);
                }
 
-               public Invocation GetResultExpression (EmitContext ec)
+               public Expression GetResultExpression (EmitContext ec)
                {
                        var fe_awaiter = new FieldExpr (awaiter, loc);
                        fe_awaiter.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
@@ -179,10 +197,15 @@ namespace Mono.CSharp
                        //
                        // result = awaiter.GetResult ();
                        //
-                       var mg_result = MethodGroupExpr.CreatePredefined (get_result, fe_awaiter.Type, loc);
-                       mg_result.InstanceExpression = fe_awaiter;
+                       if (IsDynamic) {
+                               var rc = new ResolveContext (ec.MemberContext);
+                               return new Invocation (new MemberAccess (fe_awaiter, "GetResult"), new Arguments (0)).Resolve (rc);
+                       } else {
+                               var mg_result = MethodGroupExpr.CreatePredefined (get_result, fe_awaiter.Type, loc);
+                               mg_result.InstanceExpression = fe_awaiter;
 
-                       return Invocation.CreatePredefined (mg_result, new Arguments (0));
+                               return new GetResultInvocation (mg_result, new Arguments (0));
+                       }
                }
 
                public void EmitPrologue (EmitContext ec)
@@ -197,79 +220,60 @@ namespace Mono.CSharp
 
                        Label skip_continuation = ec.DefineLabel ();
 
-                       is_completed.InstanceExpression = fe_awaiter;
-                       is_completed.EmitBranchable (ec, skip_continuation, true);
+                       Expression completed_expr;
+                       if (IsDynamic) {
+                               var rc = new ResolveContext (ec.MemberContext);
+
+                               Arguments dargs = new Arguments (1);
+                               dargs.Add (new Argument (fe_awaiter));
+                               completed_expr = new DynamicMemberBinder ("IsCompleted", dargs, loc).Resolve (rc);
+                       } else {
+                               var pe = PropertyExpr.CreatePredefined (is_completed, loc);
+                               pe.InstanceExpression = fe_awaiter;
+                               completed_expr = pe;
+                       }
+
+                       completed_expr.EmitBranchable (ec, skip_continuation, true);
 
                        base.DoEmit (ec);
 
-                       FieldSpec[] stack_fields = null;
-                       TypeSpec[] stack = null;
-                       //
-                       // Here is the clever bit. We know that await statement has to yield the control
-                       // back but it can appear inside almost any expression. This means the stack can
-                       // contain any depth of values and same values have to be present when the continuation
-                       // handles control back.
-                       //
-                       // For example: await a + await b
                        //
-                       // In this case we fabricate a static stack forwarding method which moves the values
-                       // from the stack to async storey fields. On re-entry point we restore exactly same
-                       // stack using these fields.
+                       // The stack has to be empty before calling await continuation. We handle this
+                       // by lifting values which would be left on stack into class fields. The process
+                       // is quite complicated and quite hard to test because any expression can possibly
+                       // leave a value on the stack.
                        //
-                       // We fabricate a static method because we don't want to touch original stack and
-                       // the instance method would require `this' as the first stack value on the stack
+                       // Following assert fails when some of expression called before is missing EmitToField
+                       // or parent expression fails to find await in children expressions
                        //
-                       if (ec.StackHeight > 0) {
-                               var async_storey = (AsyncTaskStorey) machine_initializer.Storey;
-
-                               stack = ec.GetStackTypes ();
-                               bool explicit_this;
-                               var method = async_storey.GetStackForwarder (stack, out stack_fields, out explicit_this);
-                               if (explicit_this)
-                                       ec.EmitThis ();
-
-                               ec.Emit (OpCodes.Call, method);
-                       }
+                       ec.AssertEmptyStack ();
 
-                       var mg_completed = MethodGroupExpr.CreatePredefined (on_completed, fe_awaiter.Type, loc);
-                       mg_completed.InstanceExpression = fe_awaiter;
+                       var storey = (AsyncTaskStorey) machine_initializer.Storey;
+                       var cont_field = storey.EmitContinuationInitialization (ec);
 
                        var args = new Arguments (1);
-                       var storey = (AsyncTaskStorey) machine_initializer.Storey;
-                       var fe_cont = new FieldExpr (storey.Continuation, loc);
-                       fe_cont.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
+                       args.Add (new Argument (cont_field));
 
-                       args.Add (new Argument (fe_cont));
+                       if (IsDynamic) {
+                               var rc = new ResolveContext (ec.MemberContext);
+                               var mg_expr = new Invocation (new MemberAccess (fe_awaiter, "OnCompleted"), args).Resolve (rc);
 
-                       //
-                       // awaiter.OnCompleted (continuation);
-                       //
-                       mg_completed.EmitCall (ec, args);
+                               ExpressionStatement es = (ExpressionStatement) mg_expr;
+                               es.EmitStatement (ec);
+                       } else {
+                               var mg_completed = MethodGroupExpr.CreatePredefined (on_completed, fe_awaiter.Type, loc);
+                               mg_completed.InstanceExpression = fe_awaiter;
+
+                               //
+                               // awaiter.OnCompleted (continuation);
+                               //
+                               mg_completed.EmitCall (ec, args);
+                       }
 
                        // Return ok
                        machine_initializer.EmitLeave (ec, unwind_protect);
 
                        ec.MarkLabel (resume_point);
-
-                       if (stack_fields != null) {
-                               for (int i = 0; i < stack_fields.Length; ++i) {
-                                       ec.EmitThis ();
-
-                                       var field = stack_fields[i];
-
-                                       //
-                                       // We don't store `this' because it can be easily re-created
-                                       //
-                                       if (field == null)
-                                               continue;
-
-                                       if (stack[i] is ReferenceContainer)
-                                               ec.Emit (OpCodes.Ldflda, field);
-                                       else
-                                               ec.Emit (OpCodes.Ldfld, field);
-                               }
-                       }
-
                        ec.MarkLabel (skip_continuation);
                }
 
@@ -288,16 +292,9 @@ namespace Mono.CSharp
                        }
                }
 
-               static void Error_WrongGetAwaiter (ResolveContext rc, Location loc, TypeSpec type)
-               {
-                       rc.Report.Error (1986, loc,
-                               "The `await' operand type `{0}' must have suitable GetAwaiter method",
-                               type.GetSignatureForError ());
-               }
-
                void Error_WrongAwaiterPattern (ResolveContext rc, TypeSpec awaiter)
                {
-                       rc.Report.Error (1999, loc, "The awaiter type `{0}' must have suitable IsCompleted, OnCompleted, and GetResult members",
+                       rc.Report.Error (4011, loc, "The awaiter type `{0}' must have suitable IsCompleted, OnCompleted, and GetResult members",
                                awaiter.GetSignatureForError ());
                }
 
@@ -306,13 +303,21 @@ namespace Mono.CSharp
                        if (!base.Resolve (bc))
                                return false;
 
+                       Arguments args = new Arguments (0);
+
                        type = expr.Type;
 
                        //
-                       // The task result is of dynamic type
+                       // The await expression is of dynamic type
                        //
-                       if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
-                               throw new NotImplementedException ("dynamic await");
+                       if (type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
+                               result_type = type;
+
+                               awaiter = ((AsyncTaskStorey) machine_initializer.Storey).AddAwaiter (type, loc);
+
+                               expr = new Invocation (new MemberAccess (expr, "GetAwaiter"), args).Resolve (bc);
+                               return true;
+                       }
 
                        //
                        // Check whether the expression is awaitable
@@ -321,40 +326,31 @@ namespace Mono.CSharp
                        if (ama == null)
                                return false;
 
-                       Arguments args = new Arguments (0);
-
                        var errors_printer = new SessionReportPrinter ();
                        var old = bc.Report.SetPrinter (errors_printer);
                        ama = new Invocation (ama, args).Resolve (bc);
+                       bc.Report.SetPrinter (old);
 
                        if (errors_printer.ErrorsCount > 0 || !MemberAccess.IsValidDotExpression (ama.Type)) {
-                               bc.Report.SetPrinter (old);
-                               Error_WrongGetAwaiter (bc, loc, expr.Type);
+                               bc.Report.Error (1986, expr.Location,
+                                       "The `await' operand type `{0}' must have suitable GetAwaiter method",
+                                       expr.Type.GetSignatureForError ());
+
                                return false;
                        }
 
                        var awaiter_type = ama.Type;
                        awaiter = ((AsyncTaskStorey) machine_initializer.Storey).AddAwaiter (awaiter_type, loc);
+
                        expr = ama;
 
                        //
                        // Predefined: bool IsCompleted { get; } 
                        //
-                       var is_completed_ma = new MemberAccess (expr, "IsCompleted").Resolve (bc);
-                       if (is_completed_ma != null) {
-                               is_completed = is_completed_ma as PropertyExpr;
-                               if (is_completed != null && is_completed.Type.BuiltinType == BuiltinTypeSpec.Type.Bool && is_completed.IsInstance && is_completed.Getter != null) {
-                                       // valid
-                               } else {
-                                       bc.Report.SetPrinter (old);
-                                       Error_WrongAwaiterPattern (bc, awaiter_type);
-                                       return false;
-                               }
-                       }
+                       is_completed = MemberCache.FindMember (awaiter_type, MemberFilter.Property ("IsCompleted", bc.Module.Compiler.BuiltinTypes.Bool),
+                               BindingRestriction.InstanceOnly) as PropertySpec;
 
-                       bc.Report.SetPrinter (old);
-
-                       if (errors_printer.ErrorsCount > 0) {
+                       if (is_completed == null || !is_completed.HasGet) {
                                Error_WrongAwaiterPattern (bc, awaiter_type);
                                return false;
                        }
@@ -387,6 +383,8 @@ namespace Mono.CSharp
                                return false;
                        }
 
+                       result_type = get_result.ReturnType;
+
                        return true;
                }
        }
@@ -395,7 +393,7 @@ namespace Mono.CSharp
        {
                TypeInferenceContext return_inference;
 
-               public AsyncInitializer (ParametersBlock block, TypeContainer host, TypeSpec returnType)
+               public AsyncInitializer (ParametersBlock block, TypeDefinition host, TypeSpec returnType)
                        : base (block, host, returnType)
                {
                }
@@ -428,14 +426,8 @@ namespace Mono.CSharp
 
                #endregion
 
-               public static void Create (ParametersBlock block, ParametersCompiled parameters, TypeContainer host, TypeSpec returnType, Location loc)
+               public static void Create (IMemberContext context, ParametersBlock block, ParametersCompiled parameters, TypeDefinition host, TypeSpec returnType, Location loc)
                {
-                       if (returnType != null && returnType.Kind != MemberKind.Void &&
-                               returnType != host.Module.PredefinedTypes.Task.TypeSpec &&
-                               !returnType.IsGenericTask) {
-                               host.Compiler.Report.Error (1983, loc, "The return type of an async method must be void, Task, or Task<T>");
-                       }
-
                        for (int i = 0; i < parameters.Count; i++) {
                                Parameter p = parameters[i];
                                Parameter.Modifier mod = p.ModFlags;
@@ -445,26 +437,25 @@ namespace Mono.CSharp
                                        return;
                                }
 
-                               // TODO:
                                if (p is ArglistParameter) {
-                                       host.Compiler.Report.Error (1636, p.Location,
-                                               "__arglist is not allowed in parameter list of iterators");
+                                       host.Compiler.Report.Error (4006, p.Location,
+                                               "__arglist is not allowed in parameter list of async methods");
                                        return;
                                }
 
-                               // TODO:
                                if (parameters.Types[i].IsPointer) {
-                                       host.Compiler.Report.Error (1637, p.Location,
-                                               "Iterators cannot have unsafe parameters or yield types");
+                                       host.Compiler.Report.Error (4005, p.Location,
+                                               "Async methods cannot have unsafe parameters");
                                        return;
                                }
                        }
 
-                       // TODO: Warning
-                       //if (!block.HasAwait) {
-                       //}
+                       if (!block.HasAwait) {
+                               host.Compiler.Report.Warning (1998, 1, loc,
+                                       "Async block lacks `await' operator and will run synchronously");
+                       }
 
-                       block.WrapIntoAsyncTask (host, returnType);
+                       block.WrapIntoAsyncTask (context, host, returnType);
                }
 
                protected override BlockContext CreateBlockContext (ResolveContext rc)
@@ -474,6 +465,7 @@ namespace Mono.CSharp
                        if (lambda != null)
                                return_inference = lambda.ReturnTypeInference;
 
+                       ctx.StartFlowBranching (this, rc.CurrentBranching);
                        return ctx;
                }
 
@@ -497,17 +489,31 @@ namespace Mono.CSharp
                {
                        var storey = (AsyncTaskStorey) Storey;
                        storey.Instance.Emit (ec);
-                       ec.Emit (OpCodes.Call, storey.StateMachineMethod.Spec);
 
+                       var move_next_entry = storey.StateMachineMethod.Spec;
+                       if (storey.MemberName.Arity > 0) {
+                               move_next_entry = MemberCache.GetMember (storey.Instance.Type, move_next_entry);
+                       }
+
+                       ec.Emit (OpCodes.Call, move_next_entry);
+
+                       //
+                       // Emits return <async-storey-instance>.$builder.Task;
+                       //
                        if (storey.Task != null) {
-                               //
-                               // async.$builder.Task;
-                               //
+                               var builder_field = storey.Builder.Spec;
+                               var task_get = storey.Task.Get;
+
+                               if (storey.MemberName.Arity > 0) {
+                                       builder_field = MemberCache.GetMember (storey.Instance.Type, builder_field);
+                                       task_get = MemberCache.GetMember (builder_field.MemberType, task_get);
+                               }
+
                                var pe_task = new PropertyExpr (storey.Task, loc) {
-                                       InstanceExpression = new FieldExpr (storey.Builder, loc) {
+                                       InstanceExpression = new FieldExpr (builder_field, loc) {
                                                InstanceExpression = storey.Instance
                                        },
-                                       Getter = storey.Task.Get
+                                       Getter = task_get
                                };
 
                                pe_task.Emit (ec);
@@ -519,52 +525,19 @@ namespace Mono.CSharp
 
        class AsyncTaskStorey : StateMachine
        {
-               sealed class ParametersLoadStatement : Statement
-               {
-                       readonly FieldSpec[] fields;
-                       readonly TypeSpec[] parametersTypes;
-                       readonly int thisParameterIndex;
-
-                       public ParametersLoadStatement (FieldSpec[] fields, TypeSpec[] parametersTypes, int thisParameterIndex)
-                       {
-                               this.fields = fields;
-                               this.parametersTypes = parametersTypes;
-                               this.thisParameterIndex = thisParameterIndex;
-                       }
-
-                       protected override void CloneTo (CloneContext clonectx, Statement target)
-                       {
-                               throw new NotImplementedException ();
-                       }
-
-                       protected override void DoEmit (EmitContext ec)
-                       {
-                               for (int i = 0; i < fields.Length; ++i) {
-                                       var field = fields[i];
-                                       if (field == null)
-                                               continue;
-
-                                       ec.EmitArgumentLoad (thisParameterIndex);
-                                       ec.EmitArgumentLoad (i);
-                                       if (parametersTypes[i] is ReferenceContainer)
-                                               ec.EmitLoadFromPtr (field.MemberType);
-
-                                       ec.Emit (OpCodes.Stfld, field);
-                               }
-                       }
-               }
-
                int awaiters;
                Field builder, continuation;
                readonly TypeSpec return_type;
                MethodSpec set_result;
+               MethodSpec set_exception;
                PropertySpec task;
                LocalVariable hoisted_return;
-               Dictionary<TypeSpec[], Tuple<MethodSpec, FieldSpec[], bool>> stack_forwarders;
-               List<FieldSpec> hoisted_stack_slots;
+               int locals_captured;
+               Dictionary<TypeSpec, List<StackField>> stack_fields;
+               TypeSpec action;
 
-               public AsyncTaskStorey (AsyncInitializer initializer, TypeSpec type)
-                       : base (initializer.OriginalBlock, initializer.Host, null, null, "async")
+               public AsyncTaskStorey (IMemberContext context, AsyncInitializer initializer, TypeSpec type)
+                       : base (initializer.OriginalBlock, initializer.Host, context.CurrentMemberDefinition as MemberBase, context.CurrentTypeParameters, "async")
                {
                        return_type = type;
                }
@@ -577,12 +550,6 @@ namespace Mono.CSharp
                        }
                }
 
-               public Field Continuation {
-                       get {
-                               return continuation;
-                       }
-               }
-
                public LocalVariable HoistedReturn {
                        get {
                                return hoisted_return;
@@ -605,50 +572,50 @@ namespace Mono.CSharp
 
                public Field AddAwaiter (TypeSpec type, Location loc)
                {
-                       return AddCompilerGeneratedField ("$awaiter" + awaiters++.ToString ("X"), new TypeExpression (type, loc));
+                       return AddCapturedVariable ("$awaiter" + awaiters++.ToString ("X"), type);
                }
 
-               int locals_captured;
-
-               public Field AddCapturedLocalVariable (TypeSpec type)
+               public StackField AddCapturedLocalVariable (TypeSpec type)
                {
-                       var field = AddCompilerGeneratedField ("<s>$" + locals_captured++.ToString ("X"), new TypeExpression (type, Location));
-                       field.Define ();
+                       if (mutator != null)
+                               type = mutator.Mutate (type);
 
-                       return field;
-               }
-
-               FieldSpec CreateStackValueField (TypeSpec type, BitArray usedFields)
-               {
-                       if (hoisted_stack_slots == null) {
-                               hoisted_stack_slots = new List<FieldSpec> ();
-                       } else {
-                               for (int i = 0; i < usedFields.Count; ++i) {
-                                       if (hoisted_stack_slots[i].MemberType == type && !usedFields[i]) {
-                                               usedFields.Set (i, true);
-                                               return hoisted_stack_slots[i];
+                       List<StackField> existing_fields = null;
+                       if (stack_fields == null) {
+                               stack_fields = new Dictionary<TypeSpec, List<StackField>> ();
+                       } else if (stack_fields.TryGetValue (type, out existing_fields)) {
+                               foreach (var f in existing_fields) {
+                                       if (f.CanBeReused) {
+                                               f.CanBeReused = false;
+                                               return f;
                                        }
                                }
                        }
 
-                       var field = AddCompilerGeneratedField ("<s>$" + hoisted_stack_slots.Count.ToString ("X"), new TypeExpression (type, Location));
+                       const Modifiers mod = Modifiers.COMPILER_GENERATED | Modifiers.PRIVATE;
+                       var field = new StackField (this, new TypeExpression (type, Location), mod, new MemberName ("<s>$" + locals_captured++.ToString ("X"), Location));
+                       AddField (field);
+
                        field.Define ();
 
-                       hoisted_stack_slots.Add (field.Spec);
-                       return field.Spec;
+                       if (existing_fields == null) {
+                               existing_fields = new List<StackField> ();
+                               stack_fields.Add (type, existing_fields);
+                       }
+
+                       existing_fields.Add (field);
+
+                       return field;
                }
 
                protected override bool DoDefineMembers ()
                {
-                       var action = Module.PredefinedTypes.Action.Resolve ();
-                       if (action != null) {
-                               continuation = AddCompilerGeneratedField ("$continuation", new TypeExpression (action, Location));
-                               continuation.ModFlags |= Modifiers.READONLY;
-                       }
+                       action = Module.PredefinedTypes.Action.Resolve ();
 
                        PredefinedType builder_type;
                        PredefinedMember<MethodSpec> bf;
                        PredefinedMember<MethodSpec> sr;
+                       PredefinedMember<MethodSpec> se;
                        bool has_task_return_type = false;
                        var pred_members = Module.PredefinedMembers;
 
@@ -656,67 +623,64 @@ namespace Mono.CSharp
                                builder_type = Module.PredefinedTypes.AsyncVoidMethodBuilder;
                                bf = pred_members.AsyncVoidMethodBuilderCreate;
                                sr = pred_members.AsyncVoidMethodBuilderSetResult;
+                               se = pred_members.AsyncVoidMethodBuilderSetException;
                        } else if (return_type == Module.PredefinedTypes.Task.TypeSpec) {
                                builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilder;
                                bf = pred_members.AsyncTaskMethodBuilderCreate;
                                sr = pred_members.AsyncTaskMethodBuilderSetResult;
-                               task = pred_members.AsyncTaskMethodBuilderTask.Resolve (Location);
+                               se = pred_members.AsyncTaskMethodBuilderSetException;
+                               task = pred_members.AsyncTaskMethodBuilderTask.Get ();
                        } else {
                                builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilderGeneric;
                                bf = pred_members.AsyncTaskMethodBuilderGenericCreate;
                                sr = pred_members.AsyncTaskMethodBuilderGenericSetResult;
-                               task = pred_members.AsyncTaskMethodBuilderGenericTask.Resolve (Location);
+                               se = pred_members.AsyncTaskMethodBuilderGenericSetException;
+                               task = pred_members.AsyncTaskMethodBuilderGenericTask.Get ();
                                has_task_return_type = true;
                        }
 
-                       set_result = sr.Resolve (Location);
-                       var builder_factory = bf.Resolve (Location);
-                       var bt = builder_type.Resolve ();
-                       if (bt == null || set_result == null || builder_factory == null)
-                               return false;
+                       set_result = sr.Get ();
+                       set_exception = se.Get ();
+                       var builder_factory = bf.Get ();
+                       if (!builder_type.Define () || set_result == null || builder_factory == null || set_exception == null) {
+                               Report.Error (1993, Location,
+                                       "Cannot find compiler required types for asynchronous functions support. Are you targeting the wrong framework version?");
+                               return base.DoDefineMembers ();
+                       }
+
+                       var bt = builder_type.TypeSpec;
 
                        //
                        // Inflate generic Task types
                        //
                        if (has_task_return_type) {
-                               bt = bt.MakeGenericType (Module, return_type.TypeArguments);
+                               var task_return_type = return_type.TypeArguments;
+                               if (mutator != null)
+                                       task_return_type = mutator.Mutate (task_return_type);
+
+                               bt = bt.MakeGenericType (Module, task_return_type);
                                builder_factory = MemberCache.GetMember<MethodSpec> (bt, builder_factory);
                                set_result = MemberCache.GetMember<MethodSpec> (bt, set_result);
+                               set_exception = MemberCache.GetMember<MethodSpec> (bt, set_exception);
 
                                if (task != null)
                                        task = MemberCache.GetMember<PropertySpec> (bt, task);
                        }
 
                        builder = AddCompilerGeneratedField ("$builder", new TypeExpression (bt, Location));
-                       builder.ModFlags |= Modifiers.READONLY;
+
+                       var ctor = DefineDefaultConstructor (false);
 
                        if (!base.DoDefineMembers ())
                                return false;
 
-                       MethodGroupExpr mg;
-                       var block = instance_constructors[0].Block;
-
-                       //
-                       // Initialize continuation with state machine method
-                       //
-                       if (continuation != null) {
-                               var args = new Arguments (1);
-                               mg = MethodGroupExpr.CreatePredefined (StateMachineMethod.Spec, spec, Location);
-                               args.Add (new Argument (mg));
-
-                               block.AddStatement (
-                                       new StatementExpression (new SimpleAssign (
-                                               new FieldExpr (continuation, Location),
-                                               new NewDelegate (action, args, Location),
-                                               Location
-                               )));
-                       }
+                       Block block = ctor.Block;
 
-                       mg = MethodGroupExpr.CreatePredefined (builder_factory, bt, Location);
+                       var mg = MethodGroupExpr.CreatePredefined (builder_factory, bt, Location);
                        block.AddStatement (
                                new StatementExpression (new SimpleAssign (
-                               new FieldExpr (builder, Location),
-                               new Invocation (mg, new Arguments (0)),
+                                       new FieldExpr (builder, Location),
+                                       new Invocation (mg, new Arguments (0)),
                                Location)));
 
                        if (has_task_return_type) {
@@ -726,6 +690,70 @@ namespace Mono.CSharp
                        return true;
                }
 
+               public Expression EmitContinuationInitialization (EmitContext ec)
+               {
+                       //
+                       // When more than 1 awaiter has been used in the block we
+                       // introduce class scope field to cache continuation delegate
+                       //
+                       if (awaiters > 1) {
+                               if (continuation == null) {
+                                       continuation = AddCompilerGeneratedField ("$continuation", new TypeExpression (action, Location), true);
+                                       continuation.Define ();
+                               }
+
+                               var fexpr = new FieldExpr (continuation, Location);
+                               fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location);
+
+                               //
+                               // if ($continuation == null)
+                               //    $continuation = new Action (MoveNext);
+                               //
+                               fexpr.Emit (ec);
+
+                               var skip_cont_init = ec.DefineLabel ();
+                               ec.Emit (OpCodes.Brtrue_S, skip_cont_init);
+
+                               ec.EmitThis ();
+                               EmitActionLoad (ec);
+                               ec.Emit (OpCodes.Stfld, continuation.Spec);
+                               ec.MarkLabel (skip_cont_init);
+
+                               return fexpr;
+                       }
+
+                       //
+                       // Otherwise simply use temporary local variable
+                       //
+                       var field = LocalVariable.CreateCompilerGenerated (action, OriginalSourceBlock, Location);
+                       EmitActionLoad (ec);
+                       field.EmitAssign (ec);
+                       return new LocalVariableReference (field, Location);
+               }
+
+               void EmitActionLoad (EmitContext ec)
+               {
+                       ec.EmitThis ();
+                       ec.Emit (OpCodes.Ldftn, StateMachineMethod.Spec);
+                       ec.Emit (OpCodes.Newobj, (MethodSpec) MemberCache.FindMember (action, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly));
+               }
+
+               public void EmitSetException (EmitContext ec, LocalVariableReference exceptionVariable)
+               {
+                       //
+                       // $builder.SetException (Exception)
+                       //
+                       var mg = MethodGroupExpr.CreatePredefined (set_exception, set_exception.DeclaringType, Location);
+                       mg.InstanceExpression = new FieldExpr (Builder, Location) {
+                               InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location)
+                       };
+
+                       Arguments args = new Arguments (1);
+                       args.Add (new Argument (exceptionVariable));
+
+                       mg.EmitCall (ec, args);
+               }
+
                public void EmitSetResult (EmitContext ec)
                {
                        //
@@ -747,93 +775,31 @@ namespace Mono.CSharp
 
                        mg.EmitCall (ec, args);
                }
+       }
 
-               //
-               // Fabricates stack forwarder based on stack types which copies all
-               // parameters to type fields
-               //
-               public MethodSpec GetStackForwarder (TypeSpec[] types, out FieldSpec[] fields, out bool explicitThisNeeded)
+       class StackField : Field
+       {
+               public StackField (TypeDefinition parent, FullNamedExpression type, Modifiers mod, MemberName name)
+                       : base (parent, type, mod, name, null)
                {
-                       if (stack_forwarders == null) {
-                               stack_forwarders = new Dictionary<TypeSpec[], Tuple<MethodSpec, FieldSpec[], bool>> (TypeSpecComparer.Default);
-                       } else {
-                               //
-                               // Does same forwarder method with same types already exist
-                               //
-                               Tuple<MethodSpec, FieldSpec[], bool> method;
-                               if (stack_forwarders.TryGetValue (types, out method)) {
-                                       fields = method.Item2;
-                                       explicitThisNeeded = method.Item3;
-                                       return method.Item1;
-                               }
-                       }
-
-                       Parameter[] p = new Parameter[types.Length + 1];
-                       TypeSpec[] ptypes = new TypeSpec[p.Length];
-                       fields = new FieldSpec[types.Length];
-                       int this_argument_index = -1;
-                       BitArray used_fields_map = null;
-
-                       for (int i = 0; i < types.Length; ++i) {
-                               var t = types[i];
-
-                               TypeSpec parameter_type = t;
-                               if (parameter_type == InternalType.CurrentTypeOnStack) {
-                                       parameter_type = CurrentType;
-                               }
-
-                               p[i] = new Parameter (new TypeExpression (parameter_type, Location), null, 0, null, Location);
-                               ptypes[i] = parameter_type;
-
-                               if (t == InternalType.CurrentTypeOnStack) {
-                                       if (this_argument_index < 0)
-                                               this_argument_index = i;
-
-                                       // Null means the type is `this' we can optimize by ignoring
-                                       continue;
-                               }
-
-                               var reference = t as ReferenceContainer;
-                               if (reference != null)
-                                       t = reference.Element;
-
-                               if (used_fields_map == null)
-                                       used_fields_map = new BitArray (hoisted_stack_slots == null ? 0 : hoisted_stack_slots.Count);
-
-                               fields[i] = CreateStackValueField (t, used_fields_map);
-                       }
-
-                       //
-                       // None of the arguments is `this' need to add an extra parameter to pass it
-                       // to static forwarder method
-                       //
-                       if (this_argument_index < 0) {
-                               explicitThisNeeded = true;
-                               this_argument_index = types.Length;
-                               var this_parameter = new Parameter (new TypeExpression (CurrentType, Location), null, 0, null, Location);
-                               p[types.Length] = this_parameter;
-                               ptypes[types.Length] = CurrentType;
-                       } else {
-                               explicitThisNeeded = false;
-                               Array.Resize (ref p, p.Length - 1);
-                               Array.Resize (ref ptypes, ptypes.Length - 1);
-                       }
-
-                       var parameters = ParametersCompiled.CreateFullyResolved (p, ptypes);
-
-                       var m = new Method (this, null, new TypeExpression (Compiler.BuiltinTypes.Void, Location),
-                               Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED | Modifiers.DEBUGGER_HIDDEN,
-                               new MemberName ("<>s__" + stack_forwarders.Count.ToString ("X")), parameters, null);
+               }
 
-                       m.Block = new ToplevelBlock (Compiler, parameters, Location);
-                       m.Block.AddScopeStatement (new ParametersLoadStatement (fields, ptypes, this_argument_index));
+               public bool CanBeReused { get; set; }
+       }
 
-                       m.Define ();
-                       Methods.Add (m);
+       class StackFieldExpr : FieldExpr
+       {
+               public StackFieldExpr (Field field)
+                       : base (field, Location.Null)
+               {
+               }
 
-                       stack_forwarders.Add (types, Tuple.Create (m.Spec, fields, explicitThisNeeded));
+               public override void Emit (EmitContext ec)
+               {
+                       base.Emit (ec);
 
-                       return m.Spec;
+                       var field = (StackField) spec.MemberDefinition;
+                       field.CanBeReused = true;
                }
        }
 }