Add better errors for async
[mono.git] / mcs / mcs / async.cs
1 //
2 // async.cs: Asynchronous functions
3 //
4 // Author:
5 //   Marek Safar (marek.safar@gmail.com)
6 //
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
8 //
9 // Copyright 2011 Novell, Inc.
10 // Copyright 2011 Xamarin Inc.
11 //
12
13 using System;
14 using System.Collections.Generic;
15 using System.Linq;
16 using System.Collections;
17
18 #if STATIC
19 using IKVM.Reflection.Emit;
20 #else
21 using System.Reflection.Emit;
22 #endif
23
24 namespace Mono.CSharp
25 {
26         public class Await : ExpressionStatement
27         {
28                 Expression expr;
29                 AwaitStatement stmt;
30
31                 public Await (Expression expr, Location loc)
32                 {
33                         this.expr = expr;
34                         this.loc = loc;
35                 }
36
37                 public Expression Expr {
38                         get {
39                                 return expr;
40                         }
41                 }
42
43                 protected override void CloneTo (CloneContext clonectx, Expression target)
44                 {
45                         var t = (Await) target;
46
47                         t.expr = expr.Clone (clonectx);
48                 }
49
50                 public override Expression CreateExpressionTree (ResolveContext ec)
51                 {
52                         throw new NotImplementedException ("ET");
53                 }
54
55                 public override bool ContainsEmitWithAwait ()
56                 {
57                         return true;
58                 }
59
60                 protected override Expression DoResolve (ResolveContext rc)
61                 {
62                         if (rc.HasSet (ResolveContext.Options.LockScope)) {
63                                 rc.Report.Error (1996, loc,
64                                         "The `await' operator cannot be used in the body of a lock statement");
65                         }
66
67                         if (rc.IsUnsafe) {
68                                 rc.Report.Error (4004, loc,
69                                         "The `await' operator cannot be used in an unsafe context");
70                         }
71
72                         var bc = (BlockContext) rc;
73
74                         stmt = new AwaitStatement (expr, loc);
75                         if (!stmt.Resolve (bc))
76                                 return null;
77
78                         type = stmt.ResultType;
79                         eclass = ExprClass.Variable;
80                         return this;
81                 }
82
83                 public override void Emit (EmitContext ec)
84                 {
85                         stmt.EmitPrologue (ec);
86
87                         using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
88                                 stmt.Emit (ec);
89                         }
90                 }
91                 
92                 public override Expression EmitToField (EmitContext ec)
93                 {
94                         stmt.EmitPrologue (ec);
95                         return stmt.GetResultExpression (ec);
96                 }
97                 
98                 public void EmitAssign (EmitContext ec, FieldExpr field)
99                 {
100                         stmt.EmitPrologue (ec);
101                         field.InstanceExpression.Emit (ec);
102                         stmt.Emit (ec);
103                 }
104
105                 public override void EmitStatement (EmitContext ec)
106                 {
107                         stmt.EmitStatement (ec);
108                 }
109
110                 public override object Accept (StructuralVisitor visitor)
111                 {
112                         return visitor.Visit (this);
113                 }
114         }
115
116         class AwaitStatement : YieldStatement<AsyncInitializer>
117         {
118                 sealed class AwaitableMemberAccess : MemberAccess
119                 {
120                         public AwaitableMemberAccess (Expression expr)
121                                 : base (expr, "GetAwaiter")
122                         {
123                         }
124
125                         protected override void Error_TypeDoesNotContainDefinition (ResolveContext rc, TypeSpec type, string name)
126                         {
127                                 Error_OperatorCannotBeApplied (rc, type);
128                         }
129
130                         protected override void Error_OperatorCannotBeApplied (ResolveContext rc, TypeSpec type)
131                         {
132                                 var invocation = LeftExpression as Invocation;
133                                 if (invocation != null && invocation.MethodGroup != null && (invocation.MethodGroup.BestCandidate.Modifiers & Modifiers.ASYNC) != 0) {
134                                         rc.Report.Error (4008, loc, "Cannot await void method `{0}'. Consider changing method return type to `Task'",
135                                                 invocation.GetSignatureForError ());
136                                 } else {
137                                         rc.Report.Error (4001, loc, "Cannot await `{0}' expression", type.GetSignatureForError ());
138                                 }
139                         }
140                 }
141
142                 sealed class GetResultInvocation : Invocation
143                 {
144                         public GetResultInvocation (MethodGroupExpr mge, Arguments arguments)
145                                 : base (null, arguments)
146                         {
147                                 mg = mge;
148                                 type = mg.BestCandidateReturnType;
149                         }
150
151                         public override Expression EmitToField (EmitContext ec)
152                         {
153                                 return this;
154                         }
155                 }
156
157                 Field awaiter;
158                 PropertySpec is_completed;
159                 MethodSpec on_completed;
160                 MethodSpec get_result;
161                 TypeSpec type;
162                 TypeSpec result_type;
163
164                 public AwaitStatement (Expression expr, Location loc)
165                         : base (expr, loc)
166                 {
167                 }
168
169                 #region Properties
170
171                 bool IsDynamic {
172                         get {
173                                 return is_completed == null;
174                         }
175                 }
176
177                 public TypeSpec ResultType {
178                         get {
179                                 return result_type;
180                         }
181                 }
182
183                 #endregion
184
185                 protected override void DoEmit (EmitContext ec)
186                 {
187                         GetResultExpression (ec).Emit (ec);
188                 }
189
190                 public Expression GetResultExpression (EmitContext ec)
191                 {
192                         var fe_awaiter = new FieldExpr (awaiter, loc);
193                         fe_awaiter.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
194
195                         //
196                         // result = awaiter.GetResult ();
197                         //
198                         if (IsDynamic) {
199                                 var rc = new ResolveContext (ec.MemberContext);
200                                 return new Invocation (new MemberAccess (fe_awaiter, "GetResult"), new Arguments (0)).Resolve (rc);
201                         } else {
202                                 var mg_result = MethodGroupExpr.CreatePredefined (get_result, fe_awaiter.Type, loc);
203                                 mg_result.InstanceExpression = fe_awaiter;
204
205                                 return new GetResultInvocation (mg_result, new Arguments (0));
206                         }
207                 }
208
209                 public void EmitPrologue (EmitContext ec)
210                 {
211                         var fe_awaiter = new FieldExpr (awaiter, loc);
212                         fe_awaiter.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
213
214                         //
215                         // awaiter = expr.GetAwaiter ();
216                         //
217                         fe_awaiter.EmitAssign (ec, expr, false, false);
218
219                         Label skip_continuation = ec.DefineLabel ();
220
221                         Expression completed_expr;
222                         if (IsDynamic) {
223                                 var rc = new ResolveContext (ec.MemberContext);
224
225                                 Arguments dargs = new Arguments (1);
226                                 dargs.Add (new Argument (fe_awaiter));
227                                 completed_expr = new DynamicMemberBinder ("IsCompleted", dargs, loc).Resolve (rc);
228                         } else {
229                                 var pe = PropertyExpr.CreatePredefined (is_completed, loc);
230                                 pe.InstanceExpression = fe_awaiter;
231                                 completed_expr = pe;
232                         }
233
234                         completed_expr.EmitBranchable (ec, skip_continuation, true);
235
236                         base.DoEmit (ec);
237
238                         //
239                         // The stack has to be empty before calling await continuation. We handle this
240                         // by lifting values which would be left on stack into class fields. The process
241                         // is quite complicated and quite hard to test because any expression can possibly
242                         // leave a value on the stack.
243                         //
244                         // Following assert fails when some of expression called before is missing EmitToField
245                         // or parent expression fails to find await in children expressions
246                         //
247                         ec.AssertEmptyStack ();
248
249                         var storey = (AsyncTaskStorey) machine_initializer.Storey;
250                         var cont_field = storey.EmitContinuationInitialization (ec);
251
252                         var args = new Arguments (1);
253                         args.Add (new Argument (cont_field));
254
255                         if (IsDynamic) {
256                                 var rc = new ResolveContext (ec.MemberContext);
257                                 var mg_expr = new Invocation (new MemberAccess (fe_awaiter, "OnCompleted"), args).Resolve (rc);
258
259                                 ExpressionStatement es = (ExpressionStatement) mg_expr;
260                                 es.EmitStatement (ec);
261                         } else {
262                                 var mg_completed = MethodGroupExpr.CreatePredefined (on_completed, fe_awaiter.Type, loc);
263                                 mg_completed.InstanceExpression = fe_awaiter;
264
265                                 //
266                                 // awaiter.OnCompleted (continuation);
267                                 //
268                                 mg_completed.EmitCall (ec, args);
269                         }
270
271                         // Return ok
272                         machine_initializer.EmitLeave (ec, unwind_protect);
273
274                         ec.MarkLabel (resume_point);
275                         ec.MarkLabel (skip_continuation);
276                 }
277
278                 public void EmitStatement (EmitContext ec)
279                 {
280                         EmitPrologue (ec);
281                         DoEmit (ec);
282
283                         if (ResultType.Kind != MemberKind.Void) {
284                                 var storey = (AsyncTaskStorey) machine_initializer.Storey;
285
286                             if (storey.HoistedReturn != null)
287                                 storey.HoistedReturn.EmitAssign (ec);
288                                 else
289                                         ec.Emit (OpCodes.Pop);
290                         }
291                 }
292
293                 void Error_WrongAwaiterPattern (ResolveContext rc, TypeSpec awaiter)
294                 {
295                         rc.Report.Error (4011, loc, "The awaiter type `{0}' must have suitable IsCompleted, OnCompleted, and GetResult members",
296                                 awaiter.GetSignatureForError ());
297                 }
298
299                 public override bool Resolve (BlockContext bc)
300                 {
301                         if (bc.CurrentBlock is Linq.QueryBlock) {
302                                 bc.Report.Error (1995, loc,
303                                         "The `await' operator may only be used in a query expression within the first collection expression of the initial `from' clause or within the collection expression of a `join' clause");
304                                 return false;
305                         }
306
307                         if (!base.Resolve (bc))
308                                 return false;
309
310                         Arguments args = new Arguments (0);
311
312                         type = expr.Type;
313
314                         //
315                         // The await expression is of dynamic type
316                         //
317                         if (type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
318                                 result_type = type;
319
320                                 awaiter = ((AsyncTaskStorey) machine_initializer.Storey).AddAwaiter (type, loc);
321
322                                 expr = new Invocation (new MemberAccess (expr, "GetAwaiter"), args).Resolve (bc);
323                                 return true;
324                         }
325
326                         //
327                         // Check whether the expression is awaitable
328                         //
329                         Expression ama = new AwaitableMemberAccess (expr).Resolve (bc);
330                         if (ama == null)
331                                 return false;
332
333                         var errors_printer = new SessionReportPrinter ();
334                         var old = bc.Report.SetPrinter (errors_printer);
335                         ama = new Invocation (ama, args).Resolve (bc);
336                         bc.Report.SetPrinter (old);
337
338                         if (errors_printer.ErrorsCount > 0 || !MemberAccess.IsValidDotExpression (ama.Type)) {
339                                 bc.Report.Error (1986, expr.Location,
340                                         "The `await' operand type `{0}' must have suitable GetAwaiter method",
341                                         expr.Type.GetSignatureForError ());
342
343                                 return false;
344                         }
345
346                         var awaiter_type = ama.Type;
347                         awaiter = ((AsyncTaskStorey) machine_initializer.Storey).AddAwaiter (awaiter_type, loc);
348
349                         expr = ama;
350
351                         //
352                         // Predefined: bool IsCompleted { get; } 
353                         //
354                         is_completed = MemberCache.FindMember (awaiter_type, MemberFilter.Property ("IsCompleted", bc.Module.Compiler.BuiltinTypes.Bool),
355                                 BindingRestriction.InstanceOnly) as PropertySpec;
356
357                         if (is_completed == null || !is_completed.HasGet) {
358                                 Error_WrongAwaiterPattern (bc, awaiter_type);
359                                 return false;
360                         }
361
362                         //
363                         // Predefined: OnCompleted (Action)
364                         //
365                         if (bc.Module.PredefinedTypes.Action.Define ()) {
366                                 on_completed = MemberCache.FindMember (awaiter_type, MemberFilter.Method ("OnCompleted", 0,
367                                         ParametersCompiled.CreateFullyResolved (bc.Module.PredefinedTypes.Action.TypeSpec), bc.Module.Compiler.BuiltinTypes.Void),
368                                         BindingRestriction.InstanceOnly) as MethodSpec;
369
370                                 if (on_completed == null) {
371                                         Error_WrongAwaiterPattern (bc, awaiter_type);
372                                         return false;
373                                 }
374                         }
375
376                         //
377                         // Predefined: GetResult ()
378                         //
379                         // The method return type is also result type of await expression
380                         //
381                         get_result = MemberCache.FindMember (awaiter_type, MemberFilter.Method ("GetResult", 0,
382                                 ParametersCompiled.EmptyReadOnlyParameters, null),
383                                 BindingRestriction.InstanceOnly) as MethodSpec;
384
385                         if (get_result == null) {
386                                 Error_WrongAwaiterPattern (bc, awaiter_type);
387                                 return false;
388                         }
389
390                         result_type = get_result.ReturnType;
391
392                         return true;
393                 }
394         }
395
396         public class AsyncInitializer : StateMachineInitializer
397         {
398                 TypeInferenceContext return_inference;
399
400                 public AsyncInitializer (ParametersBlock block, TypeDefinition host, TypeSpec returnType)
401                         : base (block, host, returnType)
402                 {
403                 }
404
405                 #region Properties
406
407                 public override string ContainerType {
408                         get {
409                                 return "async state machine block";
410                         }
411                 }
412
413                 public override bool IsIterator {
414                         get {
415                                 return false;
416                         }
417                 }
418
419                 public Block OriginalBlock {
420                         get {
421                                 return block.Parent;
422                         }
423                 }
424
425                 public TypeInferenceContext ReturnTypeInference {
426                         get {
427                                 return return_inference;
428                         }
429                 }
430
431                 #endregion
432
433                 public static void Create (IMemberContext context, ParametersBlock block, ParametersCompiled parameters, TypeDefinition host, TypeSpec returnType, Location loc)
434                 {
435                         for (int i = 0; i < parameters.Count; i++) {
436                                 Parameter p = parameters[i];
437                                 Parameter.Modifier mod = p.ModFlags;
438                                 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
439                                         host.Compiler.Report.Error (1988, p.Location,
440                                                 "Async methods cannot have ref or out parameters");
441                                         return;
442                                 }
443
444                                 if (p is ArglistParameter) {
445                                         host.Compiler.Report.Error (4006, p.Location,
446                                                 "__arglist is not allowed in parameter list of async methods");
447                                         return;
448                                 }
449
450                                 if (parameters.Types[i].IsPointer) {
451                                         host.Compiler.Report.Error (4005, p.Location,
452                                                 "Async methods cannot have unsafe parameters");
453                                         return;
454                                 }
455                         }
456
457                         if (!block.HasAwait) {
458                                 host.Compiler.Report.Warning (1998, 1, loc,
459                                         "Async block lacks `await' operator and will run synchronously");
460                         }
461
462                         block.WrapIntoAsyncTask (context, host, returnType);
463                 }
464
465                 protected override BlockContext CreateBlockContext (ResolveContext rc)
466                 {
467                         var ctx = base.CreateBlockContext (rc);
468                         var lambda = rc.CurrentAnonymousMethod as LambdaMethod;
469                         if (lambda != null)
470                                 return_inference = lambda.ReturnTypeInference;
471
472                         ctx.StartFlowBranching (this, rc.CurrentBranching);
473                         return ctx;
474                 }
475
476                 public override Expression CreateExpressionTree (ResolveContext ec)
477                 {
478                         return base.CreateExpressionTree (ec);
479                 }
480
481                 public override void Emit (EmitContext ec)
482                 {
483                         throw new NotImplementedException ();
484                 }
485
486                 protected override void EmitMoveNextEpilogue (EmitContext ec)
487                 {
488                         var storey = (AsyncTaskStorey) Storey;
489                         storey.EmitSetResult (ec);
490                 }
491
492                 public override void EmitStatement (EmitContext ec)
493                 {
494                         var storey = (AsyncTaskStorey) Storey;
495                         storey.Instance.Emit (ec);
496
497                         var move_next_entry = storey.StateMachineMethod.Spec;
498                         if (storey.MemberName.Arity > 0) {
499                                 move_next_entry = MemberCache.GetMember (storey.Instance.Type, move_next_entry);
500                         }
501
502                         ec.Emit (OpCodes.Call, move_next_entry);
503
504                         //
505                         // Emits return <async-storey-instance>.$builder.Task;
506                         //
507                         if (storey.Task != null) {
508                                 var builder_field = storey.Builder.Spec;
509                                 var task_get = storey.Task.Get;
510
511                                 if (storey.MemberName.Arity > 0) {
512                                         builder_field = MemberCache.GetMember (storey.Instance.Type, builder_field);
513                                         task_get = MemberCache.GetMember (builder_field.MemberType, task_get);
514                                 }
515
516                                 var pe_task = new PropertyExpr (storey.Task, loc) {
517                                         InstanceExpression = new FieldExpr (builder_field, loc) {
518                                                 InstanceExpression = storey.Instance
519                                         },
520                                         Getter = task_get
521                                 };
522
523                                 pe_task.Emit (ec);
524                         }
525
526                         ec.Emit (OpCodes.Ret);
527                 }
528         }
529
530         class AsyncTaskStorey : StateMachine
531         {
532                 int awaiters;
533                 Field builder, continuation;
534                 readonly TypeSpec return_type;
535                 MethodSpec set_result;
536                 MethodSpec set_exception;
537                 PropertySpec task;
538                 LocalVariable hoisted_return;
539                 int locals_captured;
540                 Dictionary<TypeSpec, List<StackField>> stack_fields;
541                 TypeSpec action;
542
543                 public AsyncTaskStorey (IMemberContext context, AsyncInitializer initializer, TypeSpec type)
544                         : base (initializer.OriginalBlock, initializer.Host, context.CurrentMemberDefinition as MemberBase, context.CurrentTypeParameters, "async")
545                 {
546                         return_type = type;
547                 }
548
549                 #region Properties
550
551                 public Field Builder {
552                         get {
553                                 return builder;
554                         }
555                 }
556
557                 public LocalVariable HoistedReturn {
558                         get {
559                                 return hoisted_return;
560                         }
561                 }
562
563                 public TypeSpec ReturnType {
564                         get {
565                                 return return_type;
566                         }
567                 }
568
569                 public PropertySpec Task {
570                         get {
571                                 return task;
572                         }
573                 }
574
575                 #endregion
576
577                 public Field AddAwaiter (TypeSpec type, Location loc)
578                 {
579                         return AddCapturedVariable ("$awaiter" + awaiters++.ToString ("X"), type);
580                 }
581
582                 public StackField AddCapturedLocalVariable (TypeSpec type)
583                 {
584                         if (mutator != null)
585                                 type = mutator.Mutate (type);
586
587                         List<StackField> existing_fields = null;
588                         if (stack_fields == null) {
589                                 stack_fields = new Dictionary<TypeSpec, List<StackField>> ();
590                         } else if (stack_fields.TryGetValue (type, out existing_fields)) {
591                                 foreach (var f in existing_fields) {
592                                         if (f.CanBeReused) {
593                                                 f.CanBeReused = false;
594                                                 return f;
595                                         }
596                                 }
597                         }
598
599                         const Modifiers mod = Modifiers.COMPILER_GENERATED | Modifiers.PRIVATE;
600                         var field = new StackField (this, new TypeExpression (type, Location), mod, new MemberName ("<s>$" + locals_captured++.ToString ("X"), Location));
601                         AddField (field);
602
603                         field.Define ();
604
605                         if (existing_fields == null) {
606                                 existing_fields = new List<StackField> ();
607                                 stack_fields.Add (type, existing_fields);
608                         }
609
610                         existing_fields.Add (field);
611
612                         return field;
613                 }
614
615                 protected override bool DoDefineMembers ()
616                 {
617                         action = Module.PredefinedTypes.Action.Resolve ();
618
619                         PredefinedType builder_type;
620                         PredefinedMember<MethodSpec> bf;
621                         PredefinedMember<MethodSpec> sr;
622                         PredefinedMember<MethodSpec> se;
623                         bool has_task_return_type = false;
624                         var pred_members = Module.PredefinedMembers;
625
626                         if (return_type.Kind == MemberKind.Void) {
627                                 builder_type = Module.PredefinedTypes.AsyncVoidMethodBuilder;
628                                 bf = pred_members.AsyncVoidMethodBuilderCreate;
629                                 sr = pred_members.AsyncVoidMethodBuilderSetResult;
630                                 se = pred_members.AsyncVoidMethodBuilderSetException;
631                         } else if (return_type == Module.PredefinedTypes.Task.TypeSpec) {
632                                 builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilder;
633                                 bf = pred_members.AsyncTaskMethodBuilderCreate;
634                                 sr = pred_members.AsyncTaskMethodBuilderSetResult;
635                                 se = pred_members.AsyncTaskMethodBuilderSetException;
636                                 task = pred_members.AsyncTaskMethodBuilderTask.Get ();
637                         } else {
638                                 builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilderGeneric;
639                                 bf = pred_members.AsyncTaskMethodBuilderGenericCreate;
640                                 sr = pred_members.AsyncTaskMethodBuilderGenericSetResult;
641                                 se = pred_members.AsyncTaskMethodBuilderGenericSetException;
642                                 task = pred_members.AsyncTaskMethodBuilderGenericTask.Get ();
643                                 has_task_return_type = true;
644                         }
645
646                         set_result = sr.Get ();
647                         set_exception = se.Get ();
648                         var builder_factory = bf.Get ();
649                         if (!builder_type.Define () || set_result == null || builder_factory == null || set_exception == null) {
650                                 Report.Error (1993, Location,
651                                         "Cannot find compiler required types for asynchronous functions support. Are you targeting the wrong framework version?");
652                                 return base.DoDefineMembers ();
653                         }
654
655                         var bt = builder_type.TypeSpec;
656
657                         //
658                         // Inflate generic Task types
659                         //
660                         if (has_task_return_type) {
661                                 var task_return_type = return_type.TypeArguments;
662                                 if (mutator != null)
663                                         task_return_type = mutator.Mutate (task_return_type);
664
665                                 bt = bt.MakeGenericType (Module, task_return_type);
666                                 builder_factory = MemberCache.GetMember<MethodSpec> (bt, builder_factory);
667                                 set_result = MemberCache.GetMember<MethodSpec> (bt, set_result);
668                                 set_exception = MemberCache.GetMember<MethodSpec> (bt, set_exception);
669
670                                 if (task != null)
671                                         task = MemberCache.GetMember<PropertySpec> (bt, task);
672                         }
673
674                         builder = AddCompilerGeneratedField ("$builder", new TypeExpression (bt, Location));
675
676                         var ctor = DefineDefaultConstructor (false);
677
678                         if (!base.DoDefineMembers ())
679                                 return false;
680
681                         Block block = ctor.Block;
682
683                         var mg = MethodGroupExpr.CreatePredefined (builder_factory, bt, Location);
684                         block.AddStatement (
685                                 new StatementExpression (new SimpleAssign (
686                                         new FieldExpr (builder, Location),
687                                         new Invocation (mg, new Arguments (0)),
688                                 Location)));
689
690                         if (has_task_return_type) {
691                                 hoisted_return = LocalVariable.CreateCompilerGenerated (bt.TypeArguments[0], block, Location);
692                         }
693
694                         return true;
695                 }
696
697                 public Expression EmitContinuationInitialization (EmitContext ec)
698                 {
699                         //
700                         // When more than 1 awaiter has been used in the block we
701                         // introduce class scope field to cache continuation delegate
702                         //
703                         if (awaiters > 1) {
704                                 if (continuation == null) {
705                                         continuation = AddCompilerGeneratedField ("$continuation", new TypeExpression (action, Location), true);
706                                         continuation.Define ();
707                                 }
708
709                                 var fexpr = new FieldExpr (continuation, Location);
710                                 fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location);
711
712                                 //
713                                 // if ($continuation == null)
714                                 //    $continuation = new Action (MoveNext);
715                                 //
716                                 fexpr.Emit (ec);
717
718                                 var skip_cont_init = ec.DefineLabel ();
719                                 ec.Emit (OpCodes.Brtrue_S, skip_cont_init);
720
721                                 ec.EmitThis ();
722                                 EmitActionLoad (ec);
723                                 ec.Emit (OpCodes.Stfld, continuation.Spec);
724                                 ec.MarkLabel (skip_cont_init);
725
726                                 return fexpr;
727                         }
728
729                         //
730                         // Otherwise simply use temporary local variable
731                         //
732                         var field = LocalVariable.CreateCompilerGenerated (action, OriginalSourceBlock, Location);
733                         EmitActionLoad (ec);
734                         field.EmitAssign (ec);
735                         return new LocalVariableReference (field, Location);
736                 }
737
738                 void EmitActionLoad (EmitContext ec)
739                 {
740                         ec.EmitThis ();
741                         ec.Emit (OpCodes.Ldftn, StateMachineMethod.Spec);
742                         ec.Emit (OpCodes.Newobj, (MethodSpec) MemberCache.FindMember (action, MemberFilter.Constructor (null), BindingRestriction.DeclaredOnly));
743                 }
744
745                 public void EmitSetException (EmitContext ec, LocalVariableReference exceptionVariable)
746                 {
747                         //
748                         // $builder.SetException (Exception)
749                         //
750                         var mg = MethodGroupExpr.CreatePredefined (set_exception, set_exception.DeclaringType, Location);
751                         mg.InstanceExpression = new FieldExpr (Builder, Location) {
752                                 InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location)
753                         };
754
755                         Arguments args = new Arguments (1);
756                         args.Add (new Argument (exceptionVariable));
757
758                         mg.EmitCall (ec, args);
759                 }
760
761                 public void EmitSetResult (EmitContext ec)
762                 {
763                         //
764                         // $builder.SetResult ();
765                         // $builder.SetResult<return-type> (value);
766                         //
767                         var mg = MethodGroupExpr.CreatePredefined (set_result, set_result.DeclaringType, Location);
768                         mg.InstanceExpression = new FieldExpr (Builder, Location) {
769                                 InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location)
770                         };
771
772                         Arguments args;
773                         if (hoisted_return == null) {
774                                 args = new Arguments (0);
775                         } else {
776                                 args = new Arguments (1);
777                                 args.Add (new Argument (new LocalVariableReference (hoisted_return, Location)));
778                         }
779
780                         mg.EmitCall (ec, args);
781                 }
782         }
783
784         class StackField : Field
785         {
786                 public StackField (TypeDefinition parent, FullNamedExpression type, Modifiers mod, MemberName name)
787                         : base (parent, type, mod, name, null)
788                 {
789                 }
790
791                 public bool CanBeReused { get; set; }
792         }
793
794         class StackFieldExpr : FieldExpr
795         {
796                 public StackFieldExpr (Field field)
797                         : base (field, Location.Null)
798                 {
799                 }
800
801                 public override void Emit (EmitContext ec)
802                 {
803                         base.Emit (ec);
804
805                         var field = (StackField) spec.MemberDefinition;
806                         field.CanBeReused = true;
807                 }
808         }
809 }