var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
- fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
+ fc.BranchDefiniteAssignment (fc.DefiniteAssignmentOnTrue);
+ var labels = fc.CopyLabelStack ();
var res = TrueStatement.FlowAnalysis (fc);
+ fc.SetLabelStack (labels);
+
if (FalseStatement == null) {
+
var c = expr as Constant;
if (c != null && !c.IsDefaultValue)
return true_returns;
if (true_returns) {
fc.DefiniteAssignment = da_false;
- return FalseStatement.FlowAnalysis (fc);
+
+ res = FalseStatement.FlowAnalysis (fc);
+ fc.SetLabelStack (labels);
+ return res;
}
var da_true = fc.DefiniteAssignment;
fc.DefiniteAssignment = da_false;
+
res &= FalseStatement.FlowAnalysis (fc);
+ fc.SetLabelStack (labels);
+
if (!TrueStatement.IsUnreachable) {
if (false_returns || FalseStatement.IsUnreachable)
fc.DefiniteAssignment = da_true;
ec.Emit (OpCodes.Br, ec.LoopBegin);
ec.MarkLabel (while_loop);
+ expr.EmitPrepare (ec);
+
Statement.Emit (ec);
ec.MarkLabel (ec.LoopBegin);
{
expr.FlowAnalysisConditional (fc);
- fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
+ fc.BranchDefiniteAssignment (fc.DefiniteAssignmentOnTrue);
+
Statement.FlowAnalysis (fc);
//
public class Return : ExitStatement
{
Expression expr;
+ bool expr_returns;
public Return (Expression expr, Location l)
{
if (expr == null)
return false;
+ if (expr is ReferenceExpression && block_return_type.Kind != MemberKind.ByRef) {
+ ec.Report.Error (8149, loc, "By-reference returns can only be used in methods that return by reference");
+ return false;
+ }
+
if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
- expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
+ if (block_return_type.Kind == MemberKind.ByRef) {
+ var ref_expr = Expr as ReferenceExpression;
+ if (ref_expr == null) {
+ ec.Report.Error (8150, loc, "By-reference return is required when method returns by reference");
+ return false;
+ }
+
+ var byref_return = (ReferenceContainer)block_return_type;
- if (expr == null) {
- if (am != null && block_return_type == ec.ReturnType) {
- ec.Report.Error (1662, loc,
- "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
- am.ContainerType, am.GetSignatureForError ());
+ if (expr.Type != byref_return.Element) {
+ ec.Report.Error (8151, loc, "The return by reference expression must be of type `{0}' because this method returns by reference",
+ byref_return.GetSignatureForError ());
+ return false;
+ }
+ } else {
+
+ expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
+
+ if (expr == null) {
+ if (am != null && block_return_type == ec.ReturnType) {
+ ec.Report.Error (1662, loc,
+ "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
+ am.ContainerType, am.GetSignatureForError ());
+ }
+ return false;
}
- return false;
}
}
// Special case hoisted return value (happens in try/finally scenario)
//
if (ec.TryFinallyUnwind != null) {
- if (storey.HoistedReturnValue is VariableReference) {
- storey.HoistedReturnValue = ec.GetTemporaryField (storey.HoistedReturnValue.Type);
- }
-
- exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
+ exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
}
var async_return = (IAssignMethod)storey.HoistedReturnValue;
expr.Emit (ec);
if (ec.TryFinallyUnwind != null)
- exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
+ exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
}
ec.Emit (OpCodes.Leave, exit_label);
if (expr != null)
expr.FlowAnalysis (fc);
- base.DoFlowAnalysis (fc);
+ if (!expr_returns)
+ base.DoFlowAnalysis (fc);
+
return true;
}
public override Reachability MarkReachable (Reachability rc)
{
base.MarkReachable (rc);
+
+ if (Expr != null) {
+ rc = Expr.MarkReachable (rc);
+ expr_returns = rc.IsUnreachable;
+ }
+
return Reachability.CreateUnreachable ();
}
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
{
+ // Goto to unreachable label
+ if (label == null)
+ return true;
+
if (fc.AddReachedLabel (label))
return true;
if (try_finally != null) {
if (try_finally.FinallyBlock.HasReachableClosingBrace) {
- label.AddGotoReference (rc, false);
+ label.AddGotoReference (rc);
} else {
- label.AddGotoReference (rc, true);
+ label = null;
}
} else {
- label.AddGotoReference (rc, false);
+ label.AddGotoReference (rc);
}
return Reachability.CreateUnreachable ();
protected override void DoEmit (EmitContext ec)
{
+ // This should only happen for goto from try block to unrechable label
if (label == null)
- throw new InternalErrorException ("goto emitted before target resolved");
+ return;
Label l = label.LabelTarget (ec);
if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
- l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block);
+ l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block, unwind_protect);
}
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
string name;
bool defined;
bool referenced;
- bool finalTarget;
Label label;
Block block;
{
LabelTarget (ec);
ec.MarkLabel (label);
-
- if (finalTarget)
- ec.Emit (OpCodes.Br_S, label);
}
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
return rc;
}
- public void AddGotoReference (Reachability rc, bool finalTarget)
+ public void AddGotoReference (Reachability rc)
{
if (referenced)
return;
referenced = true;
MarkReachable (rc);
- //
- // Label is final target when goto jumps out of try block with
- // finally clause. In that case we need leave with target but in C#
- // terms the label is unreachable. Using finalTarget we emit
- // explicit label not just marker
- //
- if (finalTarget) {
- this.finalTarget = true;
- return;
- }
-
block.ScanGotoJump (this);
}
}
}
+ public static Expression ConvertType (ResolveContext rc, Expression expr)
+ {
+ var et = rc.BuiltinTypes.Exception;
+ if (Convert.ImplicitConversionExists (rc, expr, et))
+ expr = Convert.ImplicitConversion (rc, expr, et, expr.Location);
+ else {
+ rc.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
+ expr = EmptyCast.Create (expr, et);
+ }
+
+ return expr;
+ }
+
public override bool Resolve (BlockContext ec)
{
if (expr == null) {
if (expr == null)
return false;
- var et = ec.BuiltinTypes.Exception;
- if (Convert.ImplicitConversionExists (ec, expr, et))
- expr = Convert.ImplicitConversion (ec, expr, et, loc);
- else
- ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
+ expr = ConvertType (ec, expr);
return true;
}
if (ec.TryFinallyUnwind != null) {
var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
- l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
+ l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
}
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
if (ec.TryFinallyUnwind != null) {
var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
- l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
+ l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
}
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
}
if (type == null) {
- type = type_expr.ResolveAsType (bc);
+ type = ResolveTypeExpression (bc);
if (type == null)
return false;
}
if (initializer != null) {
+ if (li.IsByRef) {
+ if (!(initializer is ReferenceExpression)) {
+ bc.Report.Error (8172, loc, "Cannot initialize a by-reference variable `{0}' with a value", li.Name);
+ return false;
+ }
+
+ if (bc.CurrentAnonymousMethod is AsyncInitializer) {
+ bc.Report.Error (8177, loc, "Async methods cannot use by-reference variables");
+ } else if (bc.CurrentIterator != null) {
+ bc.Report.Error (8176, loc, "Iterators cannot use by-reference variables");
+ }
+
+ } else {
+ if (initializer is ReferenceExpression) {
+ bc.Report.Error (8171, loc, "Cannot initialize a by-value variable `{0}' with a reference expression", li.Name);
+ return false;
+ }
+ }
+
initializer = ResolveInitializer (bc, li, initializer);
// li.Variable.DefinitelyAssigned
}
return a.ResolveStatement (bc);
}
+ protected virtual TypeSpec ResolveTypeExpression (BlockContext bc)
+ {
+ return type_expr.ResolveAsType (bc);
+ }
+
protected override void DoEmit (EmitContext ec)
{
li.CreateBuilder (ec);
public override Reachability MarkReachable (Reachability rc)
{
- var init = initializer as ExpressionStatement;
- if (init != null)
- init.MarkReachable (rc);
-
- return base.MarkReachable (rc);
+ base.MarkReachable (rc);
+ return initializer == null ? rc : initializer.MarkReachable (rc);
}
protected override void CloneTo (CloneContext clonectx, Statement target)
public override void Emit (EmitContext ec)
{
+ if (!Variable.IsUsed)
+ ec.Report.Warning (219, 3, loc, "The constant `{0}' is never used", Variable.Name);
+
// Nothing to emit, not even sequence point
}
UsingVariable = 1 << 7,
IsLocked = 1 << 8,
SymbolFileHidden = 1 << 9,
+ ByRef = 1 << 10,
ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
}
}
}
+ public bool Created {
+ get {
+ return builder != null;
+ }
+ }
+
public bool IsDeclared {
get {
return type != null;
}
}
+ public bool IsByRef => (flags & Flags.ByRef) != 0;
+
public bool IsCompilerGenerated {
get {
return (flags & Flags.CompilerGenerated) != 0;
}
}
+ public bool IsUsed {
+ get {
+ return (flags & Flags.Used) != 0;
+ }
+ }
+
public bool IsFixed {
get {
return (flags & Flags.FixedVariable) != 0;
}
+ set {
+ flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
+ }
}
bool INamedBlockVariable.IsParameter {
throw new InternalErrorException ("Already created variable `{0}'", name);
}
- //
- // All fixed variabled are pinned, a slot has to be alocated
- //
- builder = ec.DeclareLocal (Type, IsFixed);
+ if (IsByRef) {
+ builder = ec.DeclareLocal (ReferenceContainer.MakeType (ec.Module, Type), IsFixed);
+ } else {
+ //
+ // All fixed variabled are pinned, a slot has to be alocated
+ //
+ builder = ec.DeclareLocal(Type, IsFixed);
+ }
+
if ((flags & Flags.SymbolFileHidden) == 0)
ec.DefineLocalVariable (name, builder);
}
public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
{
- if (IsConstant && const_value != null)
+ if (IsConstant && const_value != null) {
+ SetIsUsed ();
return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
+ }
return new LocalVariableReference (this, loc);
}
if ((flags & Flags.CompilerGenerated) != 0)
CreateBuilder (ec);
- ec.Emit (OpCodes.Ldloca, builder);
+ if (IsByRef)
+ ec.Emit (OpCodes.Ldloc, builder);
+ else
+ ec.Emit (OpCodes.Ldloca, builder);
}
public static string GetCompilerGeneratedName (Block block)
AwaitBlock = 1 << 13,
FinallyBlock = 1 << 14,
CatchBlock = 1 << 15,
+ HasReferenceToStoreyForInstanceLambdas = 1 << 16,
Iterator = 1 << 20,
NoFlowAnalysis = 1 << 21,
InitializationEmitted = 1 << 22
if (end_unreachable) {
bool after_goto_case = goto_flow_analysis && s is GotoCase;
- for (++startIndex; startIndex < statements.Count; ++startIndex) {
- s = statements[startIndex];
- if (s is SwitchLabel) {
- if (!after_goto_case)
- s.FlowAnalysis (fc);
+ var f = s as TryFinally;
+ if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
+ //
+ // Special case for try-finally with unreachable code after
+ // finally block. Try block has to include leave opcode but there is
+ // no label to leave to after unreachable finally block closing
+ // brace. This sentinel ensures there is always IL instruction to
+ // leave to even if we know it'll never be reached.
+ //
+ statements.Insert (startIndex + 1, new SentinelStatement ());
+ } else {
+ for (++startIndex; startIndex < statements.Count; ++startIndex) {
+ s = statements [startIndex];
+ if (s is SwitchLabel) {
+ if (!after_goto_case)
+ s.FlowAnalysis (fc);
- break;
- }
+ break;
+ }
- if (s.IsUnreachable) {
- s.FlowAnalysis (fc);
- statements [startIndex] = RewriteUnreachableStatement (s);
+ if (s.IsUnreachable) {
+ s.FlowAnalysis (fc);
+ statements [startIndex] = RewriteUnreachableStatement (s);
+ }
}
}
// L:
// v = 1;
- if (s is BlockVariable || s is EmptyStatement)
+ if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
return s;
return new EmptyStatement (s.loc);
public class ExplicitBlock : Block
{
protected AnonymousMethodStorey am_storey;
+ int debug_scope_index;
public ExplicitBlock (Block parent, Location start, Location end)
: this (parent, (Flags) 0, start, end)
public override void Emit (EmitContext ec)
{
- if (Parent != null)
- ec.BeginScope ();
+ // TODO: It's needed only when scope has variable (normal or lifted)
+ var scopeIndex = GetDebugSymbolScopeIndex ();
+ if (scopeIndex > 0) {
+ ec.BeginScope (scopeIndex);
+ }
EmitScopeInitialization (ec);
DoEmit (ec);
- if (Parent != null)
+ if (scopeIndex > 0)
ec.EndScope ();
if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
//
storey.CreateContainer ();
storey.DefineContainer ();
+ storey.ExpandBaseInterfaces ();
if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
break;
}
}
-
+
//
// We are the first storey on path and 'this' has to be hoisted
//
//
// If we are state machine with no parent. We can hook into parent without additional
- // reference and capture this directly
+ // reference and capture this directly
//
ExplicitBlock parent_storey_block = pb;
while (parent_storey_block.Parent != null) {
var parent_this_block = pb;
while (parent_this_block.Parent != null) {
parent_this_block = parent_this_block.Parent.ParametersBlock;
- if (parent_this_block.StateMachine != null) {
+ if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
break;
}
}
storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
}
+ public void DisableDebugScopeIndex ()
+ {
+ debug_scope_index = -1;
+ }
+
+ public virtual int GetDebugSymbolScopeIndex ()
+ {
+ if (debug_scope_index == 0)
+ debug_scope_index = ++ParametersBlock.debug_scope_index;
+
+ return debug_scope_index;
+ }
+
public void RegisterAsyncAwait ()
{
var block = this;
#region Properties
+ public bool HasReferenceToStoreyForInstanceLambdas {
+ get {
+ return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
+ }
+ set {
+ flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
+ }
+ }
+
public bool IsAsync {
get {
return (flags & Flags.HasAsyncModifier) != 0;
return res;
}
+ public override int GetDebugSymbolScopeIndex ()
+ {
+ return 0;
+ }
+
public LabeledStatement GetLabel (string name, Block block)
{
//
return new ParameterReference (parameter_info[index], loc);
}
- public Statement PerformClone ()
+ public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
{
+ undeclaredVariables = TopBlock.GetUndeclaredVariables ();
+
CloneContext clonectx = new CloneContext ();
return Clone (clonectx);
}
base.CheckControlExit (fc, dat);
}
+ public HashSet<LocalVariable> GetUndeclaredVariables ()
+ {
+ if (names == null)
+ return null;
+
+ HashSet<LocalVariable> variables = null;
+
+ foreach (var entry in names) {
+ var complex = entry.Value as List<INamedBlockVariable>;
+ if (complex != null) {
+ foreach (var centry in complex) {
+ if (IsUndeclaredVariable (centry)) {
+ if (variables == null)
+ variables = new HashSet<LocalVariable> ();
+
+ variables.Add ((LocalVariable) centry);
+ }
+ }
+ } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
+ if (variables == null)
+ variables = new HashSet<LocalVariable> ();
+
+ variables.Add ((LocalVariable)entry.Value);
+ }
+ }
+
+ return variables;
+ }
+
+ static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
+ {
+ var lv = namedBlockVariable as LocalVariable;
+ return lv != null && !lv.IsDeclared;
+ }
+
+ public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
+ {
+ if (names == null)
+ return;
+
+ foreach (var entry in names) {
+ var complex = entry.Value as List<INamedBlockVariable>;
+ if (complex != null) {
+ foreach (var centry in complex) {
+ var lv = centry as LocalVariable;
+ if (lv != null && undeclaredVariables.Contains (lv)) {
+ lv.Type = null;
+ }
+ }
+ } else {
+ var lv = entry.Value as LocalVariable;
+ if (lv != null && undeclaredVariables.Contains (lv))
+ lv.Type = null;
+ }
+ }
+ }
+
public override void Emit (EmitContext ec)
{
if (Report.Errors > 0)
continue;
}
- if (constant_label != null && constant_label != sl)
+ if (section_rc.IsUnreachable) {
+ //
+ // Common case. Previous label section end is unreachable as
+ // it ends with break, return, etc. For next section revert
+ // to reachable again unless we have constant switch block
+ //
+ section_rc = constant_label != null && constant_label != sl ?
+ Reachability.CreateUnreachable () :
+ new Reachability ();
+ } else if (prev_label != null) {
+ //
+ // Error case as control cannot fall through from one case label
+ //
+ sl.SectionStart = false;
+ s = new MissingBreak (prev_label);
+ s.MarkReachable (rc);
+ block.Statements.Insert (i - 1, s);
+ ++i;
+ } else if (constant_label != null && constant_label != sl) {
+ //
+ // Special case for the first unreachable label in constant
+ // switch block
+ //
section_rc = Reachability.CreateUnreachable ();
- else if (section_rc.IsUnreachable) {
- section_rc = new Reachability ();
- } else {
- if (prev_label != null) {
- sl.SectionStart = false;
- s = new MissingBreak (prev_label);
- s.MarkReachable (rc);
- block.Statements.Insert (i - 1, s);
- ++i;
- }
}
prev_label = sl;
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
{
var res = stmt.FlowAnalysis (fc);
- parent = null;
+ parent_try_block = null;
return res;
}
{
bool ok;
- parent = bc.CurrentTryBlock;
+ parent_try_block = bc.CurrentTryBlock;
bc.CurrentTryBlock = this;
- using (bc.Set (ResolveContext.Options.TryScope)) {
+ if (stmt is TryCatch) {
ok = stmt.Resolve (bc);
+ } else {
+ using (bc.Set (ResolveContext.Options.TryScope)) {
+ ok = stmt.Resolve (bc);
+ }
}
- bc.CurrentTryBlock = parent;
+ bc.CurrentTryBlock = parent_try_block;
//
// Finally block inside iterator is called from MoveNext and
{
protected List<ResumableStatement> resume_points;
protected int first_resume_pc;
- protected ExceptionStatement parent;
+ protected ExceptionStatement parent_try_block;
+ protected int first_catch_resume_pc = -1;
protected ExceptionStatement (Location loc)
{
this.loc = loc;
}
- protected virtual void EmitBeginException (EmitContext ec)
- {
- ec.BeginExceptionBlock ();
- }
-
protected virtual void EmitTryBodyPrepare (EmitContext ec)
{
StateMachineInitializer state_machine = null;
ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
}
- EmitBeginException (ec);
+ //
+ // The resume points in catch section when this is try-catch-finally
+ //
+ if (IsRewrittenTryCatchFinally ()) {
+ ec.BeginExceptionBlock ();
- if (resume_points != null) {
- ec.MarkLabel (resume_point);
+ if (first_catch_resume_pc >= 0) {
+
+ ec.MarkLabel (resume_point);
+
+ // For normal control flow, we want to fall-through the Switch
+ // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
+ ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
+ ec.EmitInt (first_resume_pc + first_catch_resume_pc);
+ ec.Emit (OpCodes.Sub);
+
+ var labels = new Label [resume_points.Count - first_catch_resume_pc];
+ for (int i = 0; i < labels.Length; ++i)
+ labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
+ ec.Emit (OpCodes.Switch, labels);
+ }
+ }
+
+ ec.BeginExceptionBlock ();
+
+ //
+ // The resume points for try section
+ //
+ if (resume_points != null && first_catch_resume_pc != 0) {
+ if (first_catch_resume_pc < 0)
+ ec.MarkLabel (resume_point);
// For normal control flow, we want to fall-through the Switch
// So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
ec.EmitInt (first_resume_pc);
ec.Emit (OpCodes.Sub);
- Label[] labels = new Label[resume_points.Count];
- for (int i = 0; i < resume_points.Count; ++i)
+ var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
+ for (int i = 0; i < labels.Length; ++i)
labels[i] = resume_points[i].PrepareForEmit (ec);
ec.Emit (OpCodes.Switch, labels);
}
}
- public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
+ bool IsRewrittenTryCatchFinally ()
{
- if (parent != null) {
- // TODO: MOVE to virtual TryCatch
- var tc = this as TryCatch;
- var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
+ var tf = this as TryFinally;
+ if (tf == null)
+ return false;
- pc = parent.AddResumePoint (s, pc, stateMachine);
+ var tc = tf.Statement as TryCatch;
+ if (tc == null)
+ return false;
+
+ return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
+ }
+
+ public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
+ {
+ if (parent_try_block != null) {
+ pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
} else {
pc = stateMachine.AddResumePoint (this);
}
if (pc != first_resume_pc + resume_points.Count)
throw new InternalErrorException ("missed an intervening AddResumePoint?");
+ var tf = this as TryFinally;
+ if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
+ first_catch_resume_pc = resume_points.Count;
+ }
+
resume_points.Add (stmt);
return pc;
}
public override bool Resolve (BlockContext ec)
{
if (ec.CurrentIterator != null)
- ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
+ Expression.UnsafeInsideIteratorError (ec, loc);
using (ec.Set (ResolveContext.Options.UnsafeScope))
return Block.Resolve (ec);
LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
vi.Location);
pinned_string.Type = rc.BuiltinTypes.String;
+ vi.IsFixed = false;
eclass = ExprClass.Variable;
type = rc.BuiltinTypes.Int;
{
ExplicitBlock fini;
List<DefiniteAssignmentBitSet> try_exit_dat;
- List<Label> redirected_jumps;
+ List<Tuple<Label, bool>> redirected_jumps;
Label? start_fin_label;
public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
return ok;
}
- protected override void EmitBeginException (EmitContext ec)
- {
- if (fini.HasAwait && stmt is TryCatch)
- ec.BeginExceptionBlock ();
-
- base.EmitBeginException (ec);
- }
-
protected override void EmitTryBody (EmitContext ec)
{
if (fini.HasAwait) {
ec.TryFinallyUnwind.Add (this);
stmt.Emit (ec);
- if (stmt is TryCatch)
+ if (first_catch_resume_pc < 0 && stmt is TryCatch)
ec.EndExceptionBlock ();
ec.TryFinallyUnwind.Remove (this);
ec.Emit (OpCodes.Stloc, temp);
var exception_field = ec.GetTemporaryField (type);
+ exception_field.AutomaticallyReuse = false;
ec.EmitThis ();
ec.Emit (OpCodes.Ldloc, temp);
exception_field.EmitAssignFromStack (ec);
ec.Emit (OpCodes.Throw);
ec.MarkLabel (skip_throw);
- exception_field.IsAvailableForReuse = true;
+ exception_field.PrepareCleanup (ec);
EmitUnwindFinallyTable (ec);
}
return false;
}
- public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
+ public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock, bool unwindProtect)
{
int idx;
if (labelBlock != null) {
if (labelBlock != null && !fin.IsParentBlock (labelBlock))
break;
- fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
+ fin.EmitRedirectedExit (ec, label, initializer, set_return_state, unwindProtect);
set_return_state = false;
if (fin.start_fin_label == null) {
return label;
}
- public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
+ public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer, bool unwindProtect)
{
- return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
+ return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null, unwindProtect);
}
- void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
+ void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState, bool unwindProtect)
{
if (redirected_jumps == null) {
- redirected_jumps = new List<Label> ();
+ redirected_jumps = new List<Tuple<Label, bool>> ();
// Add fallthrough label
- redirected_jumps.Add (ec.DefineLabel ());
+ redirected_jumps.Add (Tuple.Create (ec.DefineLabel (), false));
if (setReturnState)
initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
}
- int index = redirected_jumps.IndexOf (label);
+ int index = redirected_jumps.FindIndex (l => l.Item1 == label);
if (index < 0) {
- redirected_jumps.Add (label);
+ redirected_jumps.Add (Tuple.Create (label, unwindProtect));
index = redirected_jumps.Count - 1;
}
var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
initializer.HoistedReturnState.EmitLoad (ec);
- ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
+
+ var jumps_table = new Label [redirected_jumps.Count];
+ List<Tuple<Label, Label>> leave_redirect = null;
+ for (int i = 0; i < jumps_table.Length; ++i) {
+ var val = redirected_jumps [i];
+
+ if (val.Item2) {
+ if (leave_redirect == null)
+ leave_redirect = new List<Tuple<Label, Label>> ();
+ var label = ec.DefineLabel ();
+ leave_redirect.Add (Tuple.Create (label, val.Item1));
+ jumps_table [i] = label;
+ } else {
+ jumps_table [i] = val.Item1;
+ }
+ }
+
+ ec.Emit (OpCodes.Switch, jumps_table);
+
+ if (leave_redirect != null) {
+ foreach (var entry in leave_redirect) {
+ ec.MarkLabel (entry.Item1);
+ ec.Emit (OpCodes.Leave, entry.Item2);
+ }
+ }
// Mark fallthrough label
- ec.MarkLabel (redirected_jumps [0]);
+ ec.MarkLabel (jumps_table [0]);
}
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
}
}
+ public bool HasClauseWithAwait {
+ get {
+ return catch_sm != null;
+ }
+ }
+
public bool IsTryCatchFinally {
get {
return inside_try_finally;
bool ok;
using (bc.Set (ResolveContext.Options.TryScope)) {
- parent = bc.CurrentTryBlock;
+
+ parent_try_block = bc.CurrentTryBlock;
if (IsTryCatchFinally) {
ok = Block.Resolve (bc);
using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
bc.CurrentTryBlock = this;
ok = Block.Resolve (bc);
- bc.CurrentTryBlock = parent;
+ bc.CurrentTryBlock = parent_try_block;
}
}
}
+ var prev_catch = bc.CurrentTryCatch;
+ bc.CurrentTryCatch = this;
+
for (int i = 0; i < clauses.Count; ++i) {
var c = clauses[i];
}
}
+ bc.CurrentTryCatch = prev_catch;
+
return base.Resolve (bc) && ok;
}
}
}
- if (!inside_try_finally)
+ if (state_variable == null) {
+ if (!inside_try_finally)
+ ec.EndExceptionBlock ();
+ } else {
ec.EndExceptionBlock ();
- if (state_variable != null) {
ec.Emit (OpCodes.Ldloc, state_variable);
var labels = new Label [catch_sm.Count + 1];
ec.Emit (OpCodes.Br, end);
ec.MarkLabel (labels [i + 1]);
+
+ ec.EmitInt (0);
+ ec.Emit (OpCodes.Stloc, state_variable);
+
c = catch_sm [i];
ec.AsyncThrowVariable = c.Variable;
c.Block.Emit (ec);
}
fc.DefiniteAssignment = try_fc ?? start_fc;
- parent = null;
+ parent_try_block = null;
return res;
}
for_each.variable.Type = var_type;
+ var prev_block = ec.CurrentBlock;
+ ec.CurrentBlock = variables_block;
var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
+ ec.CurrentBlock = prev_block;
+
if (variable_ref == null)
return false;
return false;
}
+ var prev_block = ec.CurrentBlock;
+ ec.CurrentBlock = for_each.variable.Block;
var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
+ ec.CurrentBlock = prev_block;
if (variable_ref == null)
return false;
ec.LoopBegin = ec.DefineLabel ();
ec.LoopEnd = ec.DefineLabel ();
- if (!(Statement is Block))
- ec.BeginCompilerScope ();
+ ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
+ body.Explicit.DisableDebugScopeIndex ();
variable.CreateBuilder (ec);
Statement.Emit (ec);
- if (!(Statement is Block))
- ec.EndScope ();
+ ec.EndScope ();
ec.LoopBegin = old_begin;
ec.LoopEnd = old_end;
return visitor.Visit (this);
}
}
+
+ class SentinelStatement: Statement
+ {
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ }
+
+ protected override void DoEmit (EmitContext ec)
+ {
+ var l = ec.DefineLabel ();
+ ec.MarkLabel (l);
+ ec.Emit (OpCodes.Br_S, l);
+ }
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ throw new NotImplementedException ();
+ }
+ }
}