if (!stmt.Resolve (bc))
return null;
+ if (rc.HasSet (ResolveContext.Options.FinallyScope) && rc.CurrentAnonymousMethod != null) {
+ var ats = (AsyncTaskStorey)rc.CurrentAnonymousMethod.Storey;
+ ats.HasAwaitInsideFinally = true;
+ }
+
type = stmt.ResultType;
eclass = ExprClass.Variable;
return this;
public bool ProbingMode { get; set; }
- protected override void Error_TypeDoesNotContainDefinition (ResolveContext rc, TypeSpec type, string name)
+ public override void Error_TypeDoesNotContainDefinition (ResolveContext rc, TypeSpec type, string name)
{
Error_OperatorCannotBeApplied (rc, type);
}
if (invocation != null && invocation.MethodGroup != null && (invocation.MethodGroup.BestCandidate.Modifiers & Modifiers.ASYNC) != 0) {
rc.Report.Error (4008, loc, "Cannot await void method `{0}'. Consider changing method return type to `Task'",
invocation.GetSignatureForError ());
- } else {
+ } else if (type != InternalType.ErrorType) {
rc.Report.Error (4001, loc, "Cannot await `{0}' expression", type.GetSignatureForError ());
}
}
var errors_printer = new SessionReportPrinter ();
var old = bc.Report.SetPrinter (errors_printer);
- ama = new Invocation (ama, args).Resolve (bc);
+
+ //
+ // The expression await t is classified the same way as the expression (t).GetAwaiter().GetResult().
+ //
+ ama = new Invocation (new ParenthesizedExpression (ama, Location.Null), args).Resolve (bc);
bc.Report.SetPrinter (old);
if (errors_printer.ErrorsCount > 0 || !MemberAccess.IsValidDotExpression (ama.Type)) {
public class AsyncInitializer : StateMachineInitializer
{
TypeInferenceContext return_inference;
- List<Label> redirected_jumps;
- FieldExpr HoistedReturnState;
public AsyncInitializer (ParametersBlock block, TypeDefinition host, TypeSpec returnType)
: base (block, host, returnType)
get; set;
}
+ public StackFieldExpr HoistedReturnState {
+ get; set;
+ }
+
public override bool IsIterator {
get {
return false;
throw new NotImplementedException ();
}
+ public void EmitCatchBlock (EmitContext ec)
+ {
+ var catch_value = LocalVariable.CreateCompilerGenerated (ec.Module.Compiler.BuiltinTypes.Exception, block, Location);
+
+ ec.BeginCatchBlock (catch_value.Type);
+ catch_value.EmitAssign (ec);
+
+ ec.EmitThis ();
+ ec.EmitInt ((int) IteratorStorey.State.After);
+ ec.Emit (OpCodes.Stfld, storey.PC.Spec);
+
+ ((AsyncTaskStorey) Storey).EmitSetException (ec, new LocalVariableReference (catch_value, Location));
+
+ ec.Emit (OpCodes.Leave, move_next_ok);
+ ec.EndExceptionBlock ();
+
+ }
+
protected override void EmitMoveNextEpilogue (EmitContext ec)
{
var storey = (AsyncTaskStorey) Storey;
ec.Emit (OpCodes.Ret);
}
- //
- // Emits state table of jumps outside of try block and reload of return
- // value when try block returns value
- //
- public void EmitRedirectedJumpsTable (EmitContext ec, StackFieldExpr returnResult, Label localReturn)
- {
- if (redirected_jumps == null)
- return;
-
- int ret_index = redirected_jumps.IndexOf (localReturn);
- if (ret_index >= 0) {
- redirected_jumps [ret_index] = ec.DefineLabel ();
- }
-
- HoistedReturnState.Emit (ec);
- ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
-
- if (ret_index >= 0) {
- ec.MarkLabel (redirected_jumps [ret_index]);
- var s = (AsyncTaskStorey)storey;
- ((IAssignMethod)s.HoistedReturnValue).EmitAssign (ec, returnResult, false, false);
-
- ec.Emit (OpCodes.Leave, BodyEnd);
- }
-
- // Mark fallthrough label
- ec.MarkLabel (redirected_jumps [0]);
- }
-
- public void EmitRedirectedJump (EmitContext ec, Label label)
- {
- if (redirected_jumps == null) {
- redirected_jumps = new List<Label> ();
-
- // Add fallthrough label
- redirected_jumps.Add (ec.DefineLabel ());
- HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int);
- }
-
- int index = redirected_jumps.IndexOf (label);
- if (index < 0) {
- redirected_jumps.Add (label);
- index = redirected_jumps.Count - 1;
- }
-
- //
- // Indicates we have return value captured
- //
- HoistedReturnState.EmitAssign (ec, new IntConstant (HoistedReturnState.Type, index, Location.Null), false, false);
- }
-
public override void MarkReachable (Reachability rc)
{
//
#region Properties
+ public bool HasAwaitInsideFinally { get; set; }
+
public Expression HoistedReturnValue { get; set; }
public TypeSpec ReturnType {
return field;
}
- public Field AddCapturedLocalVariable (TypeSpec type)
+ public Field AddCapturedLocalVariable (TypeSpec type, bool requiresUninitialized = false)
{
if (mutator != null)
type = mutator.Mutate (type);
List<Field> existing_fields = null;
if (stack_fields == null) {
stack_fields = new Dictionary<TypeSpec, List<Field>> ();
- } else if (stack_fields.TryGetValue (type, out existing_fields)) {
+ } else if (stack_fields.TryGetValue (type, out existing_fields) && !requiresUninitialized) {
foreach (var f in existing_fields) {
if (f.IsAvailableForReuse) {
f.IsAvailableForReuse = false;
builder = AddCompilerGeneratedField ("$builder", new TypeExpression (bt, Location));
+ Field rfield;
+ if (has_task_return_type && HasAwaitInsideFinally) {
+ //
+ // Special case async block with return value from finally clause. In such case
+ // we rewrite all return expresison stores to stfld to $return. Instead of treating
+ // returns outside of finally and inside of finally differently.
+ //
+ rfield = AddCompilerGeneratedField ("$return", new TypeExpression (bt.TypeArguments [0], Location));
+ } else {
+ rfield = null;
+ }
+
var set_state_machine = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Void, Location),
Modifiers.COMPILER_GENERATED | Modifiers.DEBUGGER_HIDDEN | Modifiers.PUBLIC,
new MemberName ("SetStateMachine"),
set_state_machine.Block.AddStatement (new StatementExpression (new Invocation (mg, args)));
if (has_task_return_type) {
- HoistedReturnValue = TemporaryVariableReference.Create (bt.TypeArguments [0], StateMachineMethod.Block, Location);
+ if (rfield != null) {
+ HoistedReturnValue = new FieldExpr (rfield, Location) {
+ InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null)
+ };
+ } else {
+ HoistedReturnValue = TemporaryVariableReference.Create (bt.TypeArguments [0], StateMachineMethod.Block, Location);
+ }
}
return true;
args.Add (new Argument (awaiter, Argument.AType.Ref));
args.Add (new Argument (new CompilerGeneratedThis (CurrentType, Location), Argument.AType.Ref));
using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
- mg.EmitCall (ec, args);
+ mg.EmitCall (ec, args, true);
}
}
args.Add (new Argument (exceptionVariable));
using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
- mg.EmitCall (ec, args);
+ mg.EmitCall (ec, args, true);
}
}
}
using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
- mg.EmitCall (ec, args);
+ mg.EmitCall (ec, args, true);
}
}