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