2006-05-18 Raja R Harinath <rharinath@novell.com>
+ Fix #77601
+ * statement.cs (Goto.Resolve): Move responsibility for resolving
+ 'goto' to FlowBranching.AddGotoOrigin.
+ (Goto.SetResolvedTarget): New. Callback to set the
+ LabeledStatement that's the target of the goto.
+ (Goto.DoEmit): Use Leave instead of Br when crossing an
+ unwind-protect boundary.
+ * flowanalysis.cs (FlowBranching.AddGotoOrigin): Rename from
+ LookupLabel and adjust to new semantics.
+ (FlowBranchingToplevel.AddGotoOrigin): Likewise.
+ (FlowBranchingBlock.AddGotoOrigin): Likewise. Use
+ Goto.SetResolvedTarget to update target.
+ (FlowBranchingLabeled.AddGotoOrigin): Likewise.
+ (FlowBranchingException.AddGotoOrigin): Rewrite to be similar to
+ AddBreakOrigin & co. Delay propagation until ...
+ (FlowBranchingException.Merge): ... this.
+
* statement.cs (Block.Resolve): Always depend on flow-branching to
determine unreachability. Kill workaround that originally emitted
only one statement after an "unreachable" label (see infloop in
protected abstract void AddSibling (UsageVector uv);
- public virtual LabeledStatement LookupLabel (string name, Location loc)
- {
- return Parent.LookupLabel (name, loc);
- }
-
protected abstract UsageVector Merge ();
// <summary>
return Parent.AddReturnOrigin (vector, loc);
}
+ // returns true if we crossed an unwind-protected region (try/catch/finally, lock, using, ...)
+ public virtual bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
+ {
+ return Parent.AddGotoOrigin (vector, goto_stmt);
+ }
+
public virtual void StealFinallyClauses (ref ArrayList list)
{
Parent.StealFinallyClauses (ref list);
sibling_list = sibling;
}
- public override LabeledStatement LookupLabel (string name, Location loc)
+ public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
{
- LabeledStatement stmt = Block == null ? null : Block.LookupLabel (name);
+ LabeledStatement stmt = Block == null ? null : Block.LookupLabel (goto_stmt.Target);
if (stmt == null)
- return Parent.LookupLabel (name, loc);
+ return Parent.AddGotoOrigin (vector, goto_stmt);
- stmt.AddReference ();
- return stmt;
+ // forward jump
+ goto_stmt.SetResolvedTarget (stmt);
+ stmt.AddUsageVector (vector);
+ return false;
}
protected override UsageVector Merge ()
public class FlowBranchingLabeled : FlowBranchingBlock
{
- bool unreachable, backward_jump;
LabeledStatement stmt;
UsageVector actual;
this.stmt = stmt;
CurrentUsageVector.MergeOrigins (stmt.JumpOrigins);
actual = CurrentUsageVector.Clone ();
- unreachable = actual.Reachability.IsUnreachable;
// stand-in for backward jumps
CurrentUsageVector.Reachability.Meet (Reachability.Always ());
}
- public override LabeledStatement LookupLabel (string name, Location loc)
+ public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
{
- if (name != stmt.Name)
- return Parent.LookupLabel (name, loc);
+ if (goto_stmt.Target != stmt.Name)
+ return Parent.AddGotoOrigin (vector, goto_stmt);
- unreachable = false;
- backward_jump = true;
+ // backward jump
+ goto_stmt.SetResolvedTarget (stmt);
+ actual.MergeOrigins (vector.Clone ());
- stmt.AddReference ();
- return stmt;
+ return false;
}
protected override UsageVector Merge ()
{
UsageVector vector = base.Merge ();
- if (unreachable)
+ if (actual.Reachability.IsUnreachable)
Report.Warning (162, 2, stmt.loc, "Unreachable code detected");
- if (backward_jump)
- return vector;
-
actual.MergeChild (vector, false);
return actual;
}
// nothing to do
}
- public override LabeledStatement LookupLabel (string name, Location loc)
+ public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
{
+ string name = goto_stmt.Target;
LabeledStatement s = Block.LookupLabel (name);
if (s != null)
throw new InternalErrorException ("Shouldn't get here");
- if (Parent != null) {
- s = Parent.LookupLabel (name, loc);
- if (s != null) {
- Report.Error (1632, loc, "Control cannot leave the body of an anonymous method");
- return null;
- }
+ if (Parent == null) {
+ Report.Error (159, goto_stmt.loc, "No such label `{0}' in this scope", name);
+ return false;
}
- Report.Error (159, loc, "No such label `{0}' in this scope", name);
- return null;
+ int errors = Report.Errors;
+ Parent.AddGotoOrigin (vector, goto_stmt);
+ if (errors == Report.Errors)
+ Report.Error (1632, goto_stmt.loc, "Control cannot leave the body of an anonymous method");
+ return false;
}
public Reachability End ()
UsageVector break_origins;
UsageVector continue_origins;
UsageVector return_origins;
+ GotoOrigin goto_origins;
+
+ class GotoOrigin {
+ public GotoOrigin Next;
+ public Goto GotoStmt;
+ public UsageVector Vector;
+
+ public GotoOrigin (UsageVector vector, Goto goto_stmt, GotoOrigin next)
+ {
+ Vector = vector;
+ GotoStmt = goto_stmt;
+ Next = next;
+ }
+ }
bool emit_finally;
return true;
}
+ public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
+ {
+ LabeledStatement s = current_vector.Block == null ? null : current_vector.Block.LookupLabel (goto_stmt.Target);
+ if (s != null)
+ throw new InternalErrorException ("Shouldn't get here");
+
+ if (finally_vector != null)
+ Report.Error (157, goto_stmt.loc, "Control cannot leave the body of a finally clause");
+ else
+ goto_origins = new GotoOrigin (vector.Clone (), goto_stmt, goto_origins);
+ return true;
+ }
+
public override void StealFinallyClauses (ref ArrayList list)
{
if (list == null)
get { return emit_finally; }
}
- public override LabeledStatement LookupLabel (string name, Location loc)
- {
- if (current_vector.Block == null)
- return base.LookupLabel (name, loc);
-
- LabeledStatement s = current_vector.Block.LookupLabel (name);
- if (s != null)
- return s;
-
- if (finally_vector != null) {
- Report.Error (157, loc, "Control cannot leave the body of a finally clause");
- return null;
- }
-
- return base.LookupLabel (name, loc);
- }
-
protected override UsageVector Merge ()
{
Report.Debug (2, " MERGING TRY/CATCH", Name);
Parent.AddReturnOrigin (origin, origin.Location);
}
+ for (GotoOrigin origin = goto_origins; origin != null; origin = origin.Next) {
+ if (finally_vector != null)
+ origin.Vector.MergeChild (finally_vector, false);
+ if (!origin.Vector.Reachability.IsUnreachable)
+ Parent.AddGotoOrigin (origin.Vector, origin.GotoStmt);
+ }
+
return vector;
}
}
loc = l;
}
- bool in_exc;
+ bool unwind_protect;
public override bool Resolve (EmitContext ec)
{
}
int errors = Report.Errors;
- in_exc = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
- if (in_exc)
+ unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
+ if (unwind_protect)
ec.NeedReturnLabel ();
ec.CurrentBranching.CurrentUsageVector.Return ();
return errors == Report.Errors;
if (Expr != null) {
Expr.Emit (ec);
- if (in_exc)
+ if (unwind_protect)
ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
}
- if (in_exc)
+ if (unwind_protect)
ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
else
ec.ig.Emit (OpCodes.Ret);
public class Goto : Statement {
string target;
LabeledStatement label;
+ bool unwind_protect;
public override bool Resolve (EmitContext ec)
{
- label = ec.CurrentBranching.LookupLabel (target, loc);
- if (label == null)
- return false;
-
- // If this is a forward goto.
- if (!label.IsDefined)
- label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector);
-
+ int errors = Report.Errors;
+ unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
ec.CurrentBranching.CurrentUsageVector.Goto ();
-
- return true;
+ return errors == Report.Errors;
}
public Goto (string label, Location l)
get { return target; }
}
+ public void SetResolvedTarget (LabeledStatement label)
+ {
+ this.label = label;
+ label.AddReference ();
+ }
+
protected override void DoEmit (EmitContext ec)
{
+ if (label == null)
+ throw new InternalErrorException ("goto emitted before target resolved");
Label l = label.LabelTarget (ec);
- ec.ig.Emit (OpCodes.Br, l);
+ ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
}
}
loc = l;
}
- bool crossing_exc;
+ bool unwind_protect;
public override bool Resolve (EmitContext ec)
{
int errors = Report.Errors;
- crossing_exc = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
+ unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
ec.CurrentBranching.CurrentUsageVector.Goto ();
return errors == Report.Errors;
}
protected override void DoEmit (EmitContext ec)
{
- ec.ig.Emit (crossing_exc ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
+ ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
}
}
loc = l;
}
- bool crossing_exc;
+ bool unwind_protect;
public override bool Resolve (EmitContext ec)
{
int errors = Report.Errors;
- crossing_exc = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
+ unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
ec.CurrentBranching.CurrentUsageVector.Goto ();
return errors == Report.Errors;
}
protected override void DoEmit (EmitContext ec)
{
- ec.ig.Emit (crossing_exc ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
+ ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
}
}
2006-05-18 Raja R Harinath <rharinath@novell.com>
+ Fix #77601
+ * statement.cs (Goto.Resolve): Move responsibility for resolving
+ 'goto' to FlowBranching.AddGotoOrigin.
+ (Goto.SetResolvedTarget): New. Callback to set the
+ LabeledStatement that's the target of the goto.
+ (Goto.DoEmit): Use Leave instead of Br when crossing an
+ unwind-protect boundary.
+ * flowanalysis.cs (FlowBranching.AddGotoOrigin): Rename from
+ LookupLabel and adjust to new semantics.
+ (FlowBranchingToplevel.AddGotoOrigin): Likewise.
+ (FlowBranchingBlock.AddGotoOrigin): Likewise. Use
+ Goto.SetResolvedTarget to update target.
+ (FlowBranchingLabeled.AddGotoOrigin): Likewise.
+ (FlowBranchingException.AddGotoOrigin): Rewrite to be similar to
+ AddBreakOrigin & co. Delay propagation until ...
+ (FlowBranchingException.Merge): ... this.
+
* statement.cs (Block.Resolve): Always depend on flow-branching to
determine unreachability. Kill workaround that originally emitted
only one statement after an "unreachable" label (see infloop in
protected abstract void AddSibling (UsageVector uv);
- public virtual LabeledStatement LookupLabel (string name, Location loc)
- {
- return Parent.LookupLabel (name, loc);
- }
-
protected abstract UsageVector Merge ();
// <summary>
return Parent.AddReturnOrigin (vector, loc);
}
+ // returns true if we crossed an unwind-protected region (try/catch/finally, lock, using, ...)
+ public virtual bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
+ {
+ return Parent.AddGotoOrigin (vector, goto_stmt);
+ }
+
public virtual void StealFinallyClauses (ref ArrayList list)
{
Parent.StealFinallyClauses (ref list);
sibling_list = sibling;
}
- public override LabeledStatement LookupLabel (string name, Location loc)
+ public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
{
- LabeledStatement stmt = Block == null ? null : Block.LookupLabel (name);
+ LabeledStatement stmt = Block == null ? null : Block.LookupLabel (goto_stmt.Target);
if (stmt == null)
- return Parent.LookupLabel (name, loc);
+ return Parent.AddGotoOrigin (vector, goto_stmt);
- stmt.AddReference ();
- return stmt;
+ // forward jump
+ goto_stmt.SetResolvedTarget (stmt);
+ stmt.AddUsageVector (vector);
+ return false;
}
protected override UsageVector Merge ()
public class FlowBranchingLabeled : FlowBranchingBlock
{
- bool unreachable, backward_jump;
LabeledStatement stmt;
UsageVector actual;
this.stmt = stmt;
CurrentUsageVector.MergeOrigins (stmt.JumpOrigins);
actual = CurrentUsageVector.Clone ();
- unreachable = actual.Reachability.IsUnreachable;
// stand-in for backward jumps
CurrentUsageVector.Reachability.Meet (Reachability.Always ());
}
- public override LabeledStatement LookupLabel (string name, Location loc)
+ public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
{
- if (name != stmt.Name)
- return Parent.LookupLabel (name, loc);
+ if (goto_stmt.Target != stmt.Name)
+ return Parent.AddGotoOrigin (vector, goto_stmt);
- unreachable = false;
- backward_jump = true;
+ // backward jump
+ goto_stmt.SetResolvedTarget (stmt);
+ actual.MergeOrigins (vector.Clone ());
- stmt.AddReference ();
- return stmt;
+ return false;
}
protected override UsageVector Merge ()
{
UsageVector vector = base.Merge ();
- if (unreachable)
+ if (actual.Reachability.IsUnreachable)
Report.Warning (162, 2, stmt.loc, "Unreachable code detected");
- if (backward_jump)
- return vector;
-
actual.MergeChild (vector, false);
return actual;
}
// nothing to do
}
- public override LabeledStatement LookupLabel (string name, Location loc)
+ public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
{
+ string name = goto_stmt.Target;
LabeledStatement s = Block.LookupLabel (name);
if (s != null)
throw new InternalErrorException ("Shouldn't get here");
- if (Parent != null) {
- s = Parent.LookupLabel (name, loc);
- if (s != null) {
- Report.Error (1632, loc, "Control cannot leave the body of an anonymous method");
- return null;
- }
+ if (Parent == null) {
+ Report.Error (159, goto_stmt.loc, "No such label `{0}' in this scope", name);
+ return false;
}
- Report.Error (159, loc, "No such label `{0}' in this scope", name);
- return null;
+ int errors = Report.Errors;
+ Parent.AddGotoOrigin (vector, goto_stmt);
+ if (errors == Report.Errors)
+ Report.Error (1632, goto_stmt.loc, "Control cannot leave the body of an anonymous method");
+ return false;
}
public Reachability End ()
UsageVector break_origins;
UsageVector continue_origins;
UsageVector return_origins;
+ GotoOrigin goto_origins;
+
+ class GotoOrigin {
+ public GotoOrigin Next;
+ public Goto GotoStmt;
+ public UsageVector Vector;
+
+ public GotoOrigin (UsageVector vector, Goto goto_stmt, GotoOrigin next)
+ {
+ Vector = vector;
+ GotoStmt = goto_stmt;
+ Next = next;
+ }
+ }
bool emit_finally;
return true;
}
+ public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
+ {
+ LabeledStatement s = current_vector.Block == null ? null : current_vector.Block.LookupLabel (goto_stmt.Target);
+ if (s != null)
+ throw new InternalErrorException ("Shouldn't get here");
+
+ if (finally_vector != null)
+ Report.Error (157, goto_stmt.loc, "Control cannot leave the body of a finally clause");
+ else
+ goto_origins = new GotoOrigin (vector.Clone (), goto_stmt, goto_origins);
+ return true;
+ }
+
public override void StealFinallyClauses (ref ArrayList list)
{
if (list == null)
get { return emit_finally; }
}
- public override LabeledStatement LookupLabel (string name, Location loc)
- {
- if (current_vector.Block == null)
- return base.LookupLabel (name, loc);
-
- LabeledStatement s = current_vector.Block.LookupLabel (name);
- if (s != null)
- return s;
-
- if (finally_vector != null) {
- Report.Error (157, loc, "Control cannot leave the body of a finally clause");
- return null;
- }
-
- return base.LookupLabel (name, loc);
- }
-
protected override UsageVector Merge ()
{
Report.Debug (2, " MERGING TRY/CATCH", Name);
Parent.AddReturnOrigin (origin, origin.Location);
}
+ for (GotoOrigin origin = goto_origins; origin != null; origin = origin.Next) {
+ if (finally_vector != null)
+ origin.Vector.MergeChild (finally_vector, false);
+ if (!origin.Vector.Reachability.IsUnreachable)
+ Parent.AddGotoOrigin (origin.Vector, origin.GotoStmt);
+ }
+
return vector;
}
}
loc = l;
}
- bool in_exc;
+ bool unwind_protect;
public override bool Resolve (EmitContext ec)
{
}
int errors = Report.Errors;
- in_exc = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
- if (in_exc)
+ unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
+ if (unwind_protect)
ec.NeedReturnLabel ();
ec.CurrentBranching.CurrentUsageVector.Return ();
return errors == Report.Errors;
if (Expr != null) {
Expr.Emit (ec);
- if (in_exc)
+ if (unwind_protect)
ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
}
- if (in_exc)
+ if (unwind_protect)
ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
else
ec.ig.Emit (OpCodes.Ret);
public class Goto : Statement {
string target;
LabeledStatement label;
+ bool unwind_protect;
public override bool Resolve (EmitContext ec)
{
- label = ec.CurrentBranching.LookupLabel (target, loc);
- if (label == null)
- return false;
-
- // If this is a forward goto.
- if (!label.IsDefined)
- label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector);
-
+ int errors = Report.Errors;
+ unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
ec.CurrentBranching.CurrentUsageVector.Goto ();
-
- return true;
+ return errors == Report.Errors;
}
public Goto (string label, Location l)
get { return target; }
}
+ public void SetResolvedTarget (LabeledStatement label)
+ {
+ this.label = label;
+ label.AddReference ();
+ }
+
protected override void DoEmit (EmitContext ec)
{
+ if (label == null)
+ throw new InternalErrorException ("goto emitted before target resolved");
Label l = label.LabelTarget (ec);
- ec.ig.Emit (OpCodes.Br, l);
+ ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
}
}
loc = l;
}
- bool crossing_exc;
+ bool unwind_protect;
public override bool Resolve (EmitContext ec)
{
int errors = Report.Errors;
- crossing_exc = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
+ unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
ec.CurrentBranching.CurrentUsageVector.Goto ();
return errors == Report.Errors;
}
protected override void DoEmit (EmitContext ec)
{
- ec.ig.Emit (crossing_exc ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
+ ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
}
}
loc = l;
}
- bool crossing_exc;
+ bool unwind_protect;
public override bool Resolve (EmitContext ec)
{
int errors = Report.Errors;
- crossing_exc = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
+ unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
ec.CurrentBranching.CurrentUsageVector.Goto ();
return errors == Report.Errors;
}
protected override void DoEmit (EmitContext ec)
{
- ec.ig.Emit (crossing_exc ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
+ ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
}
}
2006-05-18 Raja R Harinath <rharinath@novell.com>
+ * test-518.cs: New test based on #77601.
+
* test-514.cs: New test from #76148.
* test-515.cs, test-516.cs: New tests based on #77755.
* test-517.cs: New test based on #75255.
--- /dev/null
+class Foo {
+ static int Main ()
+ {
+ int ret = 1;
+ try {
+ goto done;
+ } finally {
+ ret = 0;
+ }
+ done:
+ return ret;
+ }
+}
+