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