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