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