var async_type = storey.ReturnType;
if (async_type == null && async_block.ReturnTypeInference != null) {
- async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
+ if (expr.Type.Kind == MemberKind.Void && !(this is ContextualReturn))
+ ec.Report.Error (4029, loc, "Cannot return an expression of type `void'");
+ else
+ async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
return true;
}
if (async_type.Kind == MemberKind.Void) {
- ec.Report.Error (127, loc,
- "`{0}': A return keyword must not be followed by any expression when method returns void",
- ec.GetSignatureForError ());
-
+ ec.Report.Error (8030, loc,
+ "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
return false;
}
if (this is ContextualReturn)
return true;
- // Same error code as .NET but better error message
if (async_block.DelegateType != null) {
- ec.Report.Error (1997, loc,
- "`{0}': A return keyword must not be followed by an expression when async delegate returns `Task'. Consider using `Task<T>' return type",
- async_block.DelegateType.GetSignatureForError ());
+ ec.Report.Error (8031, loc,
+ "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task<T>'");
} else {
ec.Report.Error (1997, loc,
"`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
ec.GetSignatureForError ());
-
}
-
return false;
}
}
}
} else {
- // Same error code as .NET but better error message
if (block_return_type.Kind == MemberKind.Void) {
- ec.Report.Error (127, loc,
- "`{0}': A return keyword must not be followed by any expression when delegate returns void",
- am.GetSignatureForError ());
-
+ ec.Report.Error (8030, loc,
+ "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
return false;
}
protected override void DoEmit (EmitContext ec)
{
- if (expr == null)
- ec.Emit (OpCodes.Rethrow);
- else {
+ if (expr == null) {
+ var atv = ec.AsyncThrowVariable;
+ if (atv != null) {
+ if (atv.HoistedVariant != null) {
+ atv.HoistedVariant.Emit (ec);
+ } else {
+ atv.Emit (ec);
+ }
+
+ ec.Emit (OpCodes.Throw);
+ } else {
+ ec.Emit (OpCodes.Rethrow);
+ }
+ } else {
expr.Emit (ec);
ec.Emit (OpCodes.Throw);
{
li.CreateBuilder (ec);
- if (Initializer != null)
+ if (Initializer != null && !IsUnreachable)
((ExpressionStatement) Initializer).EmitStatement (ec);
if (declarators != null) {
foreach (var d in declarators) {
d.Variable.CreateBuilder (ec);
- if (d.Initializer != null) {
+ if (d.Initializer != null && !IsUnreachable) {
ec.Mark (d.Variable.Location);
((ExpressionStatement) d.Initializer).EmitStatement (ec);
}
DoEmit (ec);
}
- protected void EmitScopeInitializers (EmitContext ec)
+ public void EmitScopeInitializers (EmitContext ec)
{
foreach (Statement s in scope_initializers)
s.Emit (ec);
+
+ scope_initializers = null;
}
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
end_unreachable = s.FlowAnalysis (fc);
if (s.IsUnreachable) {
- statements[startIndex] = new EmptyStatement (s.loc);
+ statements [startIndex] = RewriteUnreachableStatement (s);
continue;
}
if (s.IsUnreachable) {
s.FlowAnalysis (fc);
- statements[startIndex] = new EmptyStatement (s.loc);
+ statements [startIndex] = RewriteUnreachableStatement (s);
}
}
}
return !Explicit.HasReachableClosingBrace;
}
+ static Statement RewriteUnreachableStatement (Statement s)
+ {
+ // LAMESPEC: It's not clear whether declararion statement should be part of reachability
+ // analysis. Even csc report unreachable warning for it but it's actually used hence
+ // we try to emulate this behaviour
+ //
+ // Consider:
+ // goto L;
+ // int v;
+ // L:
+ // v = 1;
+
+ if (s is BlockVariable)
+ return s;
+
+ return new EmptyStatement (s.loc);
+ }
+
public void ScanGotoJump (Statement label)
{
int i;
EmitTryBodyPrepare (ec);
EmitTryBody (ec);
- ec.BeginFinallyBlock ();
+ bool begin = EmitBeginFinallyBlock (ec);
Label start_finally = ec.DefineLabel ();
if (resume_points != null) {
EmitFinallyBody (ec);
}
- ec.EndExceptionBlock ();
+ if (begin)
+ ec.EndExceptionBlock ();
}
public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
return res;
}
+ protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
+ {
+ ec.BeginFinallyBlock ();
+ return true;
+ }
+
public override Reachability MarkReachable (Reachability rc)
{
base.MarkReachable (rc);
public override bool Resolve (BlockContext bc)
{
ctch.Filter = ctch.Filter.Resolve (bc);
- var c = ctch.Filter as Constant;
- if (c != null && !c.IsDefaultValue) {
- bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
+
+ if (ctch.Filter != null) {
+ if (ctch.Filter.ContainsEmitWithAwait ()) {
+ bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
+ }
+
+ var c = ctch.Filter as Constant;
+ if (c != null && !c.IsDefaultValue) {
+ bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
+ }
}
return true;
if (li != null)
EmitCatchVariableStore (ec);
- } else {
- if (IsGeneral)
- ec.BeginCatchBlock (ec.BuiltinTypes.Object);
- else
- ec.BeginCatchBlock (CatchType);
- if (li != null) {
- EmitCatchVariableStore (ec);
+ if (Block.HasAwait) {
+ Block.EmitScopeInitializers (ec);
} else {
- ec.Emit (OpCodes.Pop);
+ Block.Emit (ec);
}
+
+ return;
}
- Block.Emit (ec);
+ if (IsGeneral)
+ ec.BeginCatchBlock (ec.BuiltinTypes.Object);
+ else
+ ec.BeginCatchBlock (CatchType);
+
+ if (li != null) {
+ EmitCatchVariableStore (ec);
+ } else {
+ ec.Emit (OpCodes.Pop);
+ }
+
+ if (!Block.HasAwait)
+ Block.Emit (ec);
}
void EmitCatchVariableStore (EmitContext ec)
hoisted_temp = new LocalTemporary (li.Type);
hoisted_temp.Store (ec);
- // switch to assigning from the temporary variable and not from top of the stack
+ // switch to assignment from temporary variable and not from top of the stack
assign.UpdateSource (hoisted_temp);
}
}
- public override bool Resolve (BlockContext ec)
+ public override bool Resolve (BlockContext bc)
{
- using (ec.Set (ResolveContext.Options.CatchScope)) {
- if (type_expr != null) {
- type = type_expr.ResolveAsType (ec);
+ using (bc.Set (ResolveContext.Options.CatchScope)) {
+ if (type_expr == null) {
+ if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
+ Expression source = new EmptyExpression (li.Type);
+ assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
+ Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
+ }
+ } else {
+ type = type_expr.ResolveAsType (bc);
if (type == null)
return false;
- if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
- ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
+ if (li == null)
+ CreateExceptionVariable (type);
+
+ if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
+ bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
} else if (li != null) {
li.Type = type;
- li.PrepareAssignmentAnalysis (ec);
+ li.PrepareAssignmentAnalysis (bc);
// source variable is at the top of the stack
Expression source = new EmptyExpression (li.Type);
}
Block.SetCatchBlock ();
- return Block.Resolve (ec);
+ return Block.Resolve (bc);
}
}
+ bool CreateExceptionVariable (TypeSpec type)
+ {
+ if (!Block.HasAwait)
+ return false;
+
+ // TODO: Scan the block for rethrow expression
+ //if (!Block.HasRethrow)
+ // return;
+
+ li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
+ return true;
+ }
+
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
{
- if (li != null) {
+ if (li != null && !li.IsCompilerGenerated) {
fc.SetVariableAssigned (li.VariableInfo, true);
}
stmt.Emit (ec);
}
+ protected override bool EmitBeginFinallyBlock (EmitContext ec)
+ {
+ if (fini.HasAwait)
+ return false;
+
+ return base.EmitBeginFinallyBlock (ec);
+ }
+
public override void EmitFinallyBody (EmitContext ec)
{
+ StackFieldExpr exception_field;
+
+ if (fini.HasAwait) {
+ //
+ // Emits catch block like
+ //
+ // catch (object temp) {
+ // this.exception_field = temp;
+ // }
+ //
+ var type = ec.BuiltinTypes.Object;
+ ec.BeginCatchBlock (type);
+
+ var temp = ec.GetTemporaryLocal (type);
+ ec.Emit (OpCodes.Stloc, temp);
+
+ exception_field = ec.GetTemporaryField (type);
+ ec.EmitThis ();
+ ec.Emit (OpCodes.Ldloc, temp);
+ exception_field.EmitAssignFromStack (ec);
+
+ ec.EndExceptionBlock ();
+
+ ec.FreeTemporaryLocal (temp, type);
+ } else {
+ exception_field = null;
+ }
+
fini.Emit (ec);
+
+ if (exception_field != null) {
+ //
+ // Emits exception rethrow
+ //
+ // if (this.exception_field != null)
+ // throw this.exception_field;
+ //
+ exception_field.Emit (ec);
+ var skip_throw = ec.DefineLabel ();
+ ec.Emit (OpCodes.Brfalse_S, skip_throw);
+ exception_field.Emit (ec);
+ ec.Emit (OpCodes.Throw);
+ ec.MarkLabel (skip_throw);
+
+ exception_field.IsAvailableForReuse = true;
+ }
}
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
public Block Block;
List<Catch> clauses;
readonly bool inside_try_finally;
+ List<Catch> catch_sm;
public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
: base (l)
ok &= c.Resolve (bc);
+ if (c.Block.HasAwait) {
+ if (catch_sm == null)
+ catch_sm = new List<Catch> ();
+
+ catch_sm.Add (c);
+ }
+
if (c.Filter != null)
continue;
Block.Emit (ec);
- foreach (Catch c in clauses)
+ LocalBuilder state_variable = null;
+ foreach (Catch c in clauses) {
c.Emit (ec);
+ if (catch_sm != null) {
+ if (state_variable == null)
+ state_variable = ec.GetTemporaryLocal (ec.Module.Compiler.BuiltinTypes.Int);
+
+ var index = catch_sm.IndexOf (c);
+ if (index < 0)
+ continue;
+
+ ec.EmitInt (index + 1);
+ ec.Emit (OpCodes.Stloc, state_variable);
+ }
+ }
+
if (!inside_try_finally)
ec.EndExceptionBlock ();
+
+ if (state_variable != null) {
+ ec.Emit (OpCodes.Ldloc, state_variable);
+
+ var labels = new Label [catch_sm.Count + 1];
+ for (int i = 0; i < labels.Length; ++i) {
+ labels [i] = ec.DefineLabel ();
+ }
+
+ var end = ec.DefineLabel ();
+ ec.Emit (OpCodes.Switch, labels);
+
+ // 0 value is default label
+ ec.MarkLabel (labels [0]);
+ ec.Emit (OpCodes.Br, end);
+
+ var atv = ec.AsyncThrowVariable;
+ Catch c = null;
+ for (int i = 0; i < catch_sm.Count; ++i) {
+ if (c != null && c.Block.HasReachableClosingBrace)
+ ec.Emit (OpCodes.Br, end);
+
+ ec.MarkLabel (labels [i + 1]);
+ c = catch_sm [i];
+ ec.AsyncThrowVariable = c.Variable;
+ c.Block.Emit (ec);
+ }
+ ec.AsyncThrowVariable = atv;
+
+ ec.MarkLabel (end);
+ }
}
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
public VariableDeclaration (LocalVariable li, Location loc)
: base (li)
{
+ reachable = true;
this.loc = loc;
}
public RuntimeDispose (LocalVariable lv, Location loc)
: base (lv, loc)
{
+ reachable = true;
}
protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)