2 // async.cs: Asynchronous functions
5 // Marek Safar (marek.safar@gmail.com)
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2011 Novell, Inc.
13 using System.Collections.Generic;
16 using IKVM.Reflection.Emit;
18 using System.Reflection.Emit;
23 class Await : ExpressionStatement
28 public Await (Expression expr, Location loc)
34 protected override void CloneTo (CloneContext clonectx, Expression target)
36 var t = (Await) target;
38 t.expr = expr.Clone (clonectx);
41 public override Expression CreateExpressionTree (ResolveContext ec)
43 throw new NotImplementedException ("ET");
46 protected override Expression DoResolve (ResolveContext rc)
48 if (rc.HasSet (ResolveContext.Options.FinallyScope)) {
49 rc.Report.Error (1984, loc,
50 "The `await' operator cannot be used in the body of a finally clause");
53 if (rc.HasSet (ResolveContext.Options.CatchScope)) {
54 rc.Report.Error (1985, loc,
55 "The `await' operator cannot be used in the body of a catch clause");
58 if (rc.HasSet (ResolveContext.Options.LockScope)) {
59 rc.Report.Error (1996, loc,
60 "The `await' operator cannot be used in the body of a lock statement");
63 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
64 rc.Report.Error (1989, loc, "An expression tree cannot contain an await operator");
69 // TODO: New error code
70 rc.Report.Error (-1900, loc,
71 "The `await' operator cannot be used in an unsafe context");
74 var bc = (BlockContext) rc;
76 if (!bc.CurrentBlock.ParametersBlock.IsAsync) {
77 // TODO: Should check for existence of await type but
81 stmt = new AwaitStatement (expr, loc);
82 if (!stmt.Resolve (bc))
85 type = stmt.ResultType;
86 eclass = ExprClass.Variable;
90 public override void Emit (EmitContext ec)
92 stmt.EmitPrologue (ec);
96 public void EmitAssign (EmitContext ec, FieldExpr field)
98 stmt.EmitPrologue (ec);
99 field.InstanceExpression.Emit (ec);
103 public override void EmitStatement (EmitContext ec)
105 stmt.EmitStatement (ec);
109 class AwaitStatement : YieldStatement<AsyncInitializer>
111 sealed class AwaitableMemberAccess : MemberAccess
113 public AwaitableMemberAccess (Expression expr)
114 : base (expr, "GetAwaiter")
118 protected override void Error_TypeDoesNotContainDefinition (ResolveContext rc, TypeSpec type, string name)
120 Error_WrongGetAwaiter (rc, loc, type);
123 protected override void Error_OperatorCannotBeApplied (ResolveContext rc, TypeSpec type)
125 rc.Report.Error (1991, loc, "Cannot await `{0}' expression", type.GetSignatureForError ());
130 PropertyExpr is_completed;
131 MethodSpec on_completed;
132 MethodSpec get_result;
135 public AwaitStatement (Expression expr, Location loc)
142 public TypeSpec Type {
148 public TypeSpec ResultType {
150 return get_result.ReturnType;
156 protected override void DoEmit (EmitContext ec)
158 var fe_awaiter = new FieldExpr (awaiter, loc);
159 fe_awaiter.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
162 // result = awaiter.GetResult ();
164 var mg_result = MethodGroupExpr.CreatePredefined (get_result, fe_awaiter.Type, loc);
165 mg_result.InstanceExpression = fe_awaiter;
167 mg_result.EmitCall (ec, new Arguments (0));
170 public void EmitPrologue (EmitContext ec)
172 var fe_awaiter = new FieldExpr (awaiter, loc);
173 fe_awaiter.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
176 // awaiter = expr.GetAwaiter ();
178 fe_awaiter.EmitAssign (ec, expr, false, false);
180 is_completed.InstanceExpression = fe_awaiter;
181 is_completed.EmitBranchable (ec, resume_point, true);
183 var mg_completed = MethodGroupExpr.CreatePredefined (on_completed, fe_awaiter.Type, loc);
184 mg_completed.InstanceExpression = fe_awaiter;
186 var args = new Arguments (1);
187 var storey = (AsyncTaskStorey) machine_initializer.Storey;
188 var fe_cont = new FieldExpr (storey.Continuation, loc);
189 fe_cont.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
191 args.Add (new Argument (fe_cont));
194 // awaiter.OnCompleted (continuation);
196 mg_completed.EmitCall (ec, args);
201 public void EmitStatement (EmitContext ec)
206 if (ResultType.Kind != MemberKind.Void) {
207 var storey = (AsyncTaskStorey) machine_initializer.Storey;
209 if (storey.HoistedReturn != null)
210 storey.HoistedReturn.EmitAssign (ec);
212 ec.Emit (OpCodes.Pop);
216 static void Error_WrongGetAwaiter (ResolveContext rc, Location loc, TypeSpec type)
218 rc.Report.Error (1986, loc,
219 "The `await' operand type `{0}' must have suitable GetAwaiter method",
220 type.GetSignatureForError ());
223 void Error_WrongAwaiterPattern (ResolveContext rc, TypeSpec awaiter)
225 rc.Report.Error (1999, loc, "The awaiter type `{0}' must have suitable IsCompleted, OnCompleted, and GetResult members",
226 awaiter.GetSignatureForError ());
229 public override bool Resolve (BlockContext bc)
231 if (!base.Resolve (bc))
237 // The task result is of dynamic type
239 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
240 throw new NotImplementedException ("dynamic await");
243 // Check whether the expression is awaitable
245 Expression ama = new AwaitableMemberAccess (expr).Resolve (bc);
249 Arguments args = new Arguments (0);
251 var errors_printer = new SessionReportPrinter ();
252 var old = bc.Report.SetPrinter (errors_printer);
253 ama = new Invocation (ama, args).Resolve (bc);
255 if (errors_printer.ErrorsCount > 0 || !MemberAccess.IsValidDotExpression (ama.Type)) {
256 bc.Report.SetPrinter (old);
257 Error_WrongGetAwaiter (bc, loc, expr.Type);
261 var awaiter_type = ama.Type;
262 awaiter = ((AsyncTaskStorey) machine_initializer.Storey).AddAwaiter (awaiter_type, loc);
266 // Predefined: bool IsCompleted { get; }
268 var is_completed_ma = new MemberAccess (expr, "IsCompleted").Resolve (bc);
269 if (is_completed_ma != null) {
270 is_completed = is_completed_ma as PropertyExpr;
271 if (is_completed != null && is_completed.Type.BuiltinType == BuiltinTypeSpec.Type.Bool && is_completed.IsInstance && is_completed.Getter != null) {
274 bc.Report.SetPrinter (old);
275 Error_WrongAwaiterPattern (bc, awaiter_type);
280 bc.Report.SetPrinter (old);
282 if (errors_printer.ErrorsCount > 0) {
283 Error_WrongAwaiterPattern (bc, awaiter_type);
288 // Predefined: OnCompleted (Action)
290 if (bc.Module.PredefinedTypes.Action.Define ()) {
291 on_completed = MemberCache.FindMember (awaiter_type, MemberFilter.Method ("OnCompleted", 0,
292 ParametersCompiled.CreateFullyResolved (bc.Module.PredefinedTypes.Action.TypeSpec), bc.Module.Compiler.BuiltinTypes.Void),
293 BindingRestriction.InstanceOnly) as MethodSpec;
295 if (on_completed == null) {
296 Error_WrongAwaiterPattern (bc, awaiter_type);
302 // Predefined: GetResult ()
304 // The method return type is also result type of await expression
306 get_result = MemberCache.FindMember (awaiter_type, MemberFilter.Method ("GetResult", 0,
307 ParametersCompiled.EmptyReadOnlyParameters, null),
308 BindingRestriction.InstanceOnly) as MethodSpec;
310 if (get_result == null) {
311 Error_WrongAwaiterPattern (bc, awaiter_type);
319 public class AsyncInitializer : StateMachineInitializer
321 TypeInferenceContext return_inference;
323 public AsyncInitializer (ParametersBlock block, TypeContainer host, TypeSpec returnType)
324 : base (block, host, returnType)
330 public override string ContainerType {
332 return "async state machine block";
336 public override bool IsIterator {
342 public Block OriginalBlock {
348 public TypeInferenceContext ReturnTypeInference {
350 return return_inference;
356 public static void Create (ParametersBlock block, ParametersCompiled parameters, TypeContainer host, TypeSpec returnType, Location loc)
358 if (returnType != null && returnType.Kind != MemberKind.Void &&
359 returnType != host.Module.PredefinedTypes.Task.TypeSpec &&
360 !returnType.IsGenericTask) {
361 host.Compiler.Report.Error (1983, loc, "The return type of an async method must be void, Task, or Task<T>");
364 for (int i = 0; i < parameters.Count; i++) {
365 Parameter p = parameters[i];
366 Parameter.Modifier mod = p.ModFlags;
367 if ((mod & Parameter.Modifier.ISBYREF) != 0) {
368 host.Compiler.Report.Error (1988, p.Location,
369 "Async methods cannot have ref or out parameters");
374 if (p is ArglistParameter) {
375 host.Compiler.Report.Error (1636, p.Location,
376 "__arglist is not allowed in parameter list of iterators");
381 if (parameters.Types[i].IsPointer) {
382 host.Compiler.Report.Error (1637, p.Location,
383 "Iterators cannot have unsafe parameters or yield types");
389 //if (!block.HasAwait) {
392 block.WrapIntoAsyncTask (host, returnType);
395 protected override BlockContext CreateBlockContext (ResolveContext rc)
397 var ctx = base.CreateBlockContext (rc);
398 var lambda = rc.CurrentAnonymousMethod as LambdaMethod;
400 return_inference = lambda.ReturnTypeInference;
405 public override Expression CreateExpressionTree (ResolveContext ec)
407 return base.CreateExpressionTree (ec);
410 public override void Emit (EmitContext ec)
412 throw new NotImplementedException ();
415 protected override void EmitMoveNextEpilogue (EmitContext ec)
417 var storey = (AsyncTaskStorey) Storey;
418 storey.EmitSetResult (ec);
421 public override void EmitStatement (EmitContext ec)
423 var storey = (AsyncTaskStorey) Storey;
424 storey.Instance.Emit (ec);
425 ec.Emit (OpCodes.Call, storey.StateMachineMethod.Spec);
427 if (storey.Task != null) {
429 // async.$builder.Task;
431 var pe_task = new PropertyExpr (storey.Task, loc) {
432 InstanceExpression = new FieldExpr (storey.Builder, loc) {
433 InstanceExpression = storey.Instance
435 Getter = storey.Task.Get
441 ec.Emit (OpCodes.Ret);
445 class AsyncTaskStorey : StateMachine
448 Field builder, continuation;
449 readonly TypeSpec return_type;
450 MethodSpec set_result;
452 LocalVariable hoisted_return;
454 public AsyncTaskStorey (AsyncInitializer initializer, TypeSpec type)
455 : base (initializer.OriginalBlock, initializer.Host, null, null, "async")
460 public Field AddAwaiter (TypeSpec type, Location loc)
462 return AddCompilerGeneratedField ("$awaiter" + awaiters++.ToString ("X"), new TypeExpression (type, loc));
467 public Field Builder {
473 public Field Continuation {
479 public LocalVariable HoistedReturn {
481 return hoisted_return;
485 public TypeSpec ReturnType {
491 public PropertySpec Task {
499 protected override bool DoDefineMembers ()
501 var action = Module.PredefinedTypes.Action.Resolve ();
502 if (action != null) {
503 continuation = AddCompilerGeneratedField ("$continuation", new TypeExpression (action, Location));
504 continuation.ModFlags |= Modifiers.READONLY;
507 PredefinedType builder_type;
508 PredefinedMember<MethodSpec> bf;
509 PredefinedMember<MethodSpec> sr;
510 bool has_task_return_type = false;
511 var pred_members = Module.PredefinedMembers;
513 if (return_type.Kind == MemberKind.Void) {
514 builder_type = Module.PredefinedTypes.AsyncVoidMethodBuilder;
515 bf = pred_members.AsyncVoidMethodBuilderCreate;
516 sr = pred_members.AsyncVoidMethodBuilderSetResult;
517 } else if (return_type == Module.PredefinedTypes.Task.TypeSpec) {
518 builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilder;
519 bf = pred_members.AsyncTaskMethodBuilderCreate;
520 sr = pred_members.AsyncTaskMethodBuilderSetResult;
521 task = pred_members.AsyncTaskMethodBuilderTask.Resolve (Location);
523 builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilderGeneric;
524 bf = pred_members.AsyncTaskMethodBuilderGenericCreate;
525 sr = pred_members.AsyncTaskMethodBuilderGenericSetResult;
526 task = pred_members.AsyncTaskMethodBuilderGenericTask.Resolve (Location);
527 has_task_return_type = true;
530 set_result = sr.Resolve (Location);
531 var builder_factory = bf.Resolve (Location);
532 var bt = builder_type.Resolve ();
533 if (bt == null || set_result == null || builder_factory == null)
537 // Inflate generic Task types
539 if (has_task_return_type) {
540 bt = bt.MakeGenericType (Module, return_type.TypeArguments);
541 builder_factory = MemberCache.GetMember<MethodSpec> (bt, builder_factory);
542 set_result = MemberCache.GetMember<MethodSpec> (bt, set_result);
545 task = MemberCache.GetMember<PropertySpec> (bt, task);
548 builder = AddCompilerGeneratedField ("$builder", new TypeExpression (bt, Location));
549 builder.ModFlags |= Modifiers.READONLY;
551 if (!base.DoDefineMembers ())
555 var block = instance_constructors[0].Block;
558 // Initialize continuation with state machine method
560 if (continuation != null) {
561 var args = new Arguments (1);
562 mg = MethodGroupExpr.CreatePredefined (StateMachineMethod.Spec, spec, Location);
563 args.Add (new Argument (mg));
566 new StatementExpression (new SimpleAssign (
567 new FieldExpr (continuation, Location),
568 new NewDelegate (action, args, Location),
573 mg = MethodGroupExpr.CreatePredefined (builder_factory, bt, Location);
575 new StatementExpression (new SimpleAssign (
576 new FieldExpr (builder, Location),
577 new Invocation (mg, new Arguments (0)),
580 if (has_task_return_type) {
581 hoisted_return = LocalVariable.CreateCompilerGenerated (bt.TypeArguments[0], block, Location);
587 public void EmitSetResult (EmitContext ec)
590 // $builder.SetResult ();
591 // $builder.SetResult<return-type> (value);
593 var mg = MethodGroupExpr.CreatePredefined (set_result, set_result.DeclaringType, Location);
594 mg.InstanceExpression = new FieldExpr (Builder, Location) {
595 InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location)
599 if (hoisted_return == null) {
600 args = new Arguments (0);
602 args = new Arguments (1);
603 args.Add (new Argument (new LocalVariableReference (hoisted_return, Location)));
606 mg.EmitCall (ec, args);