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