[mono/mono] d12330ed: [mcs] Reachability and flow analysis rewrite to work on resolved statements and expressions
"Marek Safar (
[email protected])" <
[email protected]>
Thu, 21 Nov 2013 13:44:03 +0000
| Newsgroups |
gmane.comp.gnome.mono.patches |
| Message-ID |
<000001427ae73b78-a01d333f-51fc-49ca-8e82-5f36fece85ea-000000@email.amazonses.com> |
Branch: refs/heads/master
Home: https://github.com/mono/mono
Compare: https://github.com/mono/mono/compare/cc4049bcf262...d12330eda774
Commit: d12330eda7746321b4611865d11e932e53ac55b8
Author: Marek Safar <[email protected]> (marek-safar)
Date: 2013-11-21 13:43:08 GMT
URL: https://github.com/mono/mono/commit/d12330eda7746321b4611865d11e932e53ac55b8
[mcs] Reachability and flow analysis rewrite to work on resolved statements and expressions
Changed paths:
M mcs/errors/cs0162-7.cs
M mcs/errors/cs0163.cs
M mcs/errors/known-issues-net_4_5
M mcs/mcs/anonymous.cs
M mcs/mcs/argument.cs
M mcs/mcs/assign.cs
M mcs/mcs/async.cs
M mcs/mcs/complete.cs
M mcs/mcs/context.cs
M mcs/mcs/cs-parser.jay
M mcs/mcs/delegate.cs
M mcs/mcs/dynamic.cs
M mcs/mcs/ecore.cs
M mcs/mcs/eval.cs
M mcs/mcs/expression.cs
M mcs/mcs/flowanalysis.cs
M mcs/mcs/iterators.cs
M mcs/mcs/linq.cs
M mcs/mcs/method.cs
M mcs/mcs/nullable.cs
M mcs/mcs/statement.cs
M mcs/tests/known-issues-net_4_5
M mcs/tests/test-519.cs
M mcs/tests/test-579.cs
M mcs/tests/ver-il-net_4_5.xml
Added paths:
A mcs/errors/cs0162-20.cs
A mcs/errors/cs0163-2.cs
A mcs/errors/cs0165-22.cs
A mcs/errors/cs0165-23.cs
A mcs/errors/cs0165-24.cs
A mcs/errors/cs0201-10.cs
A mcs/errors/cs0429-4.cs
A mcs/errors/cs1632-2.cs
A mcs/errors/cs1632-3.cs
A mcs/errors/cs1632-4.cs
A mcs/tests/test-874.cs
A mcs/tests/test-876.cs
Removed paths:
D mcs/errors/cs0161-4.cs
D mcs/errors/cs0162-17.cs
D mcs/errors/cs0162-19.cs
Removed: mcs/errors/cs0161-4.cs
===================================================================
@@ -1,15 +0,0 @@
-// CS0161: `T.Main()': not all code paths return a value
-// Line: 6
-// CSC bug: The error is not reported even if it should as in other unreachable cases
-
-class T {
- public static int Main ()
- {
- switch (1) {
- case 1:
- return 0;
- default:
- break;
- }
- }
-}
Renamed: mcs/errors/cs0162-20.cs
===================================================================
@@ -1,12 +1,21 @@
// CS0162: Unreachable code detected
-// Line: 10
+// Line: 14
// Compiler options: -warnaserror
-public class X
+using System;
+
+class X
{
+
public static void Main ()
{
+ goto X;
+ A:
+ bool b = false;
+ if (b) {
+ goto A;
+ }
+ X:
return;
- 1+1;
}
}
\ No newline at end of file
Modified: mcs/errors/cs0162-7.cs
===================================================================
@@ -2,9 +2,6 @@
// Line: 9
// Compiler options: -warnaserror -warn:2
-// this requires a multi-pass algorithm for unreachable code detection
-// punting for now
-
class Foo {
static void Main ()
{
Added: mcs/errors/cs0163-2.cs
===================================================================
@@ -0,0 +1,20 @@
+// CS0163: Control cannot fall through from one case label `case 1:' to another
+// Line: 14
+
+using System;
+using System.Collections.Generic;
+
+static class C
+{
+ public static IEnumerable<int> Test (int key)
+ {
+ switch (key) {
+ case 1:
+ yield return 0;
+ case 2:
+ yield return 2;
+ default:
+ throw new ArgumentOutOfRangeException ("symbol:" + key);
+ }
+ }
+}
\ No newline at end of file
Modified: mcs/errors/cs0163.cs
===================================================================
@@ -1,6 +1,5 @@
-// CS0163: Control cannot fall through from one case label to another
-// Line: 17
-
+// CS0163: Control cannot fall through from one case label `case 3:' to another
+// Line: 21
public class Foo
{
Added: mcs/errors/cs0165-22.cs
===================================================================
@@ -0,0 +1,34 @@
+// CS0165: Use of unassigned local variable `x'
+// Line: 17
+
+using System;
+
+class Program
+{
+ static int Main ()
+ {
+ int foo = 9;
+ int x;
+
+ switch (foo) {
+ case 1:
+ x = 1;
+ gotoTarget:
+ {
+ Console.WriteLine (x);
+ }
+ break;
+ default:
+ {
+ if (foo != 0) {
+ goto gotoTarget;
+ }
+
+ break;
+ }
+ }
+
+ return 1;
+ }
+}
+
Added: mcs/errors/cs0165-23.cs
===================================================================
@@ -0,0 +1,23 @@
+// CS0165: Use of unassigned local variable `retval'
+// Line: 9
+
+class Test
+{
+ static string DoStuff (string msg)
+ {
+ string retval;
+
+ switch (msg) {
+ case "hello":
+ retval = "goodbye";
+ return retval;
+ case "goodbye":
+ return retval;
+ case "other":
+ retval = "other";
+ case "":
+ return msg;
+ }
+ return "";
+ }
+}
\ No newline at end of file
Added: mcs/errors/cs0165-24.cs
===================================================================
@@ -0,0 +1,18 @@
+// CS0165: Use of unassigned local variable `a'
+// Line: 14
+
+class X
+{
+ public static void Main ()
+ {
+ int i = 3;
+ switch (i) {
+ case 1:
+ float a = 7.0f;
+ break;
+ default:
+ float b = a + 99.0f;
+ break;
+ }
+ }
+}
\ No newline at end of file
Added: mcs/errors/cs0201-10.cs
===================================================================
@@ -0,0 +1,11 @@
+// CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
+// Line: 9
+
+public class X
+{
+ public static void Main ()
+ {
+ return;
+ 1+1;
+ }
+}
\ No newline at end of file
Renamed: mcs/errors/cs0429-4.cs
===================================================================
@@ -1,4 +1,4 @@
-// CS0162: Unreachable code detected
+// CS0429: Unreachable expression code detected
// Line: 12
// Compiler options: -warnaserror
Added: mcs/errors/cs1632-2.cs
===================================================================
@@ -0,0 +1,16 @@
+// CS1632: Control cannot leave the body of an anonymous method
+// Line: 12
+
+using System;
+
+class X
+{
+ public static void Main ()
+ {
+ while (true) {
+ Action a = () => {
+ break;
+ };
+ }
+ }
+}
Added: mcs/errors/cs1632-3.cs
===================================================================
@@ -0,0 +1,20 @@
+// CS1632: Control cannot leave the body of an anonymous method
+// Line: 14
+
+using System;
+
+class X
+{
+ public static void Main ()
+ {
+ int b = 0;
+ switch (b) {
+ case 1:
+ Action a = () => {
+ break;
+ };
+
+ break;
+ }
+ }
+}
Added: mcs/errors/cs1632-4.cs
===================================================================
@@ -0,0 +1,22 @@
+// CS1632: Control cannot leave the body of an anonymous method
+// Line: 14
+
+using System;
+
+class X
+{
+ public static void Main ()
+ {
+ int b = 0;
+ switch (b) {
+ case 1:
+ Action a = () => {
+ goto case 2;
+ };
+
+ break;
+ case 2:
+ break;
+ }
+ }
+}
Modified: mcs/errors/known-issues-net_4_5
===================================================================
@@ -14,9 +14,6 @@
# Parser problems
cs0080.cs
-cs0162-7.cs NO ERROR
-cs0165-3.cs
-
# Operators
cs0457-2.cs
cs0457.cs
Modified: mcs/mcs/anonymous.cs
===================================================================
@@ -214,6 +214,11 @@ protected override void DoEmit (EmitContext ec)
hoisted_this.EmitAssign (ec, source, false, false);
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return false;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement target)
{
// Nothing to clone
@@ -1339,13 +1344,6 @@ protected override Expression DoResolve (ResolveContext ec)
if (!DoResolveParameters (ec))
return null;
-#if !STATIC
- // FIXME: The emitted code isn't very careful about reachability
- // so, ensure we have a 'ret' at the end
- BlockContext bc = ec as BlockContext;
- if (bc != null && bc.CurrentBranching != null && bc.CurrentBranching.CurrentUsageVector.IsUnreachable)
- bc.NeedReturnLabel ();
-#endif
return this;
}
@@ -1521,12 +1519,25 @@ public AnonymousExpression Compatible (ResolveContext ec, AnonymousExpression ae
}
var bc = ec as BlockContext;
- if (bc != null)
+
+ if (bc != null) {
aec.AssignmentInfoOffset = bc.AssignmentInfoOffset;
+ aec.EnclosingLoop = bc.EnclosingLoop;
+ aec.EnclosingLoopOrSwitch = bc.EnclosingLoopOrSwitch;
+ aec.Switch = bc.Switch;
+ }
var errors = ec.Report.Errors;
- bool res = Block.Resolve (ec.CurrentBranching, aec, null);
+ bool res = Block.Resolve (aec);
+
+ if (res) {
+ MarkReachable (new Reachability ());
+
+ if (!CheckReachableExit (ec.Report)) {
+ return null;
+ }
+ }
if (am != null && am.ReturnTypeInference != null) {
am.ReturnTypeInference.FixAllTypes (ec);
@@ -1557,6 +1568,43 @@ public override bool ContainsEmitWithAwait ()
return false;
}
+ bool CheckReachableExit (Report report)
+ {
+ if (block.HasReachableClosingBrace && ReturnType.Kind != MemberKind.Void) {
+ // FIXME: Flow-analysis on MoveNext generated code
+ if (!IsIterator) {
+ report.Error (1643, StartLocation,
+ "Not all code paths return a value in anonymous method of type `{0}'", GetSignatureForError ());
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ // We are reachable, mark block body reachable too
+ MarkReachable (new Reachability ());
+
+ CheckReachableExit (fc.Report);
+
+ var das = fc.BranchDefiniteAssignment ();
+ var prev_pb = fc.ParametersBlock;
+ fc.ParametersBlock = Block;
+
+ block.FlowAnalysis (fc);
+
+ fc.ParametersBlock = prev_pb;
+ fc.DefiniteAssignment = das;
+ }
+
+ public override void MarkReachable (Reachability rc)
+ {
+ block.MarkReachable (rc);
+ }
+
public void SetHasThisAccess ()
{
ExplicitBlock b = block;
Modified: mcs/mcs/argument.cs
===================================================================
@@ -127,6 +127,29 @@ public Argument EmitToField (EmitContext ec, bool cloneResult)
return this;
}
+ public void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (ArgType == AType.Out) {
+ var vr = Expr as VariableReference;
+ if (vr != null) {
+ if (vr.VariableInfo != null)
+ fc.SetVariableAssigned (vr.VariableInfo);
+
+ return;
+ }
+
+ var fe = Expr as FieldExpr;
+ if (fe != null) {
+ fe.SetFieldAssigned (fc);
+ return;
+ }
+
+ return;
+ }
+
+ Expr.FlowAnalysis (fc);
+ }
+
public string GetSignatureForError ()
{
if (Expr.eclass == ExprClass.MethodGroup)
@@ -152,18 +175,16 @@ public bool ResolveMethodGroup (ResolveContext ec)
public void Resolve (ResolveContext ec)
{
-// using (ec.With (ResolveContext.Options.DoFlowAnalysis, true)) {
- // Verify that the argument is readable
- if (ArgType != AType.Out)
- Expr = Expr.Resolve (ec);
+ // Verify that the argument is readable
+ if (ArgType != AType.Out)
+ Expr = Expr.Resolve (ec);
- // Verify that the argument is writeable
- if (Expr != null && IsByRef)
- Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess);
+ // Verify that the argument is writeable
+ if (Expr != null && IsByRef)
+ Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess);
- if (Expr == null)
- Expr = ErrorExpression.Instance;
-// }
+ if (Expr == null)
+ Expr = ErrorExpression.Instance;
}
}
@@ -476,6 +497,12 @@ public virtual Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait)
return null;
}
+ public void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ foreach (var arg in args)
+ arg.FlowAnalysis (fc);
+ }
+
public List<Argument>.Enumerator GetEnumerator ()
{
return args.GetEnumerator ();
Modified: mcs/mcs/assign.cs
===================================================================
@@ -417,6 +417,14 @@ public override void EmitStatement (EmitContext ec)
Emit (ec, true);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ source.FlowAnalysis (fc);
+
+ if (target is ArrayAccess || target is IndexerExpr || target is PropertyExpr)
+ target.FlowAnalysis (fc);
+ }
+
protected override void CloneTo (CloneContext clonectx, Expression t)
{
Assign _target = (Assign) t;
@@ -465,6 +473,32 @@ protected override Expression DoResolve (ResolveContext ec)
return this;
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ base.FlowAnalysis (fc);
+
+ var vr = target as VariableReference;
+ if (vr != null) {
+ if (vr.VariableInfo != null)
+ fc.SetVariableAssigned (vr.VariableInfo);
+
+ return;
+ }
+
+ var fe = target as FieldExpr;
+ if (fe != null) {
+ fe.SetFieldAssigned (fc);
+ return;
+ }
+ }
+
+ public override void MarkReachable (Reachability rc)
+ {
+ var es = source as ExpressionStatement;
+ if (es != null)
+ es.MarkReachable (rc);
+ }
}
public class RuntimeExplicitAssign : Assign
@@ -593,6 +627,11 @@ public override void EmitStatement (EmitContext ec)
else
base.EmitStatement (ec);
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ source.FlowAnalysis (fc);
+ }
public bool IsDefaultInitializer {
get {
@@ -780,6 +819,12 @@ protected override Expression DoResolve (ResolveContext ec)
return base.DoResolve (ec);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ target.FlowAnalysis (fc);
+ source.FlowAnalysis (fc);
+ }
+
protected override Expression ResolveConversions (ResolveContext ec)
{
//
Modified: mcs/mcs/async.cs
===================================================================
@@ -65,8 +65,19 @@ public override bool ContainsEmitWithAwait ()
return true;
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+
+ stmt.RegisterResumePoint ();
+ }
+
protected override Expression DoResolve (ResolveContext rc)
{
+ if (rc.HasSet (ResolveContext.Options.FinallyScope)) {
+ rc.Report.Error (1984, loc, "The `await' operator cannot be used in the body of a finally clause");
+ }
+
if (rc.HasSet (ResolveContext.Options.LockScope)) {
rc.Report.Error (1996, loc,
"The `await' operator cannot be used in the body of a lock statement");
@@ -115,6 +126,12 @@ public override void EmitStatement (EmitContext ec)
stmt.EmitStatement (ec);
}
+ public override void MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+ stmt.MarkReachable (rc);
+ }
+
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
@@ -175,6 +192,7 @@ public override Expression EmitToField (EmitContext ec)
public AwaitStatement (Expression expr, Location loc)
: base (expr, loc)
{
+ unwind_protect = true;
}
#region Properties
@@ -306,6 +324,10 @@ public override bool Resolve (BlockContext bc)
return false;
}
+ if (bc.HasSet (ResolveContext.Options.CatchScope)) {
+ bc.Report.Error (1985, loc, "The `await' operator cannot be used in the body of a catch clause");
+ }
+
if (!base.Resolve (bc))
return false;
@@ -363,6 +385,47 @@ public override bool Resolve (BlockContext bc)
}
}
+ class AsyncInitializerStatement : StatementExpression
+ {
+ public AsyncInitializerStatement (AsyncInitializer expr)
+ : base (expr)
+ {
+ }
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ base.DoFlowAnalysis (fc);
+
+ var init = (AsyncInitializer) Expr;
+ var res = !init.Block.HasReachableClosingBrace;
+ var storey = (AsyncTaskStorey) init.Storey;
+
+ if (storey.ReturnType.IsGenericTask)
+ return res;
+
+ return true;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ if (!rc.IsUnreachable)
+ reachable = true;
+
+ var init = (AsyncInitializer) Expr;
+ rc = init.Block.MarkReachable (rc);
+
+ var storey = (AsyncTaskStorey) init.Storey;
+
+ //
+ // Explicit return is required for Task<T> state machine
+ //
+ if (storey.ReturnType != null && storey.ReturnType.IsGenericTask)
+ return rc;
+
+ return Reachability.CreateUnreachable ();
+ }
+ }
+
public class AsyncInitializer : StateMachineInitializer
{
TypeInferenceContext return_inference;
@@ -398,14 +461,15 @@ public AsyncInitializer (ParametersBlock block, TypeDefinition host, TypeSpec re
#endregion
- protected override BlockContext CreateBlockContext (ResolveContext rc)
+ protected override BlockContext CreateBlockContext (BlockContext bc)
{
- var ctx = base.CreateBlockContext (rc);
- var lambda = rc.CurrentAnonymousMethod as LambdaMethod;
+ var ctx = base.CreateBlockContext (bc);
+ var lambda = bc.CurrentAnonymousMethod as LambdaMethod;
if (lambda != null)
return_inference = lambda.ReturnTypeInference;
- ctx.StartFlowBranching (this, rc.CurrentBranching);
+ ctx.Set (ResolveContext.Options.TryScope);
+
return ctx;
}
@@ -426,6 +490,13 @@ public override void EmitStatement (EmitContext ec)
storey.EmitInitializer (ec);
ec.Emit (OpCodes.Ret);
}
+
+ public override void MarkReachable (Reachability rc)
+ {
+ //
+ // Reachability has been done in AsyncInitializerStatement
+ //
+ }
}
class AsyncTaskStorey : StateMachine
Modified: mcs/mcs/complete.cs
===================================================================
@@ -114,32 +114,22 @@ protected override Expression DoResolve (ResolveContext rc)
var sn = expr as SimpleName;
const ResolveFlags flags = ResolveFlags.VariableOrValue | ResolveFlags.Type;
- //
- // Resolve the expression with flow analysis turned off, we'll do the definite
- // assignment checks later. This is because we don't know yet what the expression
- // will resolve to - it may resolve to a FieldExpr and in this case we must do the
- // definite assignment check on the actual field and not on the whole struct.
- //
- using (rc.Set (ResolveContext.Options.OmitStructFlowAnalysis)) {
- if (sn != null) {
- expr = sn.LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess | MemberLookupRestrictions.ExactArity);
-
- //
- // Resolve expression which does have type set as we need expression type
- // with disable flow analysis as we don't know whether left side expression
- // is used as variable or type
- //
- if (expr is VariableReference || expr is ConstantExpr || expr is Linq.TransparentMemberAccess) {
- using (rc.With (ResolveContext.Options.DoFlowAnalysis, false)) {
- expr = expr.Resolve (rc);
- }
- } else if (expr is TypeParameterExpr) {
- expr.Error_UnexpectedKind (rc, flags, sn.Location);
- expr = null;
- }
- } else {
- expr = expr.Resolve (rc, flags);
+ if (sn != null) {
+ expr = sn.LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess | MemberLookupRestrictions.ExactArity);
+
+ //
+ // Resolve expression which does have type set as we need expression type
+ // with disable flow analysis as we don't know whether left side expression
+ // is used as variable or type
+ //
+ if (expr is VariableReference || expr is ConstantExpr || expr is Linq.TransparentMemberAccess) {
+ expr = expr.Resolve (rc);
+ } else if (expr is TypeParameterExpr) {
+ expr.Error_UnexpectedKind (rc, flags, sn.Location);
+ expr = null;
}
+ } else {
+ expr = expr.Resolve (rc, flags);
}
if (expr == null)
Modified: mcs/mcs/context.cs
===================================================================
@@ -69,8 +69,6 @@ public interface IModuleContext
//
public class BlockContext : ResolveContext
{
- FlowBranching current_flow_branching;
-
readonly TypeSpec return_type;
//
@@ -112,124 +110,17 @@ public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType
flags |= ResolveContext.Options.BaseInitializer;
}
- public override FlowBranching CurrentBranching {
- get { return current_flow_branching; }
- }
-
- public TypeSpec ReturnType {
- get { return return_type; }
- }
-
- public bool IsUnreachable {
- get {
- return HasSet (Options.UnreachableScope);
- }
- set {
- flags = value ? flags | Options.UnreachableScope : flags & ~Options.UnreachableScope;
- }
- }
-
- public bool UnreachableReported {
- get {
- return HasSet (Options.UnreachableReported);
- }
- set {
- flags = value ? flags | Options.UnreachableReported : flags & ~Options.UnreachableScope;
- }
- }
-
- // <summary>
- // Starts a new code branching. This inherits the state of all local
- // variables and parameters from the current branching.
- // </summary>
- public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
- {
- current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
- return current_flow_branching;
- }
-
- // <summary>
- // Starts a new code branching for block `block'.
- // </summary>
- public FlowBranching StartFlowBranching (Block block)
- {
- Set (Options.DoFlowAnalysis);
-
- current_flow_branching = FlowBranching.CreateBranching (
- CurrentBranching, FlowBranching.BranchingType.Block, block, block.StartLocation);
- return current_flow_branching;
- }
-
- public FlowBranchingTryCatch StartFlowBranching (TryCatch stmt)
- {
- FlowBranchingTryCatch branching = new FlowBranchingTryCatch (CurrentBranching, stmt);
- current_flow_branching = branching;
- return branching;
- }
-
- public FlowBranchingTryFinally StartFlowBranching (TryFinallyBlock stmt)
- {
- FlowBranchingTryFinally branching = new FlowBranchingTryFinally (CurrentBranching, stmt);
- current_flow_branching = branching;
- return branching;
- }
-
- public FlowBranchingLabeled StartFlowBranching (LabeledStatement stmt)
- {
- FlowBranchingLabeled branching = new FlowBranchingLabeled (CurrentBranching, stmt);
- current_flow_branching = branching;
- return branching;
- }
-
- public FlowBranchingIterator StartFlowBranching (Iterator iterator, FlowBranching parent)
- {
- FlowBranchingIterator branching = new FlowBranchingIterator (parent, iterator);
- current_flow_branching = branching;
- return branching;
- }
-
- public FlowBranchingAsync StartFlowBranching (AsyncInitializer asyncBody, FlowBranching parent)
- {
- var branching = new FlowBranchingAsync (parent, asyncBody);
- current_flow_branching = branching;
- return branching;
- }
-
- public FlowBranchingToplevel StartFlowBranching (ParametersBlock stmt, FlowBranching parent)
- {
- FlowBranchingToplevel branching = new FlowBranchingToplevel (parent, stmt);
- current_flow_branching = branching;
- return branching;
- }
+ public ExceptionStatement CurrentTryBlock { get; set; }
- // <summary>
- // Ends a code branching. Merges the state of locals and parameters
- // from all the children of the ending branching.
- // </summary>
- public bool EndFlowBranching ()
- {
- FlowBranching old = current_flow_branching;
- current_flow_branching = current_flow_branching.Parent;
+ public LoopStatement EnclosingLoop { get; set; }
- FlowBranching.UsageVector vector = current_flow_branching.MergeChild (old);
- return vector.IsUnreachable;
- }
+ public LoopStatement EnclosingLoopOrSwitch { get; set; }
- // <summary>
- // Kills the current code branching. This throws away any changed state
- // information and should only be used in case of an error.
- // </summary>
- // FIXME: this is evil
- public void KillFlowBranching ()
- {
- current_flow_branching = current_flow_branching.Parent;
- }
+ public Switch Switch { get; set; }
-#if !STATIC
- public void NeedReturnLabel ()
- {
+ public TypeSpec ReturnType {
+ get { return return_type; }
}
-#endif
}
//
@@ -290,20 +181,9 @@ public enum Options
LockScope = 1 << 13,
- UnreachableScope = 1 << 14,
+ TryScope = 1 << 14,
- UnreachableReported = 1 << 15,
-
- /// <summary>
- /// Whether control flow analysis is enabled
- /// </summary>
- DoFlowAnalysis = 1 << 20,
-
- /// <summary>
- /// Whether control flow analysis is disabled on structs
- /// (only meaningful when DoFlowAnalysis is set)
- /// </summary>
- OmitStructFlowAnalysis = 1 << 21,
+ TryWithCatchScope = 1 << 15,
///
/// Indicates the current context is in probing mode, no errors are reported.
@@ -372,11 +252,6 @@ public void Dispose ()
public readonly IMemberContext MemberContext;
- /// <summary>
- /// If this is non-null, points to the current switch statement
- /// </summary>
- public Switch Switch;
-
public ResolveContext (IMemberContext mc)
{
if (mc == null)
@@ -416,10 +291,6 @@ public ResolveContext (IMemberContext mc, Options options)
}
}
- public virtual FlowBranching CurrentBranching {
- get { return null; }
- }
-
//
// The current iterator
//
@@ -443,10 +314,6 @@ public ResolveContext (IMemberContext mc, Options options)
get { return (flags & Options.ConstantCheckState) != 0; }
}
- public bool DoFlowAnalysis {
- get { return (flags & Options.DoFlowAnalysis) != 0; }
- }
-
public bool IsInProbingMode {
get {
return (flags & Options.ProbingMode) != 0;
@@ -480,7 +347,7 @@ public ResolveContext (IMemberContext mc, Options options)
public bool IsVariableCapturingRequired {
get {
- return !IsInProbingMode && (CurrentBranching == null || !CurrentBranching.CurrentUsageVector.IsUnreachable);
+ return !IsInProbingMode;
}
}
@@ -490,10 +357,6 @@ public ResolveContext (IMemberContext mc, Options options)
}
}
- public bool OmitStructFlowAnalysis {
- get { return (flags & Options.OmitStructFlowAnalysis) != 0; }
- }
-
public Report Report {
get {
return Module.Compiler.Report;
@@ -573,6 +436,65 @@ public FullNamedExpression LookupNamespaceAlias (string name)
#endregion
}
+ public class FlowAnalysisContext
+ {
+ readonly CompilerContext ctx;
+
+ public FlowAnalysisContext (CompilerContext ctx, ParametersBlock parametersBlock)
+ {
+ this.ctx = ctx;
+ this.ParametersBlock = parametersBlock;
+
+ DefiniteAssignment = new DefiniteAssignmentBitSet ();
+ }
+
+ public DefiniteAssignmentBitSet DefiniteAssignment { get; set; }
+
+ public List<LabeledStatement> LabelStack { get; set; }
+
+ public ParametersBlock ParametersBlock { get; set; }
+
+ public Report Report {
+ get {
+ return ctx.Report;
+ }
+ }
+
+ public DefiniteAssignmentBitSet SwitchInitialDefinitiveAssignment { get; set; }
+
+ public TryFinally TryFinally { get; set; }
+
+ public bool UnreachableReported { get; set; }
+
+ public DefiniteAssignmentBitSet BranchDefiniteAssignment ()
+ {
+ var dat = DefiniteAssignment;
+ DefiniteAssignment = new DefiniteAssignmentBitSet (dat);
+ return dat;
+ }
+
+ public bool IsDefinitelyAssigned (VariableInfo variable)
+ {
+ return variable.IsAssigned (DefiniteAssignment);
+ }
+
+ public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name)
+ {
+ return variable.IsStructFieldAssigned (DefiniteAssignment, name);
+ }
+
+ public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
+ {
+ variable.SetAssigned (DefiniteAssignment, generatedAssignment);
+ }
+
+ public void SetStructFieldAssigned (VariableInfo variable, string name)
+ {
+ variable.SetStructFieldAssigned (DefiniteAssignment, name);
+ }
+ }
+
+
//
// This class is used during the Statement.Clone operation
// to remap objects that have been cloned.
Modified: mcs/mcs/cs-parser.jay
===================================================================
@@ -5900,12 +5900,12 @@ try_statement
}
| TRY block FINALLY block
{
- $$ = new TryFinally ((Statement) $2, (Block) $4, GetLocation ($1));
+ $$ = new TryFinally ((Statement) $2, (ExplicitBlock) $4, GetLocation ($1));
lbag.AddStatement ($$, GetLocation ($3));
}
| TRY block catch_clauses FINALLY block
{
- $$ = new TryFinally (new TryCatch ((Block) $2, (List<Catch>) $3, Location.Null, true), (Block) $5, GetLocation ($1));
+ $$ = new TryFinally (new TryCatch ((Block) $2, (List<Catch>) $3, Location.Null, true), (ExplicitBlock) $5, GetLocation ($1));
lbag.AddStatement ($$, GetLocation ($4));
}
| TRY block error
@@ -5945,12 +5945,12 @@ opt_identifier
catch_clause
: CATCH block
{
- $$ = new Catch ((Block) $2, GetLocation ($1));
+ $$ = new Catch ((ExplicitBlock) $2, GetLocation ($1));
}
| CATCH open_parens_any type opt_identifier CLOSE_PARENS
{
start_block (GetLocation ($2));
- var c = new Catch (current_block, GetLocation ($1));
+ var c = new Catch ((ExplicitBlock) current_block, GetLocation ($1));
c.TypeExpression = (FullNamedExpression) $3;
if ($4 != null) {
Modified: mcs/mcs/delegate.cs
===================================================================
@@ -828,6 +828,13 @@ public override Expression CreateExpressionTree (ResolveContext ec)
return CreateExpressionFactoryCall (ec, "Invoke", args);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ InstanceExpr.FlowAnalysis (fc);
+ if (arguments != null)
+ arguments.FlowAnalysis (fc);
+ }
+
protected override Expression DoResolve (ResolveContext ec)
{
TypeSpec del_type = InstanceExpr.Type;
Modified: mcs/mcs/dynamic.cs
===================================================================
@@ -532,6 +532,11 @@ protected void EmitCall (EmitContext ec, Expression binder, Arguments arguments,
}
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ arguments.FlowAnalysis (fc);
+ }
+
public static MemberAccess GetBinderNamespace (Location loc)
{
return new MemberAccess (new MemberAccess (
@@ -612,6 +617,11 @@ public override void EmitStatement (EmitContext ec)
stmt.Emit (ec);
}
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ invoke.FlowAnalysis (fc);
+ }
}
class DynamicConversion : DynamicExpressionStatement, IDynamicBinder
Modified: mcs/mcs/ecore.cs
===================================================================
@@ -923,6 +923,10 @@ protected void Error_PointerInsideExpressionTree (ResolveContext ec)
ec.Report.Error (1944, loc, "An expression tree cannot contain an unsafe pointer operation");
}
+ public virtual void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ }
+
/// <summary>
/// Returns an expression that can be used to invoke operator true
/// on the expression if it exists.
@@ -1175,6 +1179,10 @@ public virtual object Accept (StructuralVisitor visitor)
/// </summary>
public abstract class ExpressionStatement : Expression
{
+ public virtual void MarkReachable (Reachability rc)
+ {
+ }
+
public ExpressionStatement ResolveStatement (BlockContext ec)
{
Expression e = Resolve (ec);
@@ -1310,6 +1318,11 @@ public override void Emit (EmitContext ec)
child.Emit (ec);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ child.FlowAnalysis (fc);
+ }
+
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
#if STATIC
@@ -2141,6 +2154,11 @@ public override void EmitStatement (EmitContext ec)
{
stm.EmitStatement (ec);
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ stm.FlowAnalysis (fc);
+ }
}
readonly Expression expr, orig_expr;
@@ -2244,6 +2262,11 @@ public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
expr.EmitBranchable (ec, target, on_true);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ }
+
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
return orig_expr.MakeExpression (ctx);
@@ -2339,7 +2362,38 @@ public override void Emit (EmitContext ec)
{
throw new InternalErrorException ("Missing Resolve call");
}
+ }
+
+ public class UnreachableExpression : Expression
+ {
+ public UnreachableExpression (Expression expr)
+ {
+ this.loc = expr.Location;
+ }
+
+ public override Expression CreateExpressionTree (ResolveContext ec)
+ {
+ // TODO: is it ok
+ throw new NotImplementedException ();
+ }
+ protected override Expression DoResolve (ResolveContext rc)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ fc.Report.Warning (429, 4, loc, "Unreachable expression code detected");
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ }
+
+ public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
+ {
+ }
}
//
@@ -2470,14 +2524,7 @@ public SimpleName GetMethodGroup ()
protected override Expression DoResolve (ResolveContext rc)
{
- var e = SimpleNameResolve (rc, null);
-
- var fe = e as FieldExpr;
- if (fe != null) {
- fe.VerifyAssignedStructField (rc, null);
- }
-
- return e;
+ return SimpleNameResolve (rc, null);
}
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
@@ -3162,6 +3209,12 @@ public static void Error_ProtectedMemberAccess (ResolveContext rc, MemberSpec me
member.GetSignatureForError (), qualifier.GetSignatureForError (), rc.CurrentType.GetSignatureForError ());
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (InstanceExpression != null)
+ InstanceExpression.FlowAnalysis (fc);
+ }
+
public bool ResolveInstanceExpression (ResolveContext rc, Expression rhs)
{
if (!ResolveInstanceExpressionCore (rc, rhs))
@@ -3264,15 +3317,7 @@ bool ResolveInstanceExpressionCore (ResolveContext rc, Expression rhs)
DeclaringType.GetSignatureForError (), rc.CurrentType.GetSignatureForError ());
}
- InstanceExpression = new This (loc);
- if (this is FieldExpr && rc.CurrentBlock.ParametersBlock.TopBlock.ThisVariable != null) {
- using (rc.Set (ResolveContext.Options.OmitStructFlowAnalysis)) {
- InstanceExpression = InstanceExpression.Resolve (rc);
- }
- } else {
- InstanceExpression = InstanceExpression.Resolve (rc);
- }
-
+ InstanceExpression = new This (loc).Resolve (rc);
return false;
}
@@ -3280,30 +3325,18 @@ bool ResolveInstanceExpressionCore (ResolveContext rc, Expression rhs)
if (me != null) {
me.ResolveInstanceExpressionCore (rc, rhs);
- // Using this check to detect probing instance expression resolve
- if (!rc.OmitStructFlowAnalysis) {
- var fe = me as FieldExpr;
- if (fe != null && fe.IsMarshalByRefAccess (rc)) {
- rc.Report.SymbolRelatedToPreviousError (me.DeclaringType);
- rc.Report.Warning (1690, 1, loc,
- "Cannot call methods, properties, or indexers on `{0}' because it is a value type member of a marshal-by-reference class",
- me.GetSignatureForError ());
- }
+ var fe = me as FieldExpr;
+ if (fe != null && fe.IsMarshalByRefAccess (rc)) {
+ rc.Report.SymbolRelatedToPreviousError (me.DeclaringType);
+ rc.Report.Warning (1690, 1, loc,
+ "Cannot call methods, properties, or indexers on `{0}' because it is a value type member of a marshal-by-reference class",
+ me.GetSignatureForError ());
}
return true;
}
//
- // Run member-access postponed check once we know that
- // the expression is not field expression which is the only
- // expression which can use uninitialized this
- //
- if (InstanceExpression is This && !(this is FieldExpr) && rc.CurrentBlock.ParametersBlock.TopBlock.ThisVariable != null) {
- ((This)InstanceExpression).CheckStructThisDefiniteAssignment (rc);
- }
-
- //
// Additional checks for l-value member access
//
if (rhs != null) {
@@ -5696,18 +5729,14 @@ Expression DoResolve (ResolveContext ec, Expression rhs)
// "a.b" is initialized, not whether the whole struct "a" is initialized.
if (lvalue_instance) {
- using (ec.With (ResolveContext.Options.DoFlowAnalysis, false)) {
- bool out_access = rhs == EmptyExpression.OutAccess || rhs == EmptyExpression.LValueMemberOutAccess;
+ bool out_access = rhs == EmptyExpression.OutAccess || rhs == EmptyExpression.LValueMemberOutAccess;
- Expression right_side =
- out_access ? EmptyExpression.LValueMemberOutAccess : EmptyExpression.LValueMemberAccess;
+ Expression right_side =
+ out_access ? EmptyExpression.LValueMemberOutAccess : EmptyExpression.LValueMemberAccess;
- InstanceExpression = InstanceExpression.ResolveLValue (ec, right_side);
- }
+ InstanceExpression = InstanceExpression.ResolveLValue (ec, right_side);
} else {
- using (ec.With (ResolveContext.Options.DoFlowAnalysis, false)) {
- InstanceExpression = InstanceExpression.Resolve (ec, ResolveFlags.VariableOrValue);
- }
+ InstanceExpression = InstanceExpression.Resolve (ec, ResolveFlags.VariableOrValue);
}
if (InstanceExpression == null)
@@ -5720,10 +5749,6 @@ Expression DoResolve (ResolveContext ec, Expression rhs)
var fb = spec as FixedFieldSpec;
IVariableReference var = InstanceExpression as IVariableReference;
- if (lvalue_instance && var != null && var.VariableInfo != null) {
- var.VariableInfo.SetStructFieldAssigned (ec, Name);
- }
-
if (fb != null) {
IFixedExpression fe = InstanceExpression as IFixedExpression;
if (!ec.HasSet (ResolveContext.Options.FixedInitializerScope) && (fe == null || !fe.IsFixed)) {
@@ -5747,15 +5772,50 @@ Expression DoResolve (ResolveContext ec, Expression rhs)
//
if (var != null && var.VariableInfo != null && InstanceExpression.Type.IsStruct) {
variable_info = var.VariableInfo.GetStructFieldInfo (Name);
- if (rhs != null && variable_info != null)
- variable_info.SetStructFieldAssigned (ec, Name);
}
eclass = ExprClass.Variable;
return this;
}
- public void VerifyAssignedStructField (ResolveContext rc, Expression rhs)
+ public void SetFieldAssigned (FlowAnalysisContext fc)
+ {
+ if (!IsInstance)
+ return;
+
+ bool lvalue_instance = spec.DeclaringType.IsStruct;
+ if (lvalue_instance) {
+ var var = InstanceExpression as IVariableReference;
+ if (var != null && var.VariableInfo != null) {
+ fc.SetStructFieldAssigned (var.VariableInfo, Name);
+ }
+ }
+
+ var fe = InstanceExpression as FieldExpr;
+ if (fe != null || lvalue_instance) {
+ if (fe == null)
+ return;
+
+ /*
+ while (fe.InstanceExpression is FieldExpr) {
+ fe = (FieldExpr) fe.InstanceExpression;
+ if (!fe.Spec.DeclaringType.IsStruct)
+ continue;
+
+ if (fe.VariableInfo != null && fc.IsStructFieldDefinitelyAssigned (fe.VariableInfo, fe.Name)) {
+ fc.Report.Warning (1060, 1, fe.loc, "Use of possibly unassigned field `{0}'", fe.Name);
+ }
+ }
+
+ fe.InstanceExpression.FlowAnalysis (fc);
+ */
+ } else {
+ InstanceExpression.FlowAnalysis (fc);
+ }
+ }
+
+
+ public void VerifyAssignedStructField (FlowAnalysisContext fc)
{
var fe = this;
@@ -5764,14 +5824,8 @@ public void VerifyAssignedStructField (ResolveContext rc, Expression rhs)
if (var != null) {
var vi = var.VariableInfo;
- if (vi != null && !vi.IsStructFieldAssigned (rc, fe.Name) && (rhs == null || !fe.type.IsStruct)) {
- if (rhs != null) {
- rc.Report.Warning (1060, 1, fe.loc, "Use of possibly unassigned field `{0}'", fe.Name);
- } else {
- rc.Report.Error (170, fe.loc, "Use of possibly unassigned field `{0}'", fe.Name);
- }
-
- return;
+ if (vi != null && !fc.IsStructFieldDefinitelyAssigned (vi, fe.Name) && !fe.type.IsStruct) {
+ fc.Report.Warning (1060, 1, fe.loc, "Use of possibly unassigned field `{0}'", fe.Name);
}
}
@@ -5809,7 +5863,6 @@ Expression Error_AssignToReadonly (ResolveContext rc, Expression right_side)
rc.Report.Error (1649, loc, "Members of readonly field `{0}' cannot be passed ref or out (except in a constructor)",
GetSignatureForError ());
}
-
return null;
}
@@ -5875,6 +5928,23 @@ override public Expression DoResolveLValue (ResolveContext ec, Expression right_
return this;
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ var var = InstanceExpression as IVariableReference;
+ if (var != null) {
+ var vi = var.VariableInfo;
+ if (vi != null && !fc.IsStructFieldDefinitelyAssigned (vi, Name)) {
+ fc.Report.Error (170, loc, "Use of possibly unassigned field `{0}'", Name);
+ return;
+ }
+
+ if (TypeSpec.IsValueType (InstanceExpression.Type))
+ return;
+ }
+
+ base.FlowAnalysis (fc);
+ }
+
public override int GetHashCode ()
{
return spec.GetHashCode ();
@@ -6742,6 +6812,11 @@ public override void Emit (EmitContext ec)
DoEmit (ec);
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return false;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement target)
{
// Nothing
@@ -6843,10 +6918,6 @@ public override void SetHasAddressTaken ()
public override VariableInfo VariableInfo {
get { return null; }
}
-
- public override void VerifyDefiniteAssignment (ResolveContext rc)
- {
- }
}
///
Modified: mcs/mcs/eval.cs
===================================================================
@@ -389,7 +389,7 @@ public string Evaluate (string input, out object result, out bool result_set)
BlockContext bc = new BlockContext (method, method.Block, ctx.BuiltinTypes.Void);
try {
- method.Block.Resolve (null, bc, method);
+ method.Block.Resolve (bc, method);
} catch (CompletionResult cr) {
prefix = cr.BaseText;
return cr.Result;
Modified: mcs/mcs/expression.cs
===================================================================
@@ -83,6 +83,11 @@ public override void Emit (EmitContext ec)
call.EmitPredefined (ec, oper, arguments, loc);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ arguments.FlowAnalysis (fc);
+ }
+
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
#if STATIC
@@ -572,6 +577,19 @@ public override void EmitSideEffect (EmitContext ec)
Expr.EmitSideEffect (ec);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (Oper == Operator.AddressOf) {
+ var vr = Expr as VariableReference;
+ if (vr != null && vr.VariableInfo != null)
+ fc.SetVariableAssigned (vr.VariableInfo);
+
+ return;
+ }
+
+ Expr.FlowAnalysis (fc);
+ }
+
//
// Converts operator to System.Linq.Expressions.ExpressionType enum name
//
@@ -1274,6 +1292,11 @@ public override void EmitStatement (EmitContext ec)
EmitCode (ec, false);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ }
+
//
// Converts operator to System.Linq.Expressions.ExpressionType enum name
//
@@ -1367,6 +1390,11 @@ protected override Expression DoResolve (ResolveContext ec)
return this;
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ }
+
protected abstract string OperatorName { get; }
protected override void CloneTo (CloneContext clonectx, Expression t)
@@ -2518,6 +2546,12 @@ void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expressi
Error_OperatorCannotBeApplied (ec, left, right, OperName (oper), loc);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ left.FlowAnalysis (fc);
+ right.FlowAnalysis (fc);
+ }
+
//
// Converts operator to System.Linq.Expressions.ExpressionType enum name
//
@@ -4890,6 +4924,11 @@ public override void Emit (EmitContext ec)
}
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ arguments.FlowAnalysis (fc);
+ }
+
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
if (arguments.Count != 2)
@@ -5259,32 +5298,8 @@ public override Expression CreateExpressionTree (ResolveContext ec)
protected override Expression DoResolve (ResolveContext ec)
{
expr = expr.Resolve (ec);
-
- //
- // Unreachable code needs different resolve path. For instance for await
- // expression to not generate unreachable resumable statement
- //
- Constant c = expr as Constant;
- if (c != null && ec.CurrentBranching != null) {
- bool unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
-
- if (c.IsDefaultValue) {
- ec.CurrentBranching.CurrentUsageVector.IsUnreachable = true;
- true_expr = true_expr.Resolve (ec);
- ec.CurrentBranching.CurrentUsageVector.IsUnreachable = unreachable;
-
- false_expr = false_expr.Resolve (ec);
- } else {
- true_expr = true_expr.Resolve (ec);
-
- ec.CurrentBranching.CurrentUsageVector.IsUnreachable = true;
- false_expr = false_expr.Resolve (ec);
- ec.CurrentBranching.CurrentUsageVector.IsUnreachable = unreachable;
- }
- } else {
- true_expr = true_expr.Resolve (ec);
- false_expr = false_expr.Resolve (ec);
- }
+ true_expr = true_expr.Resolve (ec);
+ false_expr = false_expr.Resolve (ec);
if (true_expr == null || false_expr == null || expr == null)
return null;
@@ -5345,8 +5360,9 @@ protected override Expression DoResolve (ResolveContext ec)
true_type.GetSignatureForError (), false_type.GetSignatureForError ());
return null;
}
- }
+ }
+ Constant c = expr as Constant;
if (c != null) {
bool is_false = c.IsDefaultValue;
@@ -5392,6 +5408,14 @@ public override void Emit (EmitContext ec)
ec.MarkLabel (end_target);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ // FIXME: Need to branch
+ expr.FlowAnalysis (fc);
+ true_expr.FlowAnalysis (fc);
+ false_expr.FlowAnalysis (fc);
+ }
+
protected override void CloneTo (CloneContext clonectx, Expression t)
{
Conditional target = (Conditional) t;
@@ -5409,7 +5433,6 @@ public abstract class VariableReference : Expression, IAssignMethod, IMemoryLoca
#region Abstract
public abstract HoistedVariable GetHoistedVariable (AnonymousExpression ae);
public abstract void SetHasAddressTaken ();
- public abstract void VerifyDefiniteAssignment (ResolveContext rc);
public abstract bool IsLockedByStatement { get; set; }
@@ -5642,17 +5665,17 @@ public override HoistedVariable GetHoistedVariable (AnonymousExpression ae)
#endregion
- public override void VerifyDefiniteAssignment (ResolveContext rc)
+ public override void FlowAnalysis (FlowAnalysisContext fc)
{
VariableInfo variable_info = VariableInfo;
if (variable_info == null)
return;
- if (variable_info.IsAssigned (rc))
+ if (fc.IsDefinitelyAssigned (variable_info))
return;
- rc.Report.Error (165, loc, "Use of unassigned local variable `{0}'", Name);
- variable_info.SetAssigned (rc);
+ fc.Report.Error (165, loc, "Use of unassigned local variable `{0}'", Name);
+ variable_info.SetAssigned (fc.DefiniteAssignment, true);
}
public override void SetHasAddressTaken ()
@@ -5689,8 +5712,6 @@ protected override Expression DoResolve (ResolveContext ec)
{
local_info.SetIsUsed ();
- VerifyDefiniteAssignment (ec);
-
DoResolveBase (ec);
return this;
}
@@ -5721,8 +5742,6 @@ public override Expression DoResolveLValue (ResolveContext ec, Expression rhs)
}
ec.Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
}
- } else if (VariableInfo != null) {
- VariableInfo.SetAssigned (ec);
}
if (eclass == ExprClass.Unresolved)
@@ -5842,15 +5861,6 @@ public override void SetHasAddressTaken ()
Parameter.HasAddressTaken = true;
}
- void SetAssigned (ResolveContext ec)
- {
- if (Parameter.HoistedVariant != null)
- Parameter.HoistedVariant.IsAssigned = true;
-
- if (HasOutModifier && ec.DoFlowAnalysis)
- ec.CurrentBranching.SetAssigned (VariableInfo);
- }
-
bool DoResolveBase (ResolveContext ec)
{
if (eclass != ExprClass.Unresolved)
@@ -5916,7 +5926,6 @@ protected override Expression DoResolve (ResolveContext ec)
if (!DoResolveBase (ec))
return null;
- VerifyDefiniteAssignment (ec);
return this;
}
@@ -5925,21 +5934,23 @@ public override Expression DoResolveLValue (ResolveContext ec, Expression right_
if (!DoResolveBase (ec))
return null;
- SetAssigned (ec);
+ if (Parameter.HoistedVariant != null)
+ Parameter.HoistedVariant.IsAssigned = true;
+
return base.DoResolveLValue (ec, right_side);
}
- public override void VerifyDefiniteAssignment (ResolveContext rc)
+ public override void FlowAnalysis (FlowAnalysisContext fc)
{
VariableInfo variable_info = VariableInfo;
if (variable_info == null)
return;
- if (variable_info.IsAssigned (rc))
+ if (fc.IsDefinitelyAssigned (variable_info))
return;
- rc.Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name);
- variable_info.SetAssigned (rc);
+ fc.Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name);
+ fc.SetVariableAssigned (variable_info);
}
}
@@ -6203,6 +6214,14 @@ protected virtual MethodGroupExpr DoResolveOverload (ResolveContext ec)
return mg.OverloadResolve (ec, ref arguments, null, OverloadResolver.Restrictions.None);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ mg.FlowAnalysis (fc);
+
+ if (arguments != null)
+ arguments.FlowAnalysis (fc);
+ }
+
public override string GetSignatureForError ()
{
return mg.GetSignatureForError ();
@@ -6628,6 +6647,12 @@ public override void EmitStatement (EmitContext ec)
ec.Emit (OpCodes.Pop);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (arguments != null)
+ arguments.FlowAnalysis (fc);
+ }
+
public void AddressOf (EmitContext ec, AddressOp mode)
{
EmitAddressOf (ec, mode);
@@ -6788,6 +6813,11 @@ public override void Emit (EmitContext ec)
{
throw new InternalErrorException ("Missing Resolve call");
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ throw new InternalErrorException ("Missing Resolve call");
+ }
public override object Accept (StructuralVisitor visitor)
{
@@ -7049,6 +7079,17 @@ protected override void Error_NegativeArrayIndex (ResolveContext ec, Location lo
ec.Report.Error (248, loc, "Cannot create an array with a negative size");
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ foreach (var arg in arguments)
+ arg.FlowAnalysis (fc);
+
+ if (array_data != null) {
+ foreach (var ad in array_data)
+ ad.FlowAnalysis (fc);
+ }
+ }
+
bool InitializersContainAwait ()
{
if (array_data == null)
@@ -7693,7 +7734,7 @@ public This (Location loc)
#endregion
- public void CheckStructThisDefiniteAssignment (ResolveContext rc)
+ void CheckStructThisDefiniteAssignment (FlowAnalysisContext fc)
{
//
// It's null for all cases when we don't need to check `this'
@@ -7702,13 +7743,10 @@ public void CheckStructThisDefiniteAssignment (ResolveContext rc)
if (variable_info == null)
return;
- if (rc.OmitStructFlowAnalysis)
+ if (fc.IsDefinitelyAssigned (variable_info))
return;
- if (!variable_info.IsAssigned (rc)) {
- rc.Report.Error (188, loc,
- "The `this' object cannot be used before all of its fields are assigned to");
- }
+ fc.Report.Error (188, loc, "The `this' object cannot be used before all of its fields are assigned to");
}
protected virtual void Error_ThisNotAvailable (ResolveContext ec)
@@ -7724,6 +7762,11 @@ protected virtual void Error_ThisNotAvailable (ResolveContext ec)
}
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ CheckStructThisDefiniteAssignment (fc);
+ }
+
public override HoistedVariable GetHoistedVariable (AnonymousExpression ae)
{
if (ae == null)
@@ -7781,9 +7824,6 @@ public virtual void ResolveBase (ResolveContext ec)
protected override Expression DoResolve (ResolveContext ec)
{
ResolveBase (ec);
-
- CheckStructThisDefiniteAssignment (ec);
-
return this;
}
@@ -7792,9 +7832,6 @@ public override Expression DoResolveLValue (ResolveContext ec, Expression right_
if (eclass == ExprClass.Unresolved)
ResolveBase (ec);
- if (variable_info != null)
- variable_info.SetAssigned (ec);
-
if (type.IsClass){
if (right_side == EmptyExpression.UnaryAddress)
ec.Report.Error (459, loc, "Cannot take the address of `this' because it is read-only");
@@ -7830,10 +7867,6 @@ public override void SetHasAddressTaken ()
{
// Nothing
}
-
- public override void VerifyDefiniteAssignment (ResolveContext rc)
- {
- }
public override object Accept (StructuralVisitor visitor)
{
@@ -8616,50 +8649,25 @@ public MemberAccess (Expression expr, string identifier, int arity, Location loc
protected override Expression DoResolve (ResolveContext rc)
{
- var e = DoResolveName (rc, null);
-
- if (!rc.OmitStructFlowAnalysis) {
- var fe = e as FieldExpr;
- if (fe != null) {
- fe.VerifyAssignedStructField (rc, null);
- }
- }
+ var e = LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess);
+ if (e != null)
+ e = e.Resolve (rc, ResolveFlags.VariableOrValue | ResolveFlags.Type | ResolveFlags.MethodGroup);
return e;
}
public override Expression DoResolveLValue (ResolveContext rc, Expression rhs)
{
- var e = DoResolveName (rc, rhs);
-
- if (!rc.OmitStructFlowAnalysis) {
- var fe = e as FieldExpr;
- if (fe != null && fe.InstanceExpression is FieldExpr) {
- fe = (FieldExpr) fe.InstanceExpression;
- fe.VerifyAssignedStructField (rc, rhs);
- }
- }
-
- return e;
- }
+ var e = LookupNameExpression (rc, MemberLookupRestrictions.None);
- Expression DoResolveName (ResolveContext rc, Expression right_side)
- {
- Expression e = LookupNameExpression (rc, right_side == null ? MemberLookupRestrictions.ReadAccess : MemberLookupRestrictions.None);
- if (e == null)
+ if (e is TypeExpr) {
+ e.Error_UnexpectedKind (rc, ResolveFlags.VariableOrValue, loc);
return null;
-
- if (right_side != null) {
- if (e is TypeExpr) {
- e.Error_UnexpectedKind (rc, ResolveFlags.VariableOrValue, loc);
- return null;
- }
-
- e = e.ResolveLValue (rc, right_side);
- } else {
- e = e.Resolve (rc, ResolveFlags.VariableOrValue | ResolveFlags.Type | ResolveFlags.MethodGroup);
}
+ if (e != null)
+ e = e.ResolveLValue (rc, rhs);
+
return e;
}
@@ -8684,32 +8692,22 @@ public override Expression LookupNameExpression (ResolveContext rc, MemberLookup
var sn = expr as SimpleName;
const ResolveFlags flags = ResolveFlags.VariableOrValue | ResolveFlags.Type;
- //
- // Resolve the expression with flow analysis turned off, we'll do the definite
- // assignment checks later. This is because we don't know yet what the expression
- // will resolve to - it may resolve to a FieldExpr and in this case we must do the
- // definite assignment check on the actual field and not on the whole struct.
- //
- using (rc.Set (ResolveContext.Options.OmitStructFlowAnalysis)) {
- if (sn != null) {
- expr = sn.LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess | MemberLookupRestrictions.ExactArity);
+ if (sn != null) {
+ expr = sn.LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess | MemberLookupRestrictions.ExactArity);
- //
- // Resolve expression which does have type set as we need expression type
- // with disable flow analysis as we don't know whether left side expression
- // is used as variable or type
- //
- if (expr is VariableReference || expr is ConstantExpr || expr is Linq.TransparentMemberAccess) {
- using (rc.With (ResolveContext.Options.DoFlowAnalysis, false)) {
- expr = expr.Resolve (rc);
- }
- } else if (expr is TypeParameterExpr) {
- expr.Error_UnexpectedKind (rc, flags, sn.Location);
- expr = null;
- }
- } else {
- expr = expr.Resolve (rc, flags);
+ //
+ // Resolve expression which does have type set as we need expression type
+ // with disable flow analysis as we don't know whether left side expression
+ // is used as variable or type
+ //
+ if (expr is VariableReference || expr is ConstantExpr || expr is Linq.TransparentMemberAccess) {
+ expr = expr.Resolve (rc);
+ } else if (expr is TypeParameterExpr) {
+ expr.Error_UnexpectedKind (rc, flags, sn.Location);
+ expr = null;
}
+ } else {
+ expr = expr.Resolve (rc, flags);
}
if (expr == null)
@@ -8737,16 +8735,6 @@ public override Expression LookupNameExpression (ResolveContext rc, MemberLookup
if (me != null)
me.ResolveInstanceExpression (rc, null);
- //
- // Run defined assigned checks on expressions resolved with
- // disabled flow-analysis
- //
- if (sn != null) {
- var vr = expr as VariableReference;
- if (vr != null)
- vr.VerifyDefiniteAssignment (rc);
- }
-
Arguments args = new Arguments (1);
args.Add (new Argument (expr));
return new DynamicMemberBinder (Name, args, loc);
@@ -8777,16 +8765,6 @@ public override Expression LookupNameExpression (ResolveContext rc, MemberLookup
emg.SetTypeArguments (rc, targs);
}
- //
- // Run defined assigned checks on expressions resolved with
- // disabled flow-analysis
- //
- if (sn != null && !errorMode) {
- var vr = expr as VariableReference;
- if (vr != null)
- vr.VerifyDefiniteAssignment (rc);
- }
-
// TODO: it should really skip the checks bellow
return emg.Resolve (rc);
}
@@ -8859,16 +8837,6 @@ public override Expression LookupNameExpression (ResolveContext rc, MemberLookup
me.SetTypeArguments (rc, targs);
}
- //
- // Run defined assigned checks on expressions resolved with
- // disabled flow-analysis
- //
- if (sn != null && !(me is FieldExpr && TypeSpec.IsValueType (expr_type))) {
- var vr = expr as VariableReference;
- if (vr != null)
- vr.VerifyDefiniteAssignment (rc);
- }
-
return me;
}
@@ -9081,6 +9049,11 @@ public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
Expr.EmitBranchable (ec, target, on_true);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ Expr.FlowAnalysis (fc);
+ }
+
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
using (ctx.With (BuilderContext.Options.CheckedScope, true)) {
@@ -9153,6 +9126,11 @@ public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
Expr.EmitBranchable (ec, target, on_true);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ Expr.FlowAnalysis (fc);
+ }
+
protected override void CloneTo (CloneContext clonectx, Expression t)
{
UnCheckedExpr target = (UnCheckedExpr) t;
@@ -9291,6 +9269,12 @@ public static void Error_NamedArgument (NamedArgument na, Report Report)
Report.Error (1742, na.Location, "An element access expression cannot use named argument");
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ Expr.FlowAnalysis (fc);
+ Arguments.FlowAnalysis (fc);
+ }
+
public override string GetSignatureForError ()
{
return Expr.GetSignatureForError ();
@@ -9393,6 +9377,11 @@ protected override void Error_NegativeArrayIndex (ResolveContext ec, Location lo
ec.Report.Warning (251, 2, loc, "Indexing an array with a negative index (array indices always start at zero)");
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ ea.FlowAnalysis (fc);
+ }
+
//
// Load the array arguments into the stack.
//
@@ -9690,6 +9679,13 @@ public override void EmitAssign (EmitContext ec, Expression source, bool leave_c
}
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ // TODO: Check the order
+ base.FlowAnalysis (fc);
+ arguments.FlowAnalysis (fc);
+ }
+
public override string GetSignatureForError ()
{
return best_candidate.GetSignatureForError ();
@@ -10100,6 +10096,11 @@ public override void Emit (EmitContext ec)
ec.Emit (OpCodes.Call, method);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ source.FlowAnalysis (fc);
+ }
+
public override string GetSignatureForError ()
{
return TypeManager.CSharpSignature (method);
@@ -10801,6 +10802,12 @@ public override void EmitStatement (EmitContext ec)
e.EmitStatement (ec);
}
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ foreach (var initializer in initializers)
+ initializer.FlowAnalysis (fc);
+ }
}
//
@@ -10995,6 +11002,12 @@ protected override IMemoryLocation EmitAddressOf (EmitContext ec, AddressOp Mode
return instance;
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ base.FlowAnalysis (fc);
+ initializers.FlowAnalysis (fc);
+ }
+
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
Modified: mcs/mcs/flowanalysis.cs
===================================================================
@@ -18,1055 +18,6 @@
namespace Mono.CSharp
{
// <summary>
- // A new instance of this class is created every time a new block is resolved
- // and if there's branching in the block's control flow.
- // </summary>
- public abstract class FlowBranching
- {
- // <summary>
- // The type of a FlowBranching.
- // </summary>
- public enum BranchingType : byte {
- // Normal (conditional or toplevel) block.
- Block,
-
- // Conditional.
- Conditional,
-
- // A loop block.
- Loop,
-
- // The statement embedded inside a loop
- Embedded,
-
- // part of a block headed by a jump target
- Labeled,
-
- // TryCatch block.
- TryCatch,
-
- // TryFinally, Using, Lock, CollectionForeach
- Exception,
-
- // Switch block.
- Switch,
-
- // The toplevel block of a function
- Toplevel,
-
- // An iterator block
- Iterator
- }
-
- // <summary>
- // The type of one sibling of a branching.
- // </summary>
- public enum SiblingType : byte {
- Block,
- Conditional,
- SwitchSection,
- Try,
- Catch,
- Finally
- }
-
- public static FlowBranching CreateBranching (FlowBranching parent, BranchingType type, Block block, Location loc)
- {
- switch (type) {
- case BranchingType.Exception:
- case BranchingType.Labeled:
- case BranchingType.Toplevel:
- case BranchingType.TryCatch:
- throw new InvalidOperationException ();
-
- case BranchingType.Switch:
- return new FlowBranchingBreakable (parent, type, SiblingType.SwitchSection, block, loc);
-
- case BranchingType.Block:
- return new FlowBranchingBlock (parent, type, SiblingType.Block, block, loc);
-
- case BranchingType.Loop:
- return new FlowBranchingBreakable (parent, type, SiblingType.Conditional, block, loc);
-
- case BranchingType.Embedded:
- return new FlowBranchingContinuable (parent, type, SiblingType.Conditional, block, loc);
-
- default:
- return new FlowBranchingBlock (parent, type, SiblingType.Conditional, block, loc);
- }
- }
-
- // <summary>
- // The type of this flow branching.
- // </summary>
- public readonly BranchingType Type;
-
- // <summary>
- // The block this branching is contained in. This may be null if it's not
- // a top-level block and it doesn't declare any local variables.
- // </summary>
- public readonly Block Block;
-
- // <summary>
- // The parent of this branching or null if this is the top-block.
- // </summary>
- public readonly FlowBranching Parent;
-
- // <summary>
- // Start-Location of this flow branching.
- // </summary>
- public readonly Location Location;
-
- static int next_id;
- int id;
-
- // <summary>
- // The vector contains a BitArray with information about which local variables
- // and parameters are already initialized at the current code position.
- // </summary>
- public class UsageVector {
- // <summary>
- // The type of this branching.
- // </summary>
- public readonly SiblingType Type;
-
- // <summary>
- // Start location of this branching.
- // </summary>
- public Location Location;
-
- // <summary>
- // This is only valid for SwitchSection, Try, Catch and Finally.
- // </summary>
- public readonly Block Block;
-
- // <summary>
- // The number of locals in this block.
- // </summary>
- public readonly int CountLocals;
-
- // <summary>
- // If not null, then we inherit our state from this vector and do a
- // copy-on-write. If null, then we're the first sibling in a top-level
- // block and inherit from the empty vector.
- // </summary>
- public readonly UsageVector InheritsFrom;
-
- // <summary>
- // This is used to construct a list of UsageVector's.
- // </summary>
- public UsageVector Next;
-
- //
- // Private.
- //
- MyBitVector locals;
- bool is_unreachable;
-
- static int next_id;
- int id;
-
- //
- // Normally, you should not use any of these constructors.
- //
- public UsageVector (SiblingType type, UsageVector parent, Block block, Location loc, int num_locals)
- {
- this.Type = type;
- this.Block = block;
- this.Location = loc;
- this.InheritsFrom = parent;
- this.CountLocals = num_locals;
-
- locals = num_locals == 0
- ? MyBitVector.Empty
- : new MyBitVector (parent == null ? MyBitVector.Empty : parent.locals, num_locals);
-
- if (parent != null)
- is_unreachable = parent.is_unreachable;
-
- id = ++next_id;
-
- }
-
- public UsageVector (SiblingType type, UsageVector parent, Block block, Location loc)
- : this (type, parent, block, loc, parent.CountLocals)
- { }
-
- private UsageVector (MyBitVector locals, bool is_unreachable, Block block, Location loc)
- {
- this.Type = SiblingType.Block;
- this.Location = loc;
- this.Block = block;
-
- this.is_unreachable = is_unreachable;
-
- this.locals = locals;
-
- id = ++next_id;
-
- }
-
- // <summary>
- // This does a deep copy of the usage vector.
- // </summary>
- public UsageVector Clone ()
- {
- UsageVector retval = new UsageVector (Type, null, Block, Location, CountLocals);
-
- retval.locals = locals.Clone ();
- retval.is_unreachable = is_unreachable;
-
- return retval;
- }
-
- public bool IsAssigned (VariableInfo var, bool ignoreReachability)
- {
- if (!ignoreReachability && !var.IsParameter && IsUnreachable)
- return true;
-
- return var.IsAssigned (locals);
- }
-
- public void SetAssigned (VariableInfo var)
- {
- if (!var.IsParameter && IsUnreachable)
- return;
-
- var.SetAssigned (locals);
- }
-
- public bool IsFieldAssigned (VariableInfo var, string name)
- {
- if (/*!var.IsParameter &&*/ IsUnreachable)
- return true;
-
- return var.IsStructFieldAssigned (locals, name);
- }
-
- public void SetFieldAssigned (VariableInfo var, string name)
- {
- if (/*!var.IsParameter &&*/ IsUnreachable)
- return;
-
- var.SetStructFieldAssigned (locals, name);
- }
-
- public bool IsUnreachable {
- get {
- return is_unreachable;
- }
- set {
- is_unreachable = value;
- }
- }
-
- public void ResetBarrier ()
- {
- is_unreachable = false;
- }
-
- public void Goto ()
- {
- is_unreachable = true;
- }
-
- public static UsageVector MergeSiblings (UsageVector sibling_list, Location loc)
- {
- if (sibling_list.Next == null)
- return sibling_list;
-
- MyBitVector locals = null;
- bool is_unreachable = sibling_list.is_unreachable;
-
- if (!sibling_list.IsUnreachable)
- locals &= sibling_list.locals;
-
- for (UsageVector child = sibling_list.Next; child != null; child = child.Next) {
- is_unreachable &= child.is_unreachable;
-
- if (!child.IsUnreachable)
- locals &= child.locals;
- }
-
- return new UsageVector (locals, is_unreachable, null, loc);
- }
-
- // <summary>
- // Merges a child branching.
- // </summary>
- public UsageVector MergeChild (UsageVector child, bool overwrite)
- {
- Report.Debug (2, " MERGING CHILD EFFECTS", this, child, Type);
-
- bool new_isunr = child.is_unreachable;
-
- //
- // We've now either reached the point after the branching or we will
- // never get there since we always return or always throw an exception.
- //
- // If we can reach the point after the branching, mark all locals and
- // parameters as initialized which have been initialized in all branches
- // we need to look at (see above).
- //
-
- if ((Type == SiblingType.SwitchSection) && !new_isunr) {
- Report.Error (163, Location,
- "Control cannot fall through from one " +
- "case label to another");
- return child;
- }
-
- locals |= child.locals;
-
- // throw away un-necessary information about variables in child blocks
- if (locals.Count != CountLocals)
- locals = new MyBitVector (locals, CountLocals);
-
- if (overwrite)
- is_unreachable = new_isunr;
- else
- is_unreachable |= new_isunr;
-
- return child;
- }
-
- public void MergeOrigins (UsageVector o_vectors)
- {
- Report.Debug (1, " MERGING BREAK ORIGINS", this);
-
- if (o_vectors == null)
- return;
-
- if (IsUnreachable && locals != null)
- locals.SetAll (true);
-
- for (UsageVector vector = o_vectors; vector != null; vector = vector.Next) {
- Report.Debug (1, " MERGING BREAK ORIGIN", vector);
- if (vector.IsUnreachable)
- continue;
- locals &= vector.locals;
- is_unreachable &= vector.is_unreachable;
- }
-
- Report.Debug (1, " MERGING BREAK ORIGINS DONE", this);
- }
-
- //
- // Debugging stuff.
- //
-
- public override string ToString ()
- {
- return String.Format ("Vector ({0},{1},{2}-{3})", Type, id, is_unreachable, locals);
- }
- }
-
- // <summary>
- // Creates a new flow branching which is contained in `parent'.
- // You should only pass non-null for the `block' argument if this block
- // introduces any new variables - in this case, we need to create a new
- // usage vector with a different size than our parent's one.
- // </summary>
- protected FlowBranching (FlowBranching parent, BranchingType type, SiblingType stype,
- Block block, Location loc)
- {
- Parent = parent;
- Block = block;
- Location = loc;
- Type = type;
- id = ++next_id;
-
- UsageVector vector;
- if (Block != null) {
- UsageVector parent_vector = parent != null ? parent.CurrentUsageVector : null;
- vector = new UsageVector (stype, parent_vector, Block, loc, Block.AssignableSlots);
- } else {
- vector = new UsageVector (stype, Parent.CurrentUsageVector, null, loc);
- }
-
- AddSibling (vector);
- }
-
- public abstract UsageVector CurrentUsageVector {
- get;
- }
-
- // <summary>
- // Creates a sibling of the current usage vector.
- // </summary>
- public void CreateSibling (Block block, SiblingType type)
- {
- UsageVector vector = new UsageVector (
- type, Parent.CurrentUsageVector, block, Location);
- AddSibling (vector);
-
- Report.Debug (1, " CREATED SIBLING", CurrentUsageVector);
- }
-
- public void CreateSibling ()
- {
- CreateSibling (null, SiblingType.Conditional);
- }
-
- protected abstract void AddSibling (UsageVector uv);
-
- protected abstract UsageVector Merge ();
-
- public UsageVector MergeChild (FlowBranching child)
- {
- return CurrentUsageVector.MergeChild (child.Merge (), true);
- }
-
- public virtual bool CheckRethrow (Location loc)
- {
- return Parent.CheckRethrow (loc);
- }
-
- public virtual bool AddResumePoint (ResumableStatement stmt, ResumableStatement current, out int pc)
- {
- return Parent.AddResumePoint (stmt, current, out pc);
- }
-
- // returns true if we crossed an unwind-protected region (try/catch/finally, lock, using, ...)
- public virtual bool AddBreakOrigin (UsageVector vector, Location loc)
- {
- return Parent.AddBreakOrigin (vector, loc);
- }
-
- // returns true if we crossed an unwind-protected region (try/catch/finally, lock, using, ...)
- public virtual bool AddContinueOrigin (UsageVector vector, Location loc)
- {
- return Parent.AddContinueOrigin (vector, loc);
- }
-
- // returns true if we crossed an unwind-protected region (try/catch/finally, lock, using, ...)
- public virtual bool AddReturnOrigin (UsageVector vector, ExitStatement stmt)
- {
- return Parent.AddReturnOrigin (vector, stmt);
- }
-
- // 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 bool IsAssigned (VariableInfo vi)
- {
- return CurrentUsageVector.IsAssigned (vi, false);
- }
-
- public bool IsStructFieldAssigned (VariableInfo vi, string field_name)
- {
- return CurrentUsageVector.IsAssigned (vi, false) || CurrentUsageVector.IsFieldAssigned (vi, field_name);
- }
-
- protected static Report Report {
- get { return RootContext.ToplevelTypes.Compiler.Report; }
- }
-
- public void SetAssigned (VariableInfo vi)
- {
- CurrentUsageVector.SetAssigned (vi);
- }
-
- public void SetFieldAssigned (VariableInfo vi, string name)
- {
- CurrentUsageVector.SetFieldAssigned (vi, name);
- }
-
-#if DEBUG
- public override string ToString ()
- {
- StringBuilder sb = new StringBuilder ();
- sb.Append (GetType ());
- sb.Append (" (");
-
- sb.Append (id);
- sb.Append (",");
- sb.Append (Type);
- if (Block != null) {
- sb.Append (" - ");
- sb.Append (Block.ID);
- sb.Append (" - ");
- sb.Append (Block.StartLocation);
- }
- sb.Append (" - ");
- // sb.Append (Siblings.Length);
- // sb.Append (" - ");
- sb.Append (CurrentUsageVector);
- sb.Append (")");
- return sb.ToString ();
- }
-#endif
-
- public string Name {
- get { return String.Format ("{0} ({1}:{2}:{3})", GetType (), id, Type, Location); }
- }
- }
-
- public class FlowBranchingBlock : FlowBranching
- {
- UsageVector sibling_list = null;
-
- public FlowBranchingBlock (FlowBranching parent, BranchingType type,
- SiblingType stype, Block block, Location loc)
- : base (parent, type, stype, block, loc)
- { }
-
- public override UsageVector CurrentUsageVector {
- get { return sibling_list; }
- }
-
- protected override void AddSibling (UsageVector sibling)
- {
- if (sibling_list != null && sibling_list.Type == SiblingType.Block)
- throw new InternalErrorException ("Blocks don't have sibling flow paths");
- sibling.Next = sibling_list;
- sibling_list = sibling;
- }
-
- public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
- {
- LabeledStatement stmt = Block == null ? null : Block.LookupLabel (goto_stmt.Target);
- if (stmt == null)
- return Parent.AddGotoOrigin (vector, goto_stmt);
-
- // forward jump
- goto_stmt.SetResolvedTarget (stmt);
- stmt.AddUsageVector (vector);
- return false;
- }
-
- public static void Error_UnknownLabel (Location loc, string label, Report Report)
- {
- Report.Error(159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
- label);
- }
-
- protected override UsageVector Merge ()
- {
- Report.Debug (2, " MERGING SIBLINGS", Name);
- UsageVector vector = UsageVector.MergeSiblings (sibling_list, Location);
- Report.Debug (2, " MERGING SIBLINGS DONE", Name, vector);
- return vector;
- }
- }
-
- public class FlowBranchingBreakable : FlowBranchingBlock
- {
- UsageVector break_origins;
-
- public FlowBranchingBreakable (FlowBranching parent, BranchingType type, SiblingType stype, Block block, Location loc)
- : base (parent, type, stype, block, loc)
- { }
-
- public override bool AddBreakOrigin (UsageVector vector, Location loc)
- {
- vector = vector.Clone ();
- vector.Next = break_origins;
- break_origins = vector;
- return false;
- }
-
- protected override UsageVector Merge ()
- {
- UsageVector vector = base.Merge ();
- vector.MergeOrigins (break_origins);
- return vector;
- }
- }
-
- public class FlowBranchingContinuable : FlowBranchingBlock
- {
- UsageVector continue_origins;
-
- public FlowBranchingContinuable (FlowBranching parent, BranchingType type, SiblingType stype, Block block, Location loc)
- : base (parent, type, stype, block, loc)
- { }
-
- public override bool AddContinueOrigin (UsageVector vector, Location loc)
- {
- vector = vector.Clone ();
- vector.Next = continue_origins;
- continue_origins = vector;
- return false;
- }
-
- protected override UsageVector Merge ()
- {
- UsageVector vector = base.Merge ();
- vector.MergeOrigins (continue_origins);
- return vector;
- }
- }
-
- public class FlowBranchingLabeled : FlowBranchingBlock
- {
- LabeledStatement stmt;
- UsageVector actual;
-
- public FlowBranchingLabeled (FlowBranching parent, LabeledStatement stmt)
- : base (parent, BranchingType.Labeled, SiblingType.Conditional, null, stmt.loc)
- {
- this.stmt = stmt;
- CurrentUsageVector.MergeOrigins (stmt.JumpOrigins);
- actual = CurrentUsageVector.Clone ();
-
- // stand-in for backward jumps
- CurrentUsageVector.ResetBarrier ();
- }
-
- public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
- {
- if (goto_stmt.Target != stmt.Name)
- return Parent.AddGotoOrigin (vector, goto_stmt);
-
- // backward jump
- goto_stmt.SetResolvedTarget (stmt);
- actual.MergeOrigins (vector.Clone ());
-
- return false;
- }
-
- protected override UsageVector Merge ()
- {
- UsageVector vector = base.Merge ();
-
- if (actual.IsUnreachable)
- Report.Warning (162, 2, stmt.loc, "Unreachable code detected");
-
- actual.MergeChild (vector, false);
- return actual;
- }
- }
-
- public class FlowBranchingIterator : FlowBranchingBlock
- {
- readonly Iterator iterator;
-
- public FlowBranchingIterator (FlowBranching parent, Iterator iterator)
- : base (parent, BranchingType.Iterator, SiblingType.Block, iterator.Block, iterator.Location)
- {
- this.iterator = iterator;
- }
-
- public override bool AddResumePoint (ResumableStatement stmt, ResumableStatement current, out int pc)
- {
- pc = iterator.AddResumePoint (current);
- return false;
- }
- }
-
- public class FlowBranchingToplevel : FlowBranchingBlock
- {
- UsageVector return_origins;
-
- public FlowBranchingToplevel (FlowBranching parent, ParametersBlock stmt)
- : base (parent, BranchingType.Toplevel, SiblingType.Conditional, stmt, stmt.loc)
- {
- }
-
- public override bool CheckRethrow (Location loc)
- {
- Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
- return false;
- }
-
- public override bool AddResumePoint (ResumableStatement stmt, ResumableStatement current, out int pc)
- {
- throw new InternalErrorException ("A yield in a non-iterator block");
- }
-
- public override bool AddBreakOrigin (UsageVector vector, Location loc)
- {
- Report.Error (139, loc, "No enclosing loop out of which to break or continue");
- return false;
- }
-
- public override bool AddContinueOrigin (UsageVector vector, Location loc)
- {
- Report.Error (139, loc, "No enclosing loop out of which to break or continue");
- return false;
- }
-
- public override bool AddReturnOrigin (UsageVector vector, ExitStatement stmt)
- {
- vector = vector.Clone ();
- vector.Location = stmt.loc;
- vector.Next = return_origins;
- return_origins = vector;
- return false;
- }
-
- 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) {
- Error_UnknownLabel (goto_stmt.loc, name, Report);
- return false;
- }
-
- 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;
- }
-
- protected override UsageVector Merge ()
- {
- for (UsageVector origin = return_origins; origin != null; origin = origin.Next)
- Block.ParametersBlock.CheckOutParameters (origin);
-
- UsageVector vector = base.Merge ();
- Block.ParametersBlock.CheckOutParameters (vector);
- // Note: we _do_not_ merge in the return origins
- return vector;
- }
-
- public bool End ()
- {
- return Merge ().IsUnreachable;
- }
- }
-
- public class FlowBranchingTryCatch : FlowBranchingBlock
- {
- readonly TryCatch tc;
-
- public FlowBranchingTryCatch (FlowBranching parent, TryCatch stmt)
- : base (parent, BranchingType.Block, SiblingType.Try, null, stmt.loc)
- {
- this.tc = stmt;
- }
-
- public override bool CheckRethrow (Location loc)
- {
- return CurrentUsageVector.Next != null || Parent.CheckRethrow (loc);
- }
-
- public override bool AddResumePoint (ResumableStatement stmt, ResumableStatement current, out int pc)
- {
- int errors = Report.Errors;
- Parent.AddResumePoint (stmt, tc.IsTryCatchFinally ? current : tc, out pc);
- if (errors == Report.Errors) {
- if (stmt is AwaitStatement) {
- if (CurrentUsageVector.Next != null) {
- Report.Error (1985, stmt.loc, "The `await' operator cannot be used in the body of a catch clause");
- } else {
- this.tc.AddResumePoint (current, pc);
- }
- } else {
- if (CurrentUsageVector.Next == null)
- Report.Error (1626, stmt.loc, "Cannot yield a value in the body of a try block with a catch clause");
- else
- Report.Error (1631, stmt.loc, "Cannot yield a value in the body of a catch clause");
- }
- }
-
- return true;
- }
-
- public override bool AddBreakOrigin (UsageVector vector, Location loc)
- {
- Parent.AddBreakOrigin (vector, loc);
- tc.SomeCodeFollows ();
- return true;
- }
-
- public override bool AddContinueOrigin (UsageVector vector, Location loc)
- {
- Parent.AddContinueOrigin (vector, loc);
- tc.SomeCodeFollows ();
- return true;
- }
-
- public override bool AddReturnOrigin (UsageVector vector, ExitStatement exit_stmt)
- {
- Parent.AddReturnOrigin (vector, exit_stmt);
- tc.SomeCodeFollows ();
- return true;
- }
-
- public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
- {
- Parent.AddGotoOrigin (vector, goto_stmt);
- return true;
- }
- }
-
- public class FlowBranchingAsync : FlowBranchingBlock
- {
- readonly AsyncInitializer async_init;
-
- public FlowBranchingAsync (FlowBranching parent, AsyncInitializer async_init)
- : base (parent, BranchingType.Block, SiblingType.Try, null, async_init.Location)
- {
- this.async_init = async_init;
- }
-/*
- public override bool CheckRethrow (Location loc)
- {
- return CurrentUsageVector.Next != null || Parent.CheckRethrow (loc);
- }
-*/
- public override bool AddResumePoint (ResumableStatement stmt, ResumableStatement current, out int pc)
- {
- pc = async_init.AddResumePoint (current);
- return true;
- }
-
- public override bool AddBreakOrigin (UsageVector vector, Location loc)
- {
- Parent.AddBreakOrigin (vector, loc);
- return true;
- }
-
- public override bool AddContinueOrigin (UsageVector vector, Location loc)
- {
- Parent.AddContinueOrigin (vector, loc);
- return true;
- }
-
- public override bool AddReturnOrigin (UsageVector vector, ExitStatement exit_stmt)
- {
- Parent.AddReturnOrigin (vector, exit_stmt);
- return true;
- }
-
- public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt)
- {
- Parent.AddGotoOrigin (vector, goto_stmt);
- return true;
- }
- }
-
- public class FlowBranchingTryFinally : FlowBranching
- {
- ExceptionStatement stmt;
- UsageVector current_vector;
- UsageVector try_vector;
- UsageVector finally_vector;
-
- abstract class SavedOrigin {
- public readonly SavedOrigin Next;
- public readonly UsageVector Vector;
-
- protected SavedOrigin (SavedOrigin next, UsageVector vector)
- {
- Next = next;
- Vector = vector.Clone ();
- }
-
- protected abstract void DoPropagateFinally (FlowBranching parent);
- public void PropagateFinally (UsageVector finally_vector, FlowBranching parent)
- {
- if (finally_vector != null)
- Vector.MergeChild (finally_vector, false);
- DoPropagateFinally (parent);
- }
- }
-
- class BreakOrigin : SavedOrigin {
- Location Loc;
- public BreakOrigin (SavedOrigin next, UsageVector vector, Location loc)
- : base (next, vector)
- {
- Loc = loc;
- }
-
- protected override void DoPropagateFinally (FlowBranching parent)
- {
- parent.AddBreakOrigin (Vector, Loc);
- }
- }
-
- class ContinueOrigin : SavedOrigin {
- Location Loc;
- public ContinueOrigin (SavedOrigin next, UsageVector vector, Location loc)
- : base (next, vector)
- {
- Loc = loc;
- }
-
- protected override void DoPropagateFinally (FlowBranching parent)
- {
- parent.AddContinueOrigin (Vector, Loc);
- }
- }
-
- class ReturnOrigin : SavedOrigin {
- public ExitStatement Stmt;
-
- public ReturnOrigin (SavedOrigin next, UsageVector vector, ExitStatement stmt)
- : base (next, vector)
- {
- Stmt = stmt;
- }
-
- protected override void DoPropagateFinally (FlowBranching parent)
- {
- parent.AddReturnOrigin (Vector, Stmt);
- }
- }
-
- class GotoOrigin : SavedOrigin {
- public Goto Stmt;
-
- public GotoOrigin (SavedOrigin next, UsageVector vector, Goto stmt)
- : base (next, vector)
- {
- Stmt = stmt;
- }
-
- protected override void DoPropagateFinally (FlowBranching parent)
- {
- parent.AddGotoOrigin (Vector, Stmt);
- }
- }
-
- SavedOrigin saved_origins;
-
- public FlowBranchingTryFinally (FlowBranching parent,
- ExceptionStatement stmt)
- : base (parent, BranchingType.Exception, SiblingType.Try,
- null, stmt.loc)
- {
- this.stmt = stmt;
- }
-
- protected override void AddSibling (UsageVector sibling)
- {
- switch (sibling.Type) {
- case SiblingType.Try:
- try_vector = sibling;
- break;
- case SiblingType.Finally:
- finally_vector = sibling;
- break;
- default:
- throw new InvalidOperationException ();
- }
- current_vector = sibling;
- }
-
- public override UsageVector CurrentUsageVector {
- get { return current_vector; }
- }
-
- public override bool CheckRethrow (Location loc)
- {
- if (!Parent.CheckRethrow (loc))
- return false;
- if (finally_vector == null)
- return true;
- Report.Error (724, loc, "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
- return false;
- }
-
- public override bool AddResumePoint (ResumableStatement stmt, ResumableStatement current, out int pc)
- {
- int errors = Report.Errors;
- Parent.AddResumePoint (stmt, this.stmt, out pc);
- if (errors == Report.Errors) {
- if (finally_vector == null)
- this.stmt.AddResumePoint (current, pc);
- else {
- if (stmt is AwaitStatement) {
- Report.Error (1984, stmt.loc, "The `await' operator cannot be used in the body of a finally clause");
- } else {
- Report.Error (1625, stmt.loc, "Cannot yield in the body of a finally clause");
- }
- }
- }
- return true;
- }
-
- public override bool AddBreakOrigin (UsageVector vector, Location loc)
- {
- if (finally_vector != null) {
- int errors = Report.Errors;
- Parent.AddBreakOrigin (vector, loc);
- if (errors == Report.Errors)
- Report.Error (157, loc, "Control cannot leave the body of a finally clause");
- } else {
- saved_origins = new BreakOrigin (saved_origins, vector, loc);
- }
-
- // either the loop test or a back jump will follow code
- stmt.SomeCodeFollows ();
- return true;
- }
-
- public override bool AddContinueOrigin (UsageVector vector, Location loc)
- {
- if (finally_vector != null) {
- int errors = Report.Errors;
- Parent.AddContinueOrigin (vector, loc);
- if (errors == Report.Errors)
- Report.Error (157, loc, "Control cannot leave the body of a finally clause");
- } else {
- saved_origins = new ContinueOrigin (saved_origins, vector, loc);
- }
-
- // either the loop test or a back jump will follow code
- stmt.SomeCodeFollows ();
- return true;
- }
-
- public override bool AddReturnOrigin (UsageVector vector, ExitStatement exit_stmt)
- {
- if (finally_vector != null) {
- int errors = Report.Errors;
- Parent.AddReturnOrigin (vector, exit_stmt);
- if (errors == Report.Errors)
- exit_stmt.Error_FinallyClause (Report);
- } else {
- saved_origins = new ReturnOrigin (saved_origins, vector, exit_stmt);
- }
-
- // sets ec.NeedReturnLabel()
- stmt.SomeCodeFollows ();
- 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) {
- int errors = Report.Errors;
- Parent.AddGotoOrigin (vector, goto_stmt);
- if (errors == Report.Errors)
- Report.Error (157, goto_stmt.loc, "Control cannot leave the body of a finally clause");
- } else {
- saved_origins = new GotoOrigin (saved_origins, vector, goto_stmt);
- }
- return true;
- }
-
- protected override UsageVector Merge ()
- {
- UsageVector vector = try_vector.Clone ();
-
- if (finally_vector != null)
- vector.MergeChild (finally_vector, false);
-
- for (SavedOrigin origin = saved_origins; origin != null; origin = origin.Next)
- origin.PropagateFinally (finally_vector, Parent);
-
- return vector;
- }
- }
-
- // <summary>
// This is used by the flow analysis code to keep track of the type of local variables.
//
// The flow code uses a BitVector to keep track of whether a variable has been assigned
@@ -1178,23 +129,22 @@ public static TypeInfo GetTypeInfo (TypeSpec type)
// A struct's constructor must always assign all fields.
// This method checks whether it actually does so.
// </summary>
- public bool IsFullyInitialized (BlockContext ec, VariableInfo vi, Location loc)
+ public bool IsFullyInitialized (FlowAnalysisContext fc, VariableInfo vi, Location loc)
{
if (struct_info == null)
return true;
bool ok = true;
- FlowBranching branching = ec.CurrentBranching;
for (int i = 0; i < struct_info.Count; i++) {
- var field = struct_info.Fields [i];
+ var field = struct_info.Fields[i];
- if (!branching.IsStructFieldAssigned (vi, field.Name)) {
+ if (!fc.IsStructFieldDefinitelyAssigned (vi, field.Name)) {
if (field.MemberDefinition is Property.BackingField) {
- ec.Report.Error (843, loc,
+ fc.Report.Error (843, loc,
"An automatically implemented property `{0}' must be fully assigned before control leaves the constructor. Consider calling the default struct contructor from a constructor initializer",
field.GetSignatureForError ());
} else {
- ec.Report.Error (171, loc,
+ fc.Report.Error (171, loc,
"Field `{0}' must be fully assigned before control leaves the constructor",
field.GetSignatureForError ());
}
@@ -1401,12 +351,7 @@ public static VariableInfo Create (BlockContext bc, Parameter parameter)
return info;
}
- public bool IsAssigned (ResolveContext ec)
- {
- return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (this);
- }
-
- public bool IsAssigned (MyBitVector vector)
+ public bool IsAssigned (DefiniteAssignmentBitSet vector)
{
if (vector == null)
return true;
@@ -1445,23 +390,18 @@ public bool IsAssigned (MyBitVector vector)
return false;
}
- vector [Offset] = true;
+ vector.Set (Offset);
return true;
}
public bool IsEverAssigned { get; set; }
- public bool IsStructFieldAssigned (ResolveContext ec, string name)
- {
- return !ec.DoFlowAnalysis || ec.CurrentBranching.IsStructFieldAssigned (this, name);
- }
-
- public bool IsFullyInitialized (BlockContext bc, Location loc)
+ public bool IsFullyInitialized (FlowAnalysisContext fc, Location loc)
{
- return TypeInfo.IsFullyInitialized (bc, this, loc);
+ return TypeInfo.IsFullyInitialized (fc, this, loc);
}
- public bool IsStructFieldAssigned (MyBitVector vector, string field_name)
+ public bool IsStructFieldAssigned (DefiniteAssignmentBitSet vector, string field_name)
{
int field_idx = TypeInfo.GetFieldIndex (field_name);
@@ -1471,31 +411,20 @@ public bool IsStructFieldAssigned (MyBitVector vector, string field_name)
return vector [Offset + field_idx];
}
- public void SetStructFieldAssigned (ResolveContext ec, string name)
- {
- if (ec.DoFlowAnalysis)
- ec.CurrentBranching.SetFieldAssigned (this, name);
- }
-
- public void SetAssigned (ResolveContext ec)
- {
- if (ec.DoFlowAnalysis)
- ec.CurrentBranching.SetAssigned (this);
- }
-
- public void SetAssigned (MyBitVector vector)
+ public void SetAssigned (DefiniteAssignmentBitSet vector, bool generatedAssignment)
{
if (Length == 1)
- vector[Offset] = true;
+ vector.Set (Offset);
else
- vector.SetRange (Offset, Length);
+ vector.Set (Offset, Length);
- IsEverAssigned = true;
+ if (!generatedAssignment)
+ IsEverAssigned = true;
}
- public void SetStructFieldAssigned (MyBitVector vector, string field_name)
+ public void SetStructFieldAssigned (DefiniteAssignmentBitSet vector, string field_name)
{
- if (vector[Offset])
+ if (vector [Offset])
return;
int field_idx = TypeInfo.GetFieldIndex (field_name);
@@ -1505,15 +434,15 @@ public void SetStructFieldAssigned (MyBitVector vector, string field_name)
var complex_field = TypeInfo.GetStructField (field_name);
if (complex_field != null) {
- vector.SetRange (Offset + complex_field.Offset, complex_field.TotalLength);
+ vector.Set (Offset + complex_field.Offset, complex_field.TotalLength);
} else {
- vector[Offset + field_idx] = true;
+ vector.Set (Offset + field_idx);
}
IsEverAssigned = true;
//
- // Each field must be assigned
+ // Each field must be assigned before setting master bit
//
for (int i = Offset + 1; i < TypeInfo.TotalLength + Offset; i++) {
if (!vector[i])
@@ -1524,7 +453,7 @@ public void SetStructFieldAssigned (MyBitVector vector, string field_name)
// Set master struct flag to assigned when all tested struct
// fields have been assigned
//
- vector[Offset] = true;
+ vector.Set (Offset);
}
public VariableInfo GetStructFieldInfo (string fieldName)
@@ -1539,274 +468,124 @@ public VariableInfo GetStructFieldInfo (string fieldName)
public override string ToString ()
{
- return String.Format ("VariableInfo ({0}:{1}:{2}:{3}:{4})",
- Name, TypeInfo, Offset, Length, IsParameter);
+ return String.Format ("Name={0} Offset={1} Length={2} {3})", Name, Offset, Length, TypeInfo);
}
}
- // <summary>
- // This is a special bit vector which can inherit from another bit vector doing a
- // copy-on-write strategy. The inherited vector may have a smaller size than the
- // current one.
- // </summary>
- public class MyBitVector {
- public readonly int Count;
- public static readonly MyBitVector Empty = new MyBitVector ();
-
- // Invariant: vector != null => vector.Count == Count
- // Invariant: vector == null || shared == null
- // i.e., at most one of 'vector' and 'shared' can be non-null. They can both be null -- that means all-ones
- // The object in 'shared' cannot be modified, while 'vector' can be freely modified
- System.Collections.BitArray vector, shared;
+ public struct Reachability
+ {
+ readonly bool unreachable;
- MyBitVector ()
+ Reachability (bool unreachable)
{
- shared = new System.Collections.BitArray (0, false);
+ this.unreachable = unreachable;
}
- public MyBitVector (MyBitVector InheritsFrom, int Count)
- {
- if (InheritsFrom != null)
- shared = InheritsFrom.MakeShared (Count);
-
- this.Count = Count;
+ public bool IsUnreachable {
+ get {
+ return unreachable;
+ }
}
- System.Collections.BitArray MakeShared (int new_count)
+ public static Reachability CreateUnreachable ()
{
- // Post-condition: vector == null
-
- // ensure we don't leak out dirty bits from the BitVector we inherited from
- if (new_count > Count &&
- ((shared != null && shared.Count > Count) ||
- (shared == null && vector == null)))
- initialize_vector ();
-
- if (vector != null) {
- shared = vector;
- vector = null;
- }
-
- return shared;
+ return new Reachability (true);
}
- // <summary>
- // Get/set bit `index' in the bit vector.
- // </summary>
- public bool this [int index] {
- get {
- if (index >= Count)
- // FIXME: Disabled due to missing anonymous method flow analysis
- // throw new ArgumentOutOfRangeException ();
- return true;
-
- if (vector != null)
- return vector [index];
- if (shared == null)
- return true;
- if (index < shared.Count)
- return shared [index];
- return false;
- }
-
- set {
- // Only copy the vector if we're actually modifying it.
- if (this [index] != value) {
- if (vector == null)
- initialize_vector ();
- vector [index] = value;
- }
- }
+ public static Reachability operator & (Reachability a, Reachability b)
+ {
+ return new Reachability (a.unreachable && b.unreachable);
}
- // <summary>
- // Performs an `or' operation on the bit vector. The `new_vector' may have a
- // different size than the current one.
- // </summary>
- private MyBitVector Or (MyBitVector new_vector)
+ public static Reachability operator | (Reachability a, Reachability b)
{
- if (Count == 0 || new_vector.Count == 0)
- return this;
-
- var o = new_vector.vector != null ? new_vector.vector : new_vector.shared;
-
- if (o == null) {
- int n = new_vector.Count;
- if (n < Count) {
- for (int i = 0; i < n; ++i)
- this [i] = true;
- } else {
- SetAll (true);
- }
- return this;
- }
-
- if (Count == o.Count) {
- if (vector == null) {
- if (shared == null)
- return this;
- initialize_vector ();
- }
- vector.Or (o);
- return this;
- }
+ return new Reachability (a.unreachable | b.unreachable);
+ }
+ }
- int min = o.Count;
- if (Count < min)
- min = Count;
+ public class DefiniteAssignmentBitSet
+ {
+ // Make it
+ // int bits;
+ // int int[] bits_extended; // when bits overflows
- for (int i = 0; i < min; i++) {
- if (o [i])
- this [i] = true;
- }
+ System.Collections.BitArray bits;
+ bool copy_on_write;
- return this;
+ public DefiniteAssignmentBitSet ()
+ {
+ bits = new System.Collections.BitArray (4096); // TODO:
}
- // <summary>
- // Performs an `and' operation on the bit vector. The `new_vector' may have
- // a different size than the current one.
- // </summary>
- private MyBitVector And (MyBitVector new_vector)
+ public DefiniteAssignmentBitSet (DefiniteAssignmentBitSet source)
{
- if (Count == 0)
- return this;
-
- var o = new_vector.vector != null ? new_vector.vector : new_vector.shared;
-
- if (o == null) {
- for (int i = new_vector.Count; i < Count; ++i)
- this [i] = false;
- return this;
- }
+ bits = source.bits;
- if (o.Count == 0) {
- SetAll (false);
- return this;
- }
-
- if (Count == o.Count) {
- if (vector == null) {
- if (shared == null) {
- shared = new_vector.MakeShared (Count);
- return this;
- }
- initialize_vector ();
- }
- vector.And (o);
- return this;
- }
-
- int min = o.Count;
- if (Count < min)
- min = Count;
-
- for (int i = 0; i < min; i++) {
- if (! o [i])
- this [i] = false;
- }
-
- for (int i = min; i < Count; i++)
- this [i] = false;
-
- return this;
+ copy_on_write = true;
}
- public static MyBitVector operator & (MyBitVector a, MyBitVector b)
+ private DefiniteAssignmentBitSet (System.Collections.BitArray bits)
{
- if (a == b)
- return a;
- if (a == null)
- return b.Clone ();
- if (b == null)
- return a.Clone ();
- if (a.Count > b.Count)
- return a.Clone ().And (b);
- else
- return b.Clone ().And (a);
+ this.bits = bits;
}
- public static MyBitVector operator | (MyBitVector a, MyBitVector b)
+ public static DefiniteAssignmentBitSet operator & (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b)
{
- if (a == b)
+ if (a.bits == b.bits)
return a;
- if (a == null)
- return new MyBitVector (null, b.Count);
- if (b == null)
- return new MyBitVector (null, a.Count);
- if (a.Count > b.Count)
- return a.Clone ().Or (b);
- else
- return b.Clone ().Or (a);
+
+ return new DefiniteAssignmentBitSet (a.bits.And (b.bits));
}
- public MyBitVector Clone ()
+ public static DefiniteAssignmentBitSet operator | (DefiniteAssignmentBitSet a, DefiniteAssignmentBitSet b)
{
- return Count == 0 ? Empty : new MyBitVector (this, Count);
+ if (a.bits == b.bits)
+ return a;
+
+ return new DefiniteAssignmentBitSet (a.bits.Or (b.bits));
}
- public void SetRange (int offset, int length)
+ public static DefiniteAssignmentBitSet And (List<DefiniteAssignmentBitSet> das)
{
- if (offset > Count || offset + length > Count)
- throw new ArgumentOutOfRangeException ("flow-analysis");
+ if (das.Count == 0)
+ return new DefiniteAssignmentBitSet ();
- if (shared == null && vector == null)
- return;
-
- int i = 0;
- if (shared != null) {
- if (offset + length <= shared.Count) {
- for (; i < length; ++i)
- if (!shared [i+offset])
- break;
- if (i == length)
- return;
- }
- initialize_vector ();
+ DefiniteAssignmentBitSet res = das [0];
+ for (int i = 1; i < das.Count; ++i) {
+ res &= das[i];
}
- for (; i < length; ++i)
- vector [i+offset] = true;
+ return res;
}
- public void SetAll (bool value)
+ public void Set (int index)
{
- // Don't clobber Empty
- if (Count == 0)
- return;
- shared = value ? null : Empty.MakeShared (Count);
- vector = null;
+ if (copy_on_write && !bits[index])
+ Clone ();
+
+ bits[index] = true;
}
- void initialize_vector ()
+ public void Set (int index, int length)
{
- // Post-condition: vector != null
- if (shared == null) {
- vector = new System.Collections.BitArray (Count, true);
- return;
- }
+ for (int i = 0; i < length; ++i) {
+ if (copy_on_write && !bits[index + i])
+ Clone ();
- vector = new System.Collections.BitArray (shared);
- if (Count != vector.Count)
- vector.Length = Count;
- shared = null;
- }
+ bits[index + i] = true;
+ }
+ }
- StringBuilder Dump (StringBuilder sb)
- {
- var dump = vector == null ? shared : vector;
- if (dump == null)
- return sb.Append ("/");
- if (dump == shared)
- sb.Append ("=");
- for (int i = 0; i < dump.Count; i++)
- sb.Append (dump [i] ? "1" : "0");
- return sb;
+ public bool this [int index] {
+ get {
+ return bits [index];
+ }
}
- public override string ToString ()
+ void Clone ()
{
- return Dump (new StringBuilder ("{")).Append ("}").ToString ();
+ bits = new System.Collections.BitArray (bits);
+ copy_on_write = false;
}
}
}
Modified: mcs/mcs/iterators.cs
===================================================================
@@ -29,6 +29,7 @@ public abstract class YieldStatement<T> : ResumableStatement where T : StateMach
protected bool unwind_protect;
protected T machine_initializer;
int resume_pc;
+ ExceptionStatement inside_try_block;
protected YieldStatement (Expression expr, Location l)
{
@@ -51,6 +52,15 @@ protected override void DoEmit (EmitContext ec)
machine_initializer.InjectYield (ec, expr, resume_pc, unwind_protect, resume_point);
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+
+ RegisterResumePoint ();
+
+ return false;
+ }
+
public override bool Resolve (BlockContext bc)
{
expr = expr.Resolve (bc);
@@ -58,12 +68,20 @@ public override bool Resolve (BlockContext bc)
return false;
machine_initializer = bc.CurrentAnonymousMethod as T;
-
- if (!bc.CurrentBranching.CurrentUsageVector.IsUnreachable)
- unwind_protect = bc.CurrentBranching.AddResumePoint (this, this, out resume_pc);
-
+ inside_try_block = bc.CurrentTryBlock;
return true;
}
+
+ public void RegisterResumePoint ()
+ {
+ if (inside_try_block == null) {
+ resume_pc = machine_initializer.AddResumePoint (this);
+ } else {
+ resume_pc = inside_try_block.AddResumePoint (this, resume_pc, machine_initializer);
+ unwind_protect = true;
+ inside_try_block = null;
+ }
+ }
}
public class Yield : YieldStatement<Iterator>
@@ -73,14 +91,19 @@ public Yield (Expression expr, Location loc)
{
}
- public static bool CheckContext (ResolveContext ec, Location loc)
+ public static bool CheckContext (BlockContext bc, Location loc)
{
- if (!ec.CurrentAnonymousMethod.IsIterator) {
- ec.Report.Error (1621, loc,
+ if (!bc.CurrentAnonymousMethod.IsIterator) {
+ bc.Report.Error (1621, loc,
"The yield statement cannot be used inside anonymous method blocks");
return false;
}
+ if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
+ bc.Report.Error (1625, loc, "Cannot yield in the body of a finally clause");
+ return false;
+ }
+
return true;
}
@@ -89,6 +112,14 @@ public override bool Resolve (BlockContext bc)
if (!CheckContext (bc, loc))
return false;
+ if (bc.HasAny (ResolveContext.Options.TryWithCatchScope)) {
+ bc.Report.Error (1626, loc, "Cannot yield a value in the body of a try block with a catch clause");
+ }
+
+ if (bc.HasSet (ResolveContext.Options.CatchScope)) {
+ bc.Report.Error (1631, loc, "Cannot yield a value in the body of a catch clause");
+ }
+
if (!base.Resolve (bc))
return false;
@@ -117,9 +148,10 @@ public YieldBreak (Location l)
loc = l;
}
- public override void Error_FinallyClause (Report Report)
- {
- Report.Error (1625, loc, "Cannot yield in the body of a finally clause");
+ protected override bool IsLocalExit {
+ get {
+ return false;
+ }
}
protected override void CloneTo (CloneContext clonectx, Statement target)
@@ -127,16 +159,27 @@ protected override void CloneTo (CloneContext clonectx, Statement target)
throw new NotSupportedException ();
}
- protected override bool DoResolve (BlockContext ec)
+ protected override bool DoResolve (BlockContext bc)
{
- iterator = ec.CurrentIterator;
- return Yield.CheckContext (ec, loc);
+ iterator = bc.CurrentIterator;
+ return Yield.CheckContext (bc, loc);
}
protected override void DoEmit (EmitContext ec)
{
iterator.EmitYieldBreak (ec, unwind_protect);
}
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return true;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+ return Reachability.CreateUnreachable ();
+ }
public override object Accept (StructuralVisitor visitor)
{
@@ -265,7 +308,6 @@ public override bool Resolve (BlockContext ec)
if (new_storey != null)
new_storey = Convert.ImplicitConversionRequired (ec, new_storey, host_method.MemberType, loc);
- ec.CurrentBranching.CurrentUsageVector.Goto ();
return true;
}
@@ -293,10 +335,21 @@ protected override void DoEmit (EmitContext ec)
new_storey.Emit (ec);
ec.Emit (OpCodes.Ret);
}
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+ return Reachability.CreateUnreachable ();
+ }
}
GetEnumeratorMethod (IteratorStorey host, FullNamedExpression returnType, MemberName name)
- : base (host, null, returnType, Modifiers.DEBUGGER_HIDDEN, name)
+ : base (host, null, returnType, Modifiers.DEBUGGER_HIDDEN, name, ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis)
{
}
@@ -310,7 +363,6 @@ public static GetEnumeratorMethod Create (IteratorStorey host, FullNamedExpressi
var m = new GetEnumeratorMethod (host, returnType, name);
var stmt = statement ?? new GetEnumeratorStatement (host, m);
m.block.AddStatement (stmt);
- m.block.IsCompilerGenerated = true;
return m;
}
}
@@ -342,16 +394,20 @@ protected override void DoEmit (EmitContext ec)
ec.CurrentAnonymousMethod = iterator;
iterator.EmitDispose (ec);
}
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ throw new NotImplementedException ();
+ }
}
public DisposeMethod (IteratorStorey host)
: base (host, null, new TypeExpression (host.Compiler.BuiltinTypes.Void, host.Location), Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN,
- new MemberName ("Dispose", host.Location))
+ new MemberName ("Dispose", host.Location), ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis)
{
host.Members.Add (this);
Block.AddStatement (new DisposeMethodStatement (host.Iterator));
- Block.IsCompilerGenerated = true;
}
}
@@ -546,9 +602,8 @@ void Define_Current (bool is_generic)
var name = new MemberName ("Current", null, explicit_iface, Location);
- ToplevelBlock get_block = new ToplevelBlock (Compiler, Location) {
- IsCompilerGenerated = true
- };
+ ToplevelBlock get_block = new ToplevelBlock (Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location,
+ Block.Flags.CompilerGenerated | Block.Flags.NoFlowAnalysis);
get_block.AddStatement (new Return (new DynamicFieldExpr (CurrentField, Location), Location));
Property current = new Property (this, type, Modifiers.DEBUGGER_HIDDEN | Modifiers.COMPILER_GENERATED, name, null);
@@ -567,9 +622,8 @@ void Define_Reset ()
ParametersCompiled.EmptyReadOnlyParameters, null);
Members.Add (reset);
- reset.Block = new ToplevelBlock (Compiler, Location) {
- IsCompilerGenerated = true
- };
+ reset.Block = new ToplevelBlock (Compiler, reset.ParameterInfo, Location,
+ Block.Flags.CompilerGenerated | Block.Flags.NoFlowAnalysis);
TypeSpec ex_type = Module.PredefinedTypes.NotSupportedException.Resolve ();
if (ex_type == null)
@@ -590,12 +644,13 @@ public class StateMachineMethod : Method
{
readonly StateMachineInitializer expr;
- public StateMachineMethod (StateMachine host, StateMachineInitializer expr, FullNamedExpression returnType, Modifiers mod, MemberName name)
+ public StateMachineMethod (StateMachine host, StateMachineInitializer expr, FullNamedExpression returnType,
+ Modifiers mod, MemberName name, ToplevelBlock.Flags blockFlags)
: base (host, returnType, mod | Modifiers.COMPILER_GENERATED,
name, ParametersCompiled.EmptyReadOnlyParameters, null)
{
this.expr = expr;
- Block = new ToplevelBlock (host.Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location.Null);
+ Block = new ToplevelBlock (host.Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location.Null, blockFlags);
}
public override EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod)
@@ -642,6 +697,21 @@ public override void Emit (EmitContext ec)
// Don't create sequence point
DoEmit (ec);
}
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return state_machine.ReturnType.Kind != MemberKind.Void;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+
+ if (state_machine.ReturnType.Kind != MemberKind.Void)
+ rc = Reachability.CreateUnreachable ();
+
+ return rc;
+ }
}
public readonly TypeDefinition Host;
@@ -706,30 +776,27 @@ public override Expression CreateExpressionTree (ResolveContext ec)
throw new NotSupportedException ("ET");
}
- protected virtual BlockContext CreateBlockContext (ResolveContext rc)
+ protected virtual BlockContext CreateBlockContext (BlockContext bc)
{
- var ctx = new BlockContext (rc, block, ((BlockContext) rc).ReturnType);
+ var ctx = new BlockContext (bc, block, bc.ReturnType);
ctx.CurrentAnonymousMethod = this;
+
+ ctx.AssignmentInfoOffset = bc.AssignmentInfoOffset;
+ ctx.EnclosingLoop = bc.EnclosingLoop;
+ ctx.EnclosingLoopOrSwitch = bc.EnclosingLoopOrSwitch;
+ ctx.Switch = bc.Switch;
+
return ctx;
}
- protected override Expression DoResolve (ResolveContext ec)
+ protected override Expression DoResolve (ResolveContext rc)
{
- var ctx = CreateBlockContext (ec);
+ var ctx = CreateBlockContext ((BlockContext) rc);
Block.Resolve (ctx);
- //
- // Explicit return is required for Task<T> state machine
- //
- var task_storey = storey as AsyncTaskStorey;
- if (task_storey == null || (task_storey.ReturnType != null && !task_storey.ReturnType.IsGenericTask))
- ctx.CurrentBranching.CurrentUsageVector.Goto ();
-
- ctx.EndFlowBranching ();
-
- if (!ec.IsInProbingMode) {
- var move_next = new StateMachineMethod (storey, this, new TypeExpression (ReturnType, loc), Modifiers.PUBLIC, new MemberName ("MoveNext", loc));
+ if (!rc.IsInProbingMode) {
+ var move_next = new StateMachineMethod (storey, this, new TypeExpression (ReturnType, loc), Modifiers.PUBLIC, new MemberName ("MoveNext", loc), 0);
move_next.Block.AddStatement (new MoveNextBodyStatement (this));
storey.AddEntryMethod (move_next);
}
@@ -936,6 +1003,11 @@ protected override void CloneTo (CloneContext clonectx, Statement target)
throw new NotSupportedException ();
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ throw new NotSupportedException ();
+ }
+
protected override void DoEmit (EmitContext ec)
{
//
@@ -985,8 +1057,8 @@ public Method CreateFinallyHost (TryFinallyBlock block)
Modifiers.COMPILER_GENERATED, new MemberName (CompilerGeneratedContainer.MakeName (null, null, "Finally", finally_hosts_counter++), loc),
ParametersCompiled.EmptyReadOnlyParameters, null);
- method.Block = new ToplevelBlock (method.Compiler, method.ParameterInfo, loc);
- method.Block.IsCompilerGenerated = true;
+ method.Block = new ToplevelBlock (method.Compiler, method.ParameterInfo, loc,
+ ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis);
method.Block.AddStatement (new TryFinallyBlockProxyStatement (this, block));
// Cannot it add to storey because it'd be emitted before nested
@@ -1097,13 +1169,6 @@ public override void InjectYield (EmitContext ec, Expression expr, int resume_pc
ec.MarkLabel (resume_point);
}
- protected override BlockContext CreateBlockContext (ResolveContext rc)
- {
- var bc = base.CreateBlockContext (rc);
- bc.StartFlowBranching (this, rc.CurrentBranching);
- return bc;
- }
-
public static void CreateIterator (IMethodData method, TypeDefinition parent, Modifiers modifiers)
{
bool is_enumerable;
Modified: mcs/mcs/linq.cs
===================================================================
@@ -811,9 +811,8 @@ public static void Reset ()
}
public QueryBlock (Block parent, Location start)
- : base (parent, ParametersCompiled.EmptyReadOnlyParameters, start)
+ : base (parent, ParametersCompiled.EmptyReadOnlyParameters, start, Flags.CompilerGenerated)
{
- flags |= Flags.CompilerGenerated;
}
public void AddRangeVariable (RangeVariable variable)
Modified: mcs/mcs/method.cs
===================================================================
@@ -1498,6 +1498,12 @@ public override void EmitStatement (EmitContext ec)
{
Emit (ec);
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (argument_list != null)
+ argument_list.FlowAnalysis (fc);
+ }
}
public class ConstructorBaseInitializer : ConstructorInitializer {
@@ -1742,7 +1748,7 @@ public override void Emit ()
}
}
- if (block.Resolve (null, bc, this)) {
+ if (block.Resolve (bc, this)) {
debug_builder = Parent.CreateMethodSymbolEntry ();
EmitContext ec = new EmitContext (this, ConstructorBuilder.GetILGenerator (), bc.ReturnType, debug_builder);
ec.With (EmitContext.Options.ConstructorScope, true);
@@ -2131,7 +2137,7 @@ public void Emit (TypeDefinition parent)
ToplevelBlock block = method.Block;
if (block != null) {
BlockContext bc = new BlockContext (method, block, method.ReturnType);
- if (block.Resolve (null, bc, method)) {
+ if (block.Resolve (bc, method)) {
debug_builder = member.Parent.CreateMethodSymbolEntry ();
EmitContext ec = method.CreateEmitContext (MethodBuilder.GetILGenerator (), debug_builder);
Modified: mcs/mcs/nullable.cs
===================================================================
@@ -201,6 +201,11 @@ public override bool Equals (object obj)
return uw != null && expr.Equals (uw.expr);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ }
+
public Expression Original {
get {
return expr;
@@ -480,6 +485,11 @@ public override void Emit (EmitContext ec)
ec.MarkLabel (end_label);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ }
+
public void AddressOf (EmitContext ec, AddressOp mode)
{
unwrap.AddressOf (ec, mode);
@@ -1008,6 +1018,11 @@ void EmitEquality (EmitContext ec)
ec.MarkLabel (end_label);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ Binary.FlowAnalysis (fc);
+ }
+
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
return Binary.MakeExpression (ctx, Left, Right);
@@ -1221,6 +1236,12 @@ public override void Emit (EmitContext ec)
ec.MarkLabel (end_label);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ left.FlowAnalysis (fc);
+ right.FlowAnalysis (fc);
+ }
+
protected override void CloneTo (CloneContext clonectx, Expression t)
{
NullCoalescingOperator target = (NullCoalescingOperator) t;
Modified: mcs/mcs/statement.cs
===================================================================
@@ -24,6 +24,13 @@ namespace Mono.CSharp {
public abstract class Statement {
public Location loc;
+ protected bool reachable;
+
+ public bool IsUnreachable {
+ get {
+ return !reachable;
+ }
+ }
/// <summary>
/// Resolves the statement, true means that all sub-statements
@@ -35,44 +42,6 @@ public virtual bool Resolve (BlockContext bc)
}
/// <summary>
- /// We already know that the statement is unreachable, but we still
- /// need to resolve it to catch errors.
- /// </summary>
- public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
- {
- //
- // This conflicts with csc's way of doing this, but IMHO it's
- // the right thing to do.
- //
- // If something is unreachable, we still check whether it's
- // correct. This means that you cannot use unassigned variables
- // in unreachable code, for instance.
- //
-
- bool unreachable = false;
- if (warn && !ec.UnreachableReported) {
-
- // TODO: This is wrong, need to form of flow-analysis branch specific flag
- // or multiple unrelared unreachable code won't be reported
- // if (false) { // ok } if (false) { // not reported }
- ec.UnreachableReported = true;
- unreachable = true;
- ec.Report.Warning (162, 2, loc, "Unreachable code detected");
- }
-
- ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
- ec.CurrentBranching.CurrentUsageVector.Goto ();
- bool ok = Resolve (ec);
- ec.KillFlowBranching ();
-
- if (unreachable) {
- ec.UnreachableReported = false;
- }
-
- return ok;
- }
-
- /// <summary>
/// Return value indicates whether all code paths emitted return.
/// </summary>
protected abstract void DoEmit (EmitContext ec);
@@ -110,6 +79,64 @@ public virtual object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
+
+ //
+ // Return value indicates whether statement has unreachable end
+ //
+ protected abstract bool DoFlowAnalysis (FlowAnalysisContext fc);
+
+ public bool FlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (reachable) {
+ fc.UnreachableReported = false;
+ return DoFlowAnalysis (fc);
+ }
+
+ //
+ // Special handling cases
+ //
+ if (this is Block || this is SwitchLabel) {
+ return DoFlowAnalysis (fc);
+ }
+
+ if (this is EmptyStatement)
+ return true;
+
+ if (fc.UnreachableReported)
+ return true;
+
+ fc.Report.Warning (162, 2, loc, "Unreachable code detected");
+ fc.UnreachableReported = true;
+ return true;
+ }
+
+ public virtual Reachability MarkReachable (Reachability rc)
+ {
+ if (!rc.IsUnreachable)
+ reachable = true;
+
+ return rc;
+ }
+
+ protected void CheckExitBoundaries (BlockContext bc, Block scope)
+ {
+ if (bc.CurrentBlock.ParametersBlock.Original != scope.ParametersBlock.Original) {
+ bc.Report.Error (1632, loc, "Control cannot leave the body of an anonymous method");
+ return;
+ }
+
+ for (var b = bc.CurrentBlock; b != null && b != scope; b = b.Parent) {
+ if (b.IsFinallyBlock) {
+ Error_FinallyClauseExit (bc);
+ break;
+ }
+ }
+ }
+
+ protected void Error_FinallyClauseExit (BlockContext bc)
+ {
+ bc.Report.Error (157, loc, "Control cannot leave the body of a finally clause");
+ }
}
public sealed class EmptyStatement : Statement
@@ -118,13 +145,8 @@ public EmptyStatement (Location loc)
{
this.loc = loc;
}
-
- public override bool Resolve (BlockContext ec)
- {
- return true;
- }
- public override bool ResolveUnreachable (BlockContext ec, bool warn)
+ public override bool Resolve (BlockContext ec)
{
return true;
}
@@ -138,6 +160,11 @@ protected override void DoEmit (EmitContext ec)
throw new NotSupportedException ();
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return false;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement target)
{
// nothing needed.
@@ -148,13 +175,13 @@ public override object Accept (StructuralVisitor visitor)
return visitor.Visit (this);
}
}
-
+
public class If : Statement {
Expression expr;
public Statement TrueStatement;
public Statement FalseStatement;
- bool is_true_ret;
+ bool true_returns, false_returns;
public If (Expression bool_expr, Statement true_statement, Location l)
: this (bool_expr, true_statement, null, l)
@@ -180,51 +207,13 @@ public If (Expression bool_expr, Statement true_statement, Location l)
public override bool Resolve (BlockContext ec)
{
- bool ok = true;
-
expr = expr.Resolve (ec);
- if (expr == null) {
- ok = false;
- } else {
- //
- // Dead code elimination
- //
- if (expr is Constant) {
- bool take = !((Constant) expr).IsDefaultValue;
- if (take) {
- if (!TrueStatement.Resolve (ec))
- return false;
-
- if ((FalseStatement != null) &&
- !FalseStatement.ResolveUnreachable (ec, true))
- return false;
- FalseStatement = null;
- } else {
- if (!TrueStatement.ResolveUnreachable (ec, true))
- return false;
- TrueStatement = null;
-
- if ((FalseStatement != null) &&
- !FalseStatement.Resolve (ec))
- return false;
- }
-
- return true;
- }
- }
-
- ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
-
- ok &= TrueStatement.Resolve (ec);
- is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
+ var ok = TrueStatement.Resolve (ec);
- ec.CurrentBranching.CreateSibling ();
-
- if (FalseStatement != null)
+ if (FalseStatement != null) {
ok &= FalseStatement.Resolve (ec);
-
- ec.EndFlowBranching ();
+ }
return ok;
}
@@ -258,7 +247,7 @@ protected override void DoEmit (EmitContext ec)
bool branch_emitted = false;
end = ec.DefineLabel ();
- if (!is_true_ret){
+ if (!true_returns){
ec.Emit (OpCodes.Br, end);
branch_emitted = true;
}
@@ -273,6 +262,68 @@ protected override void DoEmit (EmitContext ec)
}
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+
+ var da = fc.BranchDefiniteAssignment ();
+
+ var res = TrueStatement.FlowAnalysis (fc);
+
+ if (FalseStatement == null) {
+ fc.DefiniteAssignment = da;
+ return false;
+ }
+
+ var da_true = fc.DefiniteAssignment;
+ if (true_returns) {
+ fc.DefiniteAssignment = da;
+ return FalseStatement.FlowAnalysis (fc);
+ }
+
+ fc.DefiniteAssignment = new DefiniteAssignmentBitSet (da);
+ res &= FalseStatement.FlowAnalysis (fc);
+
+ if (false_returns)
+ fc.DefiniteAssignment = da_true;
+ else
+ fc.DefiniteAssignment &= da_true;
+
+ return res;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ if (rc.IsUnreachable)
+ return rc;
+
+ base.MarkReachable (rc);
+
+ var c = expr as Constant;
+ if (c != null) {
+ bool take = !c.IsDefaultValue;
+ if (take) {
+ rc = TrueStatement.MarkReachable (rc);
+ } else {
+ if (FalseStatement != null)
+ rc = FalseStatement.MarkReachable (rc);
+ }
+
+ return rc;
+ }
+
+ var true_rc = TrueStatement.MarkReachable (rc);
+ true_returns = true_rc.IsUnreachable;
+
+ if (FalseStatement == null)
+ return rc;
+
+ var false_rc = FalseStatement.MarkReachable (rc);
+ false_returns = false_rc.IsUnreachable;
+
+ return true_rc & false_rc;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
If target = (If) t;
@@ -289,14 +340,15 @@ public override object Accept (StructuralVisitor visitor)
}
}
- public class Do : Statement {
+ public class Do : LoopStatement
+ {
public Expression expr;
- public Statement EmbeddedStatement;
+ bool iterator_reachable, end_reachable;
public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
+ : base (statement)
{
expr = bool_expr;
- EmbeddedStatement = statement;
loc = doLocation;
WhileLocation = whileLocation;
}
@@ -305,32 +357,11 @@ public Do (Statement statement, BooleanExpression bool_expr, Location doLocation
get; private set;
}
- public override bool Resolve (BlockContext ec)
+ public override bool Resolve (BlockContext bc)
{
- bool ok = true;
-
- ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
-
- bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
-
- ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
- if (!EmbeddedStatement.Resolve (ec))
- ok = false;
- ec.EndFlowBranching ();
-
- if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
- ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
+ var ok = base.Resolve (bc);
- expr = expr.Resolve (ec);
- if (expr == null)
- ok = false;
- else if (expr is Constant){
- bool infinite = !((Constant) expr).IsDefaultValue;
- if (infinite)
- ec.CurrentBranching.CurrentUsageVector.Goto ();
- }
-
- ec.EndFlowBranching ();
+ expr = expr.Resolve (bc);
return ok;
}
@@ -345,7 +376,7 @@ protected override void DoEmit (EmitContext ec)
ec.LoopEnd = ec.DefineLabel ();
ec.MarkLabel (loop);
- EmbeddedStatement.Emit (ec);
+ Statement.Emit (ec);
ec.MarkLabel (ec.LoopBegin);
// Mark start of while condition
@@ -370,11 +401,53 @@ protected override void DoEmit (EmitContext ec)
ec.LoopEnd = old_end;
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ var dat = fc.BranchDefiniteAssignment ();
+
+ var res = Statement.FlowAnalysis (fc);
+
+ expr.FlowAnalysis (fc);
+
+ fc.DefiniteAssignment = dat | fc.DefiniteAssignment;
+
+ if (res && !iterator_reachable)
+ return !end_reachable;
+
+ if (!end_reachable) {
+ var c = expr as Constant;
+ if (c != null && !c.IsDefaultValue)
+ return true;
+ }
+
+ return false;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+
+ var body_rc = Statement.MarkReachable (rc);
+
+ if (body_rc.IsUnreachable && !iterator_reachable) {
+ expr = new UnreachableExpression (expr);
+ return end_reachable ? rc : Reachability.CreateUnreachable ();
+ }
+
+ if (!end_reachable) {
+ var c = expr as Constant;
+ if (c != null && !c.IsDefaultValue)
+ return Reachability.CreateUnreachable ();
+ }
+
+ return rc;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Do target = (Do) t;
- target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
+ target.Statement = Statement.Clone (clonectx);
target.expr = expr.Clone (clonectx);
}
@@ -382,58 +455,46 @@ public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
+
+ public override void SetEndReachable ()
+ {
+ end_reachable = true;
+ }
+
+ public override void SetIteratorReachable ()
+ {
+ iterator_reachable = true;
+ }
}
- public class While : Statement {
+ public class While : LoopStatement
+ {
public Expression expr;
- public Statement Statement;
- bool infinite, empty;
+ bool empty, infinite, end_reachable;
+ List<DefiniteAssignmentBitSet> end_reachable_das;
public While (BooleanExpression bool_expr, Statement statement, Location l)
+ : base (statement)
{
this.expr = bool_expr;
- Statement = statement;
loc = l;
}
- public override bool Resolve (BlockContext ec)
+ public override bool Resolve (BlockContext bc)
{
bool ok = true;
- expr = expr.Resolve (ec);
+ expr = expr.Resolve (bc);
if (expr == null)
ok = false;
- //
- // Inform whether we are infinite or not
- //
- if (expr is Constant){
- bool value = !((Constant) expr).IsDefaultValue;
-
- if (value == false){
- if (!Statement.ResolveUnreachable (ec, true))
- return false;
- empty = true;
- return true;
- }
-
- infinite = true;
+ var c = expr as Constant;
+ if (c != null) {
+ empty = c.IsDefaultValue;
+ infinite = !empty;
}
- ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
- if (!infinite)
- ec.CurrentBranching.CreateSibling ();
-
- ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
- if (!Statement.Resolve (ec))
- ok = false;
- ec.EndFlowBranching ();
-
- // There's no direct control flow from the end of the embedded statement to the end of the loop
- ec.CurrentBranching.CurrentUsageVector.Goto ();
-
- ec.EndFlowBranching ();
-
+ ok &= base.Resolve (bc);
return ok;
}
@@ -489,6 +550,58 @@ protected override void DoEmit (EmitContext ec)
ec.LoopEnd = old_end;
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+
+ DefiniteAssignmentBitSet das;
+
+ das = fc.BranchDefiniteAssignment ();
+
+ Statement.FlowAnalysis (fc);
+
+ //
+ // Special case infinite while with breaks
+ //
+ if (end_reachable_das != null) {
+ das = DefiniteAssignmentBitSet.And (end_reachable_das);
+ end_reachable_das = null;
+ }
+
+ fc.DefiniteAssignment = das;
+
+ if (infinite && !end_reachable)
+ return true;
+
+ return false;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ if (rc.IsUnreachable)
+ return rc;
+
+ base.MarkReachable (rc);
+
+ //
+ // Special case unreachable while body
+ //
+ if (empty) {
+ Statement.MarkReachable (Reachability.CreateUnreachable ());
+ return rc;
+ }
+
+ Statement.MarkReachable (rc);
+
+ //
+ // When infinite while end is unreachable via break anything what follows is unreachable too
+ //
+ if (infinite && !end_reachable)
+ return Reachability.CreateUnreachable ();
+
+ return rc;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
While target = (While) t;
@@ -501,13 +614,31 @@ public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
+
+ public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
+ {
+ if (!infinite)
+ return;
+
+ if (end_reachable_das == null)
+ end_reachable_das = new List<DefiniteAssignmentBitSet> ();
+
+ end_reachable_das.Add (fc.DefiniteAssignment);
+ }
+
+ public override void SetEndReachable ()
+ {
+ end_reachable = true;
+ }
}
- public class For : Statement
+ public class For : LoopStatement
{
- bool infinite, empty;
+ bool infinite, empty, iterator_reachable, end_reachable;
+ List<DefiniteAssignmentBitSet> end_reachability;
public For (Location l)
+ : base (null)
{
loc = l;
}
@@ -524,68 +655,68 @@ public For (Location l)
get; set;
}
- public Statement Statement {
- get; set;
- }
-
- public override bool Resolve (BlockContext ec)
+ public override bool Resolve (BlockContext bc)
{
- bool ok = true;
-
- if (Initializer != null) {
- if (!Initializer.Resolve (ec))
- ok = false;
- }
+ Initializer.Resolve (bc);
if (Condition != null) {
- Condition = Condition.Resolve (ec);
- if (Condition == null)
- ok = false;
- else if (Condition is Constant) {
- bool value = !((Constant) Condition).IsDefaultValue;
-
- if (value == false){
- if (!Statement.ResolveUnreachable (ec, true))
- return false;
- if ((Iterator != null) &&
- !Iterator.ResolveUnreachable (ec, false))
- return false;
+ Condition = Condition.Resolve (bc);
+ var condition_constant = Condition as Constant;
+ if (condition_constant != null) {
+ if (condition_constant.IsDefaultValue) {
empty = true;
- return true;
+ } else {
+ infinite = true;
}
-
- infinite = true;
}
- } else
+ } else {
infinite = true;
+ }
- ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
- if (!infinite)
- ec.CurrentBranching.CreateSibling ();
+ base.Resolve (bc);
- bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
+ Iterator.Resolve (bc);
- ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
- if (!Statement.Resolve (ec))
- ok = false;
- ec.EndFlowBranching ();
+ return true;
+ }
- if (Iterator != null){
- if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
- if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
- ok = false;
- } else {
- if (!Iterator.Resolve (ec))
- ok = false;
- }
- }
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ Initializer.FlowAnalysis (fc);
- // There's no direct control flow from the end of the embedded statement to the end of the loop
- ec.CurrentBranching.CurrentUsageVector.Goto ();
+ if (Condition != null)
+ Condition.FlowAnalysis (fc);
- ec.EndFlowBranching ();
+ var das = fc.BranchDefiniteAssignment ();
- return ok;
+ Statement.FlowAnalysis (fc);
+
+ Iterator.FlowAnalysis (fc);
+
+ fc.DefiniteAssignment = das;
+
+ return infinite && !end_reachable;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+
+ Initializer.MarkReachable (rc);
+
+ var body_rc = Statement.MarkReachable (rc);
+ if (!body_rc.IsUnreachable || iterator_reachable) {
+ Iterator.MarkReachable (rc);
+ }
+
+ //
+ // When infinite for end is unreachable via break anything what follows is unreachable too
+ //
+ if (infinite && !end_reachable) {
+ return Reachability.CreateUnreachable ();
+ }
+
+ return rc;
}
protected override void DoEmit (EmitContext ec)
@@ -658,6 +789,64 @@ public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
+
+ public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
+ {
+ if (!infinite)
+ return;
+
+ if (end_reachability == null)
+ end_reachability = new List<DefiniteAssignmentBitSet> ();
+
+ end_reachability.Add (fc.DefiniteAssignment);
+ }
+
+ public override void SetEndReachable ()
+ {
+ end_reachable = true;
+ }
+
+ public override void SetIteratorReachable ()
+ {
+ iterator_reachable = true;
+ }
+ }
+
+ public abstract class LoopStatement : Statement
+ {
+ protected LoopStatement (Statement statement)
+ {
+ Statement = statement;
+ }
+
+ public Statement Statement { get; set; }
+
+ public override bool Resolve (BlockContext bc)
+ {
+ var prev_loop = bc.EnclosingLoop;
+ var prev_los = bc.EnclosingLoopOrSwitch;
+ bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
+ Statement.Resolve (bc);
+ bc.EnclosingLoopOrSwitch = prev_los;
+ bc.EnclosingLoop = prev_loop;
+
+ return true;
+ }
+
+ //
+ // Needed by possibly infinite loops statements (for, while) and switch statment
+ //
+ public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
+ {
+ }
+
+ public virtual void SetEndReachable ()
+ {
+ }
+
+ public virtual void SetIteratorReachable ()
+ {
+ }
}
public class StatementExpression : Statement
@@ -690,7 +879,20 @@ protected override void CloneTo (CloneContext clonectx, Statement t)
protected override void DoEmit (EmitContext ec)
{
- expr.EmitStatement (ec);
+ expr.EmitStatement (ec);
+ }
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ return false;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+ expr.MarkReachable (rc);
+ return rc;
}
public override bool Resolve (BlockContext ec)
@@ -732,6 +934,11 @@ protected override void DoEmit (EmitContext ec)
throw new NotSupportedException ();
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return false;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement target)
{
var t = (StatementErrorExpression) target;
@@ -784,6 +991,25 @@ protected override void DoEmit (EmitContext ec)
s.Emit (ec);
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ foreach (var s in statements)
+ s.FlowAnalysis (fc);
+
+ return false;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+
+ Reachability res = rc;
+ foreach (var s in statements)
+ res = s.MarkReachable (rc);
+
+ return res;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement target)
{
StatementList t = (StatementList) target;
@@ -799,23 +1025,54 @@ public override object Accept (StructuralVisitor visitor)
}
}
- // A 'return' or a 'yield break'
+ //
+ // For statements which require special handling when inside try or catch block
+ //
public abstract class ExitStatement : Statement
{
protected bool unwind_protect;
- protected abstract bool DoResolve (BlockContext ec);
- public virtual void Error_FinallyClause (Report Report)
+ protected abstract bool DoResolve (BlockContext bc);
+ protected abstract bool IsLocalExit { get; }
+
+ public override bool Resolve (BlockContext bc)
{
- Report.Error (157, loc, "Control cannot leave the body of a finally clause");
+ var res = DoResolve (bc);
+
+ if (!IsLocalExit) {
+ //
+ // We are inside finally scope but is it the scope we are exiting
+ //
+ if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
+
+ for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
+ if (b.IsFinallyBlock) {
+ Error_FinallyClauseExit (bc);
+ break;
+ }
+
+ if (b is ParametersBlock)
+ break;
+ }
+ }
+ }
+
+ unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
+ return res;
}
- public sealed override bool Resolve (BlockContext ec)
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
{
- var res = DoResolve (ec);
- unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
- ec.CurrentBranching.CurrentUsageVector.Goto ();
- return res;
+ if (IsLocalExit)
+ return true;
+
+ if (fc.TryFinally != null) {
+ fc.TryFinally.RegisterOutParametersCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
+ } else {
+ fc.ParametersBlock.CheckOutParametersAssignment (fc);
+ }
+
+ return true;
}
}
@@ -843,12 +1100,20 @@ public Return (Expression expr, Location l)
}
}
+ protected override bool IsLocalExit {
+ get {
+ return false;
+ }
+ }
+
#endregion
protected override bool DoResolve (BlockContext ec)
{
+ var block_return_type = ec.ReturnType;
+
if (expr == null) {
- if (ec.ReturnType.Kind == MemberKind.Void)
+ if (block_return_type.Kind == MemberKind.Void)
return true;
//
@@ -878,7 +1143,6 @@ protected override bool DoResolve (BlockContext ec)
}
expr = expr.Resolve (ec);
- TypeSpec block_return_type = ec.ReturnType;
AnonymousExpression am = ec.CurrentAnonymousMethod;
if (am == null) {
@@ -1007,7 +1271,7 @@ protected override void DoEmit (EmitContext ec)
ec.EmitEpilogue ();
}
- ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
+ ec.Emit (OpCodes.Leave, async_body.BodyEnd);
return;
}
@@ -1026,12 +1290,27 @@ protected override void DoEmit (EmitContext ec)
}
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (expr != null)
+ expr.FlowAnalysis (fc);
+
+ base.DoFlowAnalysis (fc);
+ return true;
+ }
+
void Error_ReturnFromIterator (ResolveContext rc)
{
rc.Report.Error (1622, loc,
"Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
}
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+ return Reachability.CreateUnreachable ();
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Return target = (Return) t;
@@ -1046,18 +1325,12 @@ public override object Accept (StructuralVisitor visitor)
}
}
- public class Goto : Statement {
+ public class Goto : ExitStatement
+ {
string target;
LabeledStatement label;
- bool unwind_protect;
+ TryFinally try_finally;
- public override bool Resolve (BlockContext ec)
- {
- unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
- ec.CurrentBranching.CurrentUsageVector.Goto ();
- return true;
- }
-
public Goto (string label, Location l)
{
loc = l;
@@ -1068,10 +1341,67 @@ public Goto (string label, Location l)
get { return target; }
}
- public void SetResolvedTarget (LabeledStatement label)
+ protected override bool IsLocalExit {
+ get {
+ return true;
+ }
+ }
+
+ protected override bool DoResolve (BlockContext bc)
+ {
+ label = bc.CurrentBlock.LookupLabel (target);
+ if (label == null) {
+ Error_UnknownLabel (bc, target, loc);
+ return false;
+ }
+
+ try_finally = bc.CurrentTryBlock as TryFinally;
+
+ CheckExitBoundaries (bc, label.Block);
+
+ return true;
+ }
+
+ public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
+ {
+ bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
+ label);
+ }
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
{
- this.label = label;
- label.AddReference ();
+ if (fc.LabelStack == null) {
+ fc.LabelStack = new List<LabeledStatement> ();
+ } else if (fc.LabelStack.Contains (label)) {
+ return true;
+ }
+
+ fc.LabelStack.Add (label);
+ label.Block.ScanGotoJump (label, fc);
+ fc.LabelStack.Remove (label);
+ return true;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ if (rc.IsUnreachable)
+ return rc;
+
+ base.MarkReachable (rc);
+
+ if (try_finally != null) {
+ if (try_finally.FinallyBlock.HasReachableClosingBrace) {
+ label.AddGotoReference (rc, false);
+ } else {
+ label.AddGotoReference (rc, true);
+ }
+
+ try_finally = null;
+ } else {
+ label.AddGotoReference (rc, false);
+ }
+
+ return Reachability.CreateUnreachable ();
}
protected override void CloneTo (CloneContext clonectx, Statement target)
@@ -1083,6 +1413,7 @@ protected override void DoEmit (EmitContext ec)
{
if (label == null)
throw new InternalErrorException ("goto emitted before target resolved");
+
Label l = label.LabelTarget (ec);
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
}
@@ -1097,10 +1428,9 @@ public class LabeledStatement : Statement {
string name;
bool defined;
bool referenced;
+ bool finalTarget;
Label label;
Block block;
-
- FlowBranching.UsageVector vectors;
public LabeledStatement (string name, Block block, Location l)
{
@@ -1129,51 +1459,66 @@ public Label LabelTarget (EmitContext ec)
get { return name; }
}
- public bool IsDefined {
- get { return defined; }
- }
-
- public bool HasBeenReferenced {
- get { return referenced; }
- }
-
- public FlowBranching.UsageVector JumpOrigins {
- get { return vectors; }
- }
-
- public void AddUsageVector (FlowBranching.UsageVector vector)
- {
- vector = vector.Clone ();
- vector.Next = vectors;
- vectors = vector;
- }
-
protected override void CloneTo (CloneContext clonectx, Statement target)
{
// nothing to clone
}
- public override bool Resolve (BlockContext ec)
+ public override bool Resolve (BlockContext bc)
{
- // this flow-branching will be terminated when the surrounding block ends
- ec.StartFlowBranching (this);
return true;
}
protected override void DoEmit (EmitContext ec)
{
- if (!HasBeenReferenced)
- ec.Report.Warning (164, 2, loc, "This label has not been referenced");
-
LabelTarget (ec);
ec.MarkLabel (label);
+
+ if (finalTarget)
+ ec.Emit (OpCodes.Br_S, label);
+ }
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (!referenced) {
+ fc.Report.Warning (164, 2, loc, "This label has not been referenced");
+ }
+
+ return false;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+
+ if (referenced)
+ rc = new Reachability ();
+
+ return rc;
}
- public void AddReference ()
+ public void AddGotoReference (Reachability rc, bool finalTarget)
{
+ if (referenced)
+ return;
+
referenced = true;
+
+ //
+ // 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) {
+ MarkReachable (rc);
+ this.finalTarget = true;
+ return;
+ }
+
+ block.ScanGotoJump (this);
}
-
+
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
@@ -1184,35 +1529,29 @@ public override object Accept (StructuralVisitor visitor)
/// <summary>
/// `goto default' statement
/// </summary>
- public class GotoDefault : Statement {
-
+ public class GotoDefault : SwitchGoto
+ {
public GotoDefault (Location l)
+ : base (l)
{
- loc = l;
- }
-
- protected override void CloneTo (CloneContext clonectx, Statement target)
- {
- // nothing to clone
}
- public override bool Resolve (BlockContext ec)
+ public override bool Resolve (BlockContext bc)
{
- ec.CurrentBranching.CurrentUsageVector.Goto ();
-
- if (ec.Switch == null) {
- ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
+ if (bc.Switch == null) {
+ Error_GotoCaseRequiresSwitchBlock (bc);
return false;
}
- ec.Switch.RegisterGotoCase (null, null);
+ bc.Switch.RegisterGotoCase (null, null);
+ base.Resolve (bc);
return true;
}
protected override void DoEmit (EmitContext ec)
{
- ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
+ ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
}
public override object Accept (StructuralVisitor visitor)
@@ -1224,32 +1563,31 @@ public override object Accept (StructuralVisitor visitor)
/// <summary>
/// `goto case' statement
/// </summary>
- public class GotoCase : Statement {
+ public class GotoCase : SwitchGoto
+ {
Expression expr;
public GotoCase (Expression e, Location l)
+ : base (l)
{
expr = e;
- loc = l;
}
public Expression Expr {
get {
- return this.expr;
+ return expr;
}
}
public SwitchLabel Label { get; set; }
-
+
public override bool Resolve (BlockContext ec)
{
- if (ec.Switch == null){
- ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
+ if (ec.Switch == null) {
+ Error_GotoCaseRequiresSwitchBlock (ec);
return false;
}
- ec.CurrentBranching.CurrentUsageVector.Goto ();
-
Constant c = expr.ResolveLabelConstant (ec);
if (c == null) {
return false;
@@ -1274,12 +1612,14 @@ public override bool Resolve (BlockContext ec)
}
ec.Switch.RegisterGotoCase (this, res);
+ base.Resolve (ec);
+
return true;
}
protected override void DoEmit (EmitContext ec)
{
- ec.Emit (OpCodes.Br, Label.GetILLabel (ec));
+ ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
}
protected override void CloneTo (CloneContext clonectx, Statement t)
@@ -1294,6 +1634,46 @@ public override object Accept (StructuralVisitor visitor)
return visitor.Visit (this);
}
}
+
+ public abstract class SwitchGoto : Statement
+ {
+ protected bool unwind_protect;
+
+ protected SwitchGoto (Location loc)
+ {
+ this.loc = loc;
+ }
+
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ // Nothing to clone
+ }
+
+ public override bool Resolve (BlockContext bc)
+ {
+ CheckExitBoundaries (bc, bc.Switch.Block);
+
+ unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
+
+ return true;
+ }
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return true;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+ return Reachability.CreateUnreachable ();
+ }
+
+ protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
+ {
+ bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
+ }
+ }
public class Throw : Statement {
Expression expr;
@@ -1313,12 +1693,22 @@ public Throw (Expression expr, Location l)
public override bool Resolve (BlockContext ec)
{
if (expr == null) {
- ec.CurrentBranching.CurrentUsageVector.Goto ();
- return ec.CurrentBranching.CheckRethrow (loc);
+ if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
+ ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
+ } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
+ for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
+ if (b.IsFinallyBlock) {
+ ec.Report.Error (724, loc,
+ "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
+ break;
+ }
+ }
+ }
+
+ return true;
}
expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
- ec.CurrentBranching.CurrentUsageVector.Goto ();
if (expr == null)
return false;
@@ -1343,6 +1733,20 @@ protected override void DoEmit (EmitContext ec)
}
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (expr != null)
+ expr.FlowAnalysis (fc);
+
+ return true;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+ return Reachability.CreateUnreachable ();
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Throw target = (Throw) t;
@@ -1357,20 +1761,16 @@ public override object Accept (StructuralVisitor visitor)
}
}
- public class Break : Statement {
-
+ public class Break : LocalExitStatement
+ {
public Break (Location l)
+ : base (l)
{
- loc = l;
}
-
- bool unwind_protect;
-
- public override bool Resolve (BlockContext ec)
+
+ public override object Accept (StructuralVisitor visitor)
{
- unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
- ec.CurrentBranching.CurrentUsageVector.Goto ();
- return true;
+ return visitor.Visit (this);
}
protected override void DoEmit (EmitContext ec)
@@ -1378,46 +1778,99 @@ protected override void DoEmit (EmitContext ec)
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
}
- protected override void CloneTo (CloneContext clonectx, Statement t)
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
{
- // nothing needed
+ enclosing_loop.AddEndDefiniteAssignment (fc);
+ return true;
}
-
- public override object Accept (StructuralVisitor visitor)
+
+ protected override bool DoResolve (BlockContext bc)
{
- return visitor.Visit (this);
+ enclosing_loop = bc.EnclosingLoopOrSwitch;
+ return base.DoResolve (bc);
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+
+ if (!rc.IsUnreachable)
+ enclosing_loop.SetEndReachable ();
+
+ return Reachability.CreateUnreachable ();
}
}
- public class Continue : Statement {
-
+ public class Continue : LocalExitStatement
+ {
public Continue (Location l)
+ : base (l)
{
- loc = l;
}
- bool unwind_protect;
-
- public override bool Resolve (BlockContext ec)
+ public override object Accept (StructuralVisitor visitor)
{
- unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
- ec.CurrentBranching.CurrentUsageVector.Goto ();
- return true;
+ return visitor.Visit (this);
}
+
protected override void DoEmit (EmitContext ec)
{
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
}
+ protected override bool DoResolve (BlockContext bc)
+ {
+ enclosing_loop = bc.EnclosingLoop;
+ return base.DoResolve (bc);
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+
+ if (!rc.IsUnreachable)
+ enclosing_loop.SetIteratorReachable ();
+
+ return Reachability.CreateUnreachable ();
+ }
+ }
+
+ public abstract class LocalExitStatement : ExitStatement
+ {
+ protected LoopStatement enclosing_loop;
+
+ protected LocalExitStatement (Location loc)
+ {
+ this.loc = loc;
+ }
+
+ protected override bool IsLocalExit {
+ get {
+ return true;
+ }
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
// nothing needed.
}
-
- public override object Accept (StructuralVisitor visitor)
+
+ protected override bool DoResolve (BlockContext bc)
{
- return visitor.Visit (this);
+ if (enclosing_loop == null) {
+ bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
+ return false;
+ }
+
+ var block = enclosing_loop.Statement as Block;
+
+ // Don't need to do extra checks for simple statements loops
+ if (block != null) {
+ CheckExitBoundaries (bc, block);
+ }
+
+ return true;
}
}
@@ -1679,6 +2132,30 @@ protected override void DoEmit (EmitContext ec)
}
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (Initializer != null)
+ Initializer.FlowAnalysis (fc);
+
+ if (declarators != null) {
+ foreach (var d in declarators) {
+ if (d.Initializer != null)
+ d.Initializer.FlowAnalysis (fc);
+ }
+ }
+
+ return false;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ var init = initializer as ExpressionStatement;
+ if (init != null)
+ init.MarkReachable (rc);
+
+ return base.MarkReachable (rc);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement target)
{
BlockVariable t = (BlockVariable) target;
@@ -1749,7 +2226,7 @@ public override object Accept (StructuralVisitor visitor)
//
// The information about a user-perceived local variable
//
- public class LocalVariable : INamedBlockVariable, ILocalVariable
+ public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
{
[Flags]
public enum Flags
@@ -1762,8 +2239,7 @@ public enum Flags
ForeachVariable = 1 << 5,
FixedVariable = 1 << 6,
UsingVariable = 1 << 7,
-// DefinitelyAssigned = 1 << 8,
- IsLocked = 1 << 9,
+ IsLocked = 1 << 8,
ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
}
@@ -2001,23 +2477,20 @@ public string GetReadOnlyContext ()
throw new InternalErrorException ("Variable is not readonly");
}
- public bool IsThisAssigned (BlockContext ec, Block block)
+ public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
{
if (VariableInfo == null)
throw new Exception ();
- if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
+ if (IsAssigned (fc))
return true;
- return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
+ return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
}
- public bool IsAssigned (BlockContext ec)
+ public bool IsAssigned (FlowAnalysisContext fc)
{
- if (VariableInfo == null)
- throw new Exception ();
-
- return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
+ return fc.IsDefinitelyAssigned (VariableInfo);
}
public void PrepareAssignmentAnalysis (BlockContext bc)
@@ -2069,7 +2542,7 @@ public class Block : Statement {
public enum Flags
{
Unchecked = 1,
- HasRet = 8,
+ ReachableEnd = 8,
Unsafe = 16,
HasCapturedVariable = 64,
HasCapturedThis = 1 << 7,
@@ -2079,7 +2552,10 @@ public enum Flags
Resolved = 1 << 11,
YieldBlock = 1 << 12,
AwaitBlock = 1 << 13,
- Iterator = 1 << 14
+ FinallyBlock = 1 << 14,
+ CatchBlock = 1 << 15,
+ Iterator = 1 << 20,
+ NoFlowAnalysis = 1 << 21
}
public Block Parent;
@@ -2137,15 +2613,6 @@ public Block (Block parent, Flags flags, Location start, Location end)
#region Properties
- public bool HasUnreachableClosingBrace {
- get {
- return (flags & Flags.HasRet) != 0;
- }
- set {
- flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
- }
- }
-
public Block Original {
get {
return original;
@@ -2160,6 +2627,19 @@ public Block (Block parent, Flags flags, Location start, Location end)
set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
}
+
+ public bool IsCatchBlock {
+ get {
+ return (flags & Flags.CatchBlock) != 0;
+ }
+ }
+
+ public bool IsFinallyBlock {
+ get {
+ return (flags & Flags.FinallyBlock) != 0;
+ }
+ }
+
public bool Unchecked {
get { return (flags & Flags.Unchecked) != 0; }
set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
@@ -2273,123 +2753,72 @@ public LabeledStatement LookupLabel (string name)
return ParametersBlock.TopBlock.GetLabel (name, this);
}
- public override bool Resolve (BlockContext ec)
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ if (rc.IsUnreachable)
+ return rc;
+
+ base.MarkReachable (rc);
+
+ if (scope_initializers != null) {
+ foreach (var si in scope_initializers)
+ si.MarkReachable (rc);
+ }
+
+ foreach (var s in statements) {
+ rc = s.MarkReachable (rc);
+ if (rc.IsUnreachable) {
+ if ((flags & Flags.ReachableEnd) != 0)
+ return new Reachability ();
+
+ return rc;
+ }
+ }
+
+ flags |= Flags.ReachableEnd;
+
+ return rc;
+ }
+
+ public override bool Resolve (BlockContext bc)
{
if ((flags & Flags.Resolved) != 0)
return true;
- Block prev_block = ec.CurrentBlock;
- bool ok = true;
- bool unreachable = ec.IsUnreachable;
- bool prev_unreachable = unreachable;
-
- ec.CurrentBlock = this;
- ec.StartFlowBranching (this);
+ Block prev_block = bc.CurrentBlock;
+ bc.CurrentBlock = this;
//
// Compiler generated scope statements
//
if (scope_initializers != null) {
for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
- scope_initializers[resolving_init_idx.Value].Resolve (ec);
+ scope_initializers[resolving_init_idx.Value].Resolve (bc);
}
resolving_init_idx = null;
}
- //
- // This flag is used to notate nested statements as unreachable from the beginning of this block.
- // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
- // from the beginning of the function. The outer Resolve() that detected the unreachability is
- // responsible for handling the situation.
- //
+ bool ok = true;
int statement_count = statements.Count;
for (int ix = 0; ix < statement_count; ix++){
Statement s = statements [ix];
- //
- // Warn if we detect unreachable code.
- //
- if (unreachable) {
- if (s is EmptyStatement)
- continue;
-
- if (!ec.UnreachableReported && !(s is LabeledStatement) && !(s is SwitchLabel)) {
- ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
- ec.UnreachableReported = true;
- }
- }
-
- //
- // Note that we're not using ResolveUnreachable() for unreachable
- // statements here. ResolveUnreachable() creates a temporary
- // flow branching and kills it afterwards. This leads to problems
- // if you have two unreachable statements where the first one
- // assigns a variable and the second one tries to access it.
- //
-
- if (!s.Resolve (ec)) {
+ if (!s.Resolve (bc)) {
ok = false;
- if (!ec.IsInProbingMode)
+ if (!bc.IsInProbingMode)
statements [ix] = new EmptyStatement (s.loc);
continue;
}
-
- if (unreachable && !(s is LabeledStatement) && !(s is SwitchLabel) && !(s is Block))
- statements [ix] = new EmptyStatement (s.loc);
-
- unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
- if (unreachable) {
- ec.IsUnreachable = true;
- } else if (ec.IsUnreachable)
- ec.IsUnreachable = false;
}
- if (unreachable != prev_unreachable) {
- ec.IsUnreachable = prev_unreachable;
- ec.UnreachableReported = false;
- }
-
- while (ec.CurrentBranching is FlowBranchingLabeled)
- ec.EndFlowBranching ();
-
- bool flow_unreachable = ec.EndFlowBranching ();
-
- ec.CurrentBlock = prev_block;
-
- if (flow_unreachable)
- flags |= Flags.HasRet;
-
- // If we're a non-static `struct' constructor which doesn't have an
- // initializer, then we must initialize all of the struct's fields.
- if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
- ok = false;
+ bc.CurrentBlock = prev_block;
flags |= Flags.Resolved;
return ok;
}
- public override bool ResolveUnreachable (BlockContext ec, bool warn)
- {
- bool unreachable = false;
- if (warn && !ec.UnreachableReported) {
- ec.UnreachableReported = true;
- unreachable = true;
- ec.Report.Warning (162, 2, loc, "Unreachable code detected");
- }
-
- var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
- fb.CurrentUsageVector.IsUnreachable = true;
- bool ok = Resolve (ec);
- ec.KillFlowBranching ();
-
- if (unreachable)
- ec.UnreachableReported = false;
-
- return ok;
- }
-
protected override void DoEmit (EmitContext ec)
{
for (int ix = 0; ix < statements.Count; ix++){
@@ -2411,6 +2840,103 @@ protected void EmitScopeInitializers (EmitContext ec)
s.Emit (ec);
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (scope_initializers != null) {
+ foreach (var si in scope_initializers)
+ si.FlowAnalysis (fc);
+ }
+
+ return DoFlowAnalysis (fc, 0);
+ }
+
+ bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
+ {
+ bool end_unreachable = !reachable;
+ for (; startIndex < statements.Count; ++startIndex) {
+ var s = statements[startIndex];
+
+ end_unreachable = s.FlowAnalysis (fc);
+ if (s.IsUnreachable) {
+ //
+ // This is kind of a hack, switch label is unreachable because we need to mark
+ // switch section end but at the same time we need to run Emit on it
+ //
+ if (s is SwitchLabel)
+ continue;
+
+ statements[startIndex] = new EmptyStatement (s.loc);
+ continue;
+ }
+
+ //
+ // Statement end reachability is needed mostly due to goto support. Consider
+ //
+ // if (cond) {
+ // goto X;
+ // } else {
+ // goto Y;
+ // }
+ // X:
+ //
+ // X label is reachable only via goto not as another statement after if. We need
+ // this for flow-analysis only to carry variable info correctly.
+ //
+ if (end_unreachable) { // s is ExitStatement) {
+ for (++startIndex; startIndex < statements.Count; ++startIndex) {
+ s = statements[startIndex];
+ if (s is SwitchLabel) {
+ s.FlowAnalysis (fc);
+ break;
+ }
+
+ if (s.IsUnreachable) {
+ s.FlowAnalysis (fc);
+ statements[startIndex] = new EmptyStatement (s.loc);
+ }
+ }
+ }
+ }
+
+ //
+ // The condition should be true unless there is forward jumping goto
+ //
+ // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
+ // Debug.Fail ();
+
+ return !Explicit.HasReachableClosingBrace;
+ }
+
+ public void ScanGotoJump (Statement label)
+ {
+ int i;
+ for (i = 0; i < statements.Count; ++i) {
+ if (statements[i] == label)
+ break;
+ }
+
+ var rc = new Reachability ();
+ for (; i < statements.Count; ++i) {
+ var s = statements[i];
+ rc = s.MarkReachable (rc);
+ if (rc.IsUnreachable)
+ return;
+ }
+
+ flags |= Flags.ReachableEnd;
+ }
+
+ public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
+ {
+ int i;
+ for (i = 0; i < statements.Count; ++i) {
+ if (statements[i] == label)
+ break;
+ }
+
+ DoFlowAnalysis (fc, ++i);
+ }
+
#if DEBUG
public override string ToString ()
{
@@ -2497,6 +3023,15 @@ public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
}
}
+ public bool HasReachableClosingBrace {
+ get {
+ return (flags & Flags.ReachableEnd) != 0;
+ }
+ set {
+ flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
+ }
+ }
+
public bool HasYield {
get {
return (flags & Flags.YieldBlock) != 0;
@@ -2551,7 +3086,8 @@ public override void Emit (EmitContext ec)
if (Parent != null)
ec.EndScope ();
- if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
+ if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
+ !IsCompilerGenerated && ec.Mark (EndLocation)) {
ec.Emit (OpCodes.Nop);
}
}
@@ -2744,6 +3280,16 @@ public void RegisterIteratorYield ()
}
}
+ public void SetCatchBlock ()
+ {
+ flags |= Flags.CatchBlock;
+ }
+
+ public void SetFinallyBlock ()
+ {
+ flags |= Flags.FinallyBlock;
+ }
+
public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
{
tryBlock.statements = statements;
@@ -2879,12 +3425,11 @@ public override void Emit (EmitContext ec)
protected ParametersCompiled parameters;
protected ParameterInfo[] parameter_info;
- bool resolved;
- protected bool unreachable;
+ protected bool resolved;
protected ToplevelBlock top_block;
protected StateMachine state_machine;
- public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
+ public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
: base (parent, 0, start, start)
{
if (parameters == null)
@@ -2893,7 +3438,7 @@ public ParametersBlock (Block parent, ParametersCompiled parameters, Location st
this.parameters = parameters;
ParametersBlock = this;
- flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
+ this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
this.top_block = parent.ParametersBlock.top_block;
ProcessParameters ();
@@ -2920,9 +3465,10 @@ protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters
this.scope_initializers = source.scope_initializers;
this.resolved = true;
- this.unreachable = source.unreachable;
+ this.reachable = source.reachable;
this.am_storey = source.am_storey;
this.state_machine = source.state_machine;
+ this.flags = source.flags & Flags.ReachableEnd;
ParametersBlock = this;
@@ -2984,30 +3530,30 @@ protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters
#endregion
- // <summary>
- // Check whether all `out' parameters have been assigned.
- // </summary>
- public void CheckOutParameters (FlowBranching.UsageVector vector)
+ //
+ // Checks whether all `out' parameters have been assigned.
+ //
+ public void CheckOutParametersAssignment (FlowAnalysisContext fc)
{
- if (vector.IsUnreachable)
- return;
-
- int n = parameter_info == null ? 0 : parameter_info.Length;
+ CheckOutParametersAssignment (fc, fc.DefiniteAssignment);
+ }
- for (int i = 0; i < n; i++) {
- VariableInfo var = parameter_info[i].VariableInfo;
+ public void CheckOutParametersAssignment (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
+ {
+ if (parameter_info == null)
+ return;
- if (var == null)
+ foreach (var p in parameter_info) {
+ if (p.VariableInfo == null)
continue;
- if (vector.IsAssigned (var, false))
+ if (p.VariableInfo.IsAssigned (dat))
continue;
- var p = parameter_info[i].Parameter;
- TopBlock.Report.Error (177, p.Location,
+ fc.Report.Error (177, p.Location,
"The out parameter `{0}' must be assigned to before control leaves the current method",
- p.Name);
- }
+ p.Parameter.Name);
+ }
}
public override Expression CreateExpressionTree (ResolveContext ec)
@@ -3043,6 +3589,16 @@ public void EmitEmbedded (EmitContext ec)
base.Emit (ec);
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ var res = base.DoFlowAnalysis (fc);
+
+ if (HasReachableClosingBrace)
+ CheckOutParametersAssignment (fc);
+
+ return res;
+ }
+
public ParameterInfo GetParameterInfo (Parameter p)
{
for (int i = 0; i < parameters.Count; ++i) {
@@ -3082,67 +3638,48 @@ protected void ProcessParameters ()
}
}
- public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
+ public override bool Resolve (BlockContext bc)
{
+ // TODO: if ((flags & Flags.Resolved) != 0)
+
if (resolved)
return true;
resolved = true;
- if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
+ if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
flags |= Flags.IsExpressionTree;
try {
- PrepareAssignmentAnalysis (rc);
-
- using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
- FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
+ PrepareAssignmentAnalysis (bc);
- if (!Resolve (rc))
- return false;
+ if (!base.Resolve (bc))
+ return false;
- unreachable = top_level.End ();
- }
} catch (Exception e) {
- if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException || rc.Report.Printer is NullReportPrinter)
+ if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter)
throw;
- if (rc.CurrentBlock != null) {
- rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
+ if (bc.CurrentBlock != null) {
+ bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
} else {
- rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
+ bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
}
- if (rc.Module.Compiler.Settings.DebugFlags > 0)
+ if (bc.Module.Compiler.Settings.DebugFlags > 0)
throw;
}
- if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
- if (rc.CurrentAnonymousMethod == null) {
- // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
- if (md is StateMachineMethod) {
- unreachable = true;
- } else {
- rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
- return false;
- }
- } else {
- //
- // If an asynchronous body of F is either an expression classified as nothing, or a
- // statement block where no return statements have expressions, the inferred return type is Task
- //
- if (IsAsync) {
- var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
- if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
- am.ReturnTypeInference = null;
- am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
- return true;
- }
- }
-
- rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
- rc.CurrentAnonymousMethod.GetSignatureForError ());
- return false;
+ //
+ // If an asynchronous body of F is either an expression classified as nothing, or a
+ // statement block where no return statements have expressions, the inferred return type is Task
+ //
+ if (IsAsync) {
+ var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
+ if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
+ am.ReturnTypeInference = null;
+ am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
+ return true;
}
}
@@ -3169,9 +3706,8 @@ public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host,
state_machine = stateMachine;
iterator.SetStateMachine (stateMachine);
- var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
+ var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
tlb.Original = this;
- tlb.IsCompilerGenerated = true;
tlb.state_machine = stateMachine;
tlb.AddStatement (new Return (iterator, iterator.Location));
return tlb;
@@ -3216,16 +3752,15 @@ public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinitio
state_machine = stateMachine;
initializer.SetStateMachine (stateMachine);
+ const Flags flags = Flags.CompilerGenerated;
+
var b = this is ToplevelBlock ?
- new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
- new ParametersBlock (Parent, parameters, Location.Null) {
- IsAsync = true,
- };
+ new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
+ new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
b.Original = this;
- b.IsCompilerGenerated = true;
b.state_machine = stateMachine;
- b.AddStatement (new StatementExpression (initializer));
+ b.AddStatement (new AsyncInitializerStatement (initializer));
return b;
}
}
@@ -3247,12 +3782,12 @@ public ToplevelBlock (CompilerContext ctx, Location loc)
{
}
- public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
+ public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
: base (parameters, start)
{
this.compiler = ctx;
+ this.flags = flags;
top_block = this;
- flags |= Flags.HasRet;
ProcessParameters ();
}
@@ -3268,7 +3803,6 @@ public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
{
this.compiler = source.TopBlock.compiler;
top_block = this;
- flags |= Flags.HasRet;
}
public bool IsIterator {
@@ -3543,8 +4077,11 @@ public LabeledStatement GetLabel (string name, Block block)
var label = value as LabeledStatement;
Block b = block;
if (label != null) {
- if (label.Block == b.Original)
- return label;
+ do {
+ if (label.Block == b.Original)
+ return label;
+ b = b.Parent;
+ } while (b != null);
} else {
List<LabeledStatement> list = (List<LabeledStatement>) value;
for (int i = 0; i < list.Count; ++i) {
@@ -3574,9 +4111,21 @@ public void AddThisVariable (BlockContext bc)
this_variable.PrepareAssignmentAnalysis (bc);
}
- public bool IsThisAssigned (BlockContext ec)
+ public bool IsThisAssigned (FlowAnalysisContext fc)
{
- return this_variable == null || this_variable.IsThisAssigned (ec, this);
+ return this_variable == null || this_variable.IsThisAssigned (fc, this);
+ }
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ var res = base.DoFlowAnalysis (fc);
+
+ //
+ // If we're a non-static struct constructor which doesn't have an
+ // initializer, then we must initialize all of the struct's fields.
+ //
+ IsThisAssigned (fc);
+ return res;
}
public override void Emit (EmitContext ec)
@@ -3605,7 +4154,7 @@ public override void Emit (EmitContext ec)
// As a workaround, we're always creating a return label in
// this case.
//
- if (ec.HasReturnLabel || !unreachable) {
+ if (ec.HasReturnLabel || HasReachableClosingBrace) {
if (ec.HasReturnLabel)
ec.MarkLabel (ec.ReturnLabel);
@@ -3622,12 +4171,44 @@ public override void Emit (EmitContext ec)
throw new InternalErrorException (e, StartLocation);
}
}
+
+ public bool Resolve (BlockContext bc, IMethodData md)
+ {
+ if (resolved)
+ return true;
+
+ var errors = bc.Report.Errors;
+
+ base.Resolve (bc);
+
+ if (bc.Report.Errors > errors)
+ return false;
+
+ MarkReachable (new Reachability ());
+
+ if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
+ // TODO: var md = bc.CurrentMemberDefinition;
+ bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
+ }
+
+ if ((flags & Flags.NoFlowAnalysis) != 0)
+ return true;
+
+ var fc = new FlowAnalysisContext (bc.Module.Compiler, this);
+ try {
+ FlowAnalysis (fc);
+ } catch (Exception e) {
+ throw new InternalErrorException (e, StartLocation);
+ }
+
+ return true;
+ }
}
public class SwitchLabel : Statement
{
- Expression label;
Constant converted;
+ Expression label;
Label? il_label;
@@ -3663,7 +4244,7 @@ public SwitchLabel (Expression expr, Location l)
return converted;
}
set {
- converted = value;
+ converted = value;
}
}
@@ -3683,21 +4264,34 @@ protected override void DoEmit (EmitContext ec)
ec.MarkLabel (GetILLabel (ec));
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (!SectionStart)
+ return false;
+
+ if (IsUnreachable) {
+ fc.DefiniteAssignment = new DefiniteAssignmentBitSet (fc.SwitchInitialDefinitiveAssignment);
+ } else {
+ fc.Report.Error (163, Location,
+ "Control cannot fall through from one case label `{0}' to another", GetSignatureForError ());
+ }
+
+ return false;
+ }
+
public override bool Resolve (BlockContext bc)
{
if (ResolveAndReduce (bc))
bc.Switch.RegisterLabel (bc, this);
- bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
-
- return base.Resolve (bc);
+ return true;
}
//
// Resolves the expression, reduces it to a literal if possible
// and then converts it to the requested type.
//
- bool ResolveAndReduce (ResolveContext rc)
+ bool ResolveAndReduce (BlockContext rc)
{
if (IsDefault)
return true;
@@ -3717,14 +4311,8 @@ bool ResolveAndReduce (ResolveContext rc)
public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
{
- string label;
- if (converted == null)
- label = "default";
- else
- label = converted.GetValueAsLiteral ();
-
ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
- ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
+ ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
}
protected override void CloneTo (CloneContext clonectx, Statement target)
@@ -3738,9 +4326,20 @@ public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
+
+ string GetSignatureForError ()
+ {
+ string label;
+ if (converted == null)
+ label = "default";
+ else
+ label = converted.GetValueAsLiteral ();
+
+ return string.Format ("case {0}:", label);
+ }
}
- public class Switch : Statement
+ public class Switch : LoopStatement
{
// structure used to hold blocks of keys while calculating table switch
sealed class LabelsRange : IComparable<LabelsRange>
@@ -3806,6 +4405,11 @@ protected override void CloneTo (CloneContext clonectx, Statement target)
throw new NotImplementedException ();
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return false;
+ }
+
protected override void DoEmit (EmitContext ec)
{
body.EmitDispatch (ec);
@@ -3822,6 +4426,7 @@ protected override void DoEmit (EmitContext ec)
List<SwitchLabel> case_labels;
List<Tuple<GotoCase, Constant>> goto_cases;
+ List<DefiniteAssignmentBitSet> end_reachable_das;
/// <summary>
/// The governing switch type
@@ -3838,6 +4443,7 @@ protected override void DoEmit (EmitContext ec)
ExpressionStatement string_dictionary;
FieldExpr switch_cache_field;
ExplicitBlock block;
+ bool end_reachable;
//
// Nullable Types support
@@ -3845,12 +4451,15 @@ protected override void DoEmit (EmitContext ec)
Nullable.Unwrap unwrap;
public Switch (Expression e, ExplicitBlock block, Location l)
+ : base (block)
{
Expr = e;
this.block = block;
loc = l;
}
+ public SwitchLabel ActiveLabel { get; set; }
+
public ExplicitBlock Block {
get {
return block;
@@ -3869,6 +4478,12 @@ public Switch (Expression e, ExplicitBlock block, Location l)
}
}
+ public List<SwitchLabel> RegisteredLabels {
+ get {
+ return case_labels;
+ }
+ }
+
//
// Determines the governing type for a switch. The returned
// expression might be the expression from the switch, or an
@@ -3942,7 +4557,7 @@ public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
};
}
- public void RegisterLabel (ResolveContext rc, SwitchLabel sl)
+ public void RegisterLabel (BlockContext rc, SwitchLabel sl)
{
case_labels.Add (sl);
@@ -4128,7 +4743,30 @@ SwitchLabel FindLabel (Constant value)
}
}
- return sl;
+ return sl;
+ }
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ Expr.FlowAnalysis (fc);
+
+ var prev_switch = fc.SwitchInitialDefinitiveAssignment;
+ var InitialDefinitiveAssignment = fc.DefiniteAssignment;
+ fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
+
+ block.FlowAnalysis (fc);
+
+ fc.SwitchInitialDefinitiveAssignment = prev_switch;
+
+ if (end_reachable_das != null) {
+ var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
+ InitialDefinitiveAssignment |= sections_das;
+ end_reachable_das = null;
+ }
+
+ fc.DefiniteAssignment = InitialDefinitiveAssignment;
+
+ return case_default != null && !end_reachable;
}
public override bool Resolve (BlockContext ec)
@@ -4199,33 +4837,23 @@ public override bool Resolve (BlockContext ec)
Switch old_switch = ec.Switch;
ec.Switch = this;
- ec.Switch.SwitchType = SwitchType;
-
- ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
-
- ec.CurrentBranching.CurrentUsageVector.Goto ();
+ var parent_los = ec.EnclosingLoopOrSwitch;
+ ec.EnclosingLoopOrSwitch = this;
- var ok = block.Resolve (ec);
-
- if (case_default == null)
- ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
-
- if (ec.IsUnreachable)
- ec.KillFlowBranching ();
- else
- ec.EndFlowBranching ();
+ var ok = Statement.Resolve (ec);
+ ec.EnclosingLoopOrSwitch = parent_los;
ec.Switch = old_switch;
//
// Check if all goto cases are valid. Needs to be done after switch
- // is resolved becuase goto can jump forward in the scope.
+ // is resolved because goto can jump forward in the scope.
//
if (goto_cases != null) {
foreach (var gc in goto_cases) {
if (gc.Item1 == null) {
if (DefaultLabel == null) {
- FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
+ Goto.Error_UnknownLabel (ec, "default", loc);
}
continue;
@@ -4233,17 +4861,13 @@ public override bool Resolve (BlockContext ec)
var sl = FindLabel (gc.Item2);
if (sl == null) {
- FlowBranchingBlock.Error_UnknownLabel (loc, "case " + gc.Item2.GetValueAsLiteral (), ec.Report);
+ Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
} else {
gc.Item1.Label = sl;
}
}
}
- if (constant != null) {
- ResolveUnreachableSections (ec, constant);
- }
-
if (!ok)
return false;
@@ -4274,6 +4898,87 @@ bool HasOnlyDefaultSection ()
return true;
}
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ if (rc.IsUnreachable)
+ return rc;
+
+ base.MarkReachable (rc);
+
+ SwitchLabel constant_label = null;
+ var constant = new_expr as Constant;
+ if (constant != null) {
+ constant_label = FindLabel (constant) ?? case_default;
+ if (constant_label == null) {
+ block.Statements.RemoveAt (0);
+ return rc;
+ }
+ }
+
+ var switch_rc = rc;
+ var section_rc = new Reachability ();
+ SwitchLabel prev_label = null;
+
+ for (int i = 0; i < block.Statements.Count; ++i) {
+ var s = block.Statements[i];
+ var sl = s as SwitchLabel;
+
+ if (sl != null) {
+ if (!sl.SectionStart) {
+ sl.MarkReachable (section_rc);
+ continue;
+ }
+
+ if (prev_label == null) {
+ prev_label = sl;
+ switch_rc = Reachability.CreateUnreachable ();
+
+ if (constant_label != null && sl != constant_label)
+ section_rc = Reachability.CreateUnreachable ();
+
+ continue;
+ }
+
+ //
+ // Small trick, using unreachable flag for label means
+ // the label section does not fallthrough
+ //
+ prev_label.MarkReachable (section_rc);
+
+ prev_label = sl;
+ switch_rc &= section_rc;
+ section_rc = new Reachability ();
+
+ if (constant_label != null && sl != constant_label)
+ section_rc = Reachability.CreateUnreachable ();
+
+ continue;
+ }
+
+ section_rc = s.MarkReachable (section_rc);
+ }
+
+ if (prev_label != null) {
+ prev_label.MarkReachable (section_rc);
+ switch_rc &= section_rc;
+ }
+
+ //
+ // Reachability can affect parent only when all possible paths are handled but
+ // we still need to run reachability check on switch body to check for fall-through
+ //
+ if (case_default == null && constant_label == null)
+ return rc;
+
+ //
+ // We have at least one local exit from the switch
+ //
+ if (end_reachable)
+ return rc;
+
+ return switch_rc;
+ }
+
public void RegisterGotoCase (GotoCase gotoCase, Constant value)
{
if (goto_cases == null)
@@ -4340,40 +5045,6 @@ void ResolveStringSwitchMap (ResolveContext ec)
string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
}
- void ResolveUnreachableSections (BlockContext bc, Constant value)
- {
- var constant_label = FindLabel (value) ?? case_default;
-
- bool found = false;
- bool unreachable_reported = false;
- for (int i = 0; i < block.Statements.Count; ++i) {
- var s = block.Statements[i];
-
- if (s is SwitchLabel) {
- if (unreachable_reported) {
- found = unreachable_reported = false;
- }
-
- found |= s == constant_label;
- continue;
- }
-
- if (found) {
- unreachable_reported = true;
- continue;
- }
-
- if (!unreachable_reported) {
- unreachable_reported = true;
- if (!bc.IsUnreachable) {
- bc.Report.Warning (162, 2, s.loc, "Unreachable code detected");
- }
- }
-
- block.Statements[i] = new EmptyStatement (s.loc);
- }
- }
-
void DoEmitStringSwitch (EmitContext ec)
{
Label l_initialized = ec.DefineLabel ();
@@ -4495,9 +5166,6 @@ void EmitDispatch (EmitContext ec)
protected override void DoEmit (EmitContext ec)
{
- // Workaround broken flow-analysis
- block.HasUnreachableClosingBrace = true;
-
//
// Setup the codegen context
//
@@ -4546,16 +5214,32 @@ protected override void CloneTo (CloneContext clonectx, Statement t)
Switch target = (Switch) t;
target.Expr = Expr.Clone (clonectx);
- target.block = (ExplicitBlock) block.Clone (clonectx);
+ target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
}
+
+ public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
+ {
+ if (case_default == null)
+ return;
+
+ if (end_reachable_das == null)
+ end_reachable_das = new List<DefiniteAssignmentBitSet> ();
+
+ end_reachable_das.Add (fc.DefiniteAssignment);
+ }
+
+ public override void SetEndReachable ()
+ {
+ end_reachable = true;
+ }
}
- // A place where execution can restart in an iterator
+ // A place where execution can restart in a state machine
public abstract class ResumableStatement : Statement
{
bool prepared;
@@ -4715,8 +5399,32 @@ public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end,
ec.EndExceptionBlock ();
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ var res = stmt.FlowAnalysis (fc);
+ parent = null;
+ return res;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+ return Statement.MarkReachable (rc);
+ }
+
public override bool Resolve (BlockContext bc)
{
+ bool ok;
+
+ parent = bc.CurrentTryBlock;
+ bc.CurrentTryBlock = this;
+
+ using (bc.Set (ResolveContext.Options.TryScope)) {
+ ok = stmt.Resolve (bc);
+ }
+
+ bc.CurrentTryBlock = parent;
+
//
// Finally block inside iterator is called from MoveNext and
// Dispose methods that means we need to lift the block into
@@ -4730,7 +5438,7 @@ public override bool Resolve (BlockContext bc)
}
}
- return base.Resolve (bc);
+ return base.Resolve (bc) && ok;
}
}
@@ -4739,11 +5447,9 @@ public override bool Resolve (BlockContext bc)
//
public abstract class ExceptionStatement : ResumableStatement
{
-#if !STATIC
- bool code_follows;
-#endif
protected List<ResumableStatement> resume_points;
protected int first_resume_pc;
+ protected ExceptionStatement parent;
protected ExceptionStatement (Location loc)
{
@@ -4778,26 +5484,18 @@ protected virtual void EmitTryBodyPrepare (EmitContext ec)
}
}
- public void SomeCodeFollows ()
+ public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
{
-#if !STATIC
- code_follows = true;
-#endif
- }
+ if (parent != null) {
+ // TODO: MOVE to virtual TryCatch
+ var tc = this as TryCatch;
+ var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
- public override bool Resolve (BlockContext ec)
- {
-#if !STATIC
- // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
- // So, ensure there's some IL code after this statement.
- if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
- ec.NeedReturnLabel ();
-#endif
- return true;
- }
+ pc = parent.AddResumePoint (s, pc, stateMachine);
+ } else {
+ pc = stateMachine.AddResumePoint (this);
+ }
- public void AddResumePoint (ResumableStatement stmt, int pc)
- {
if (resume_points == null) {
resume_points = new List<ResumableStatement> ();
first_resume_pc = pc;
@@ -4807,8 +5505,8 @@ public void AddResumePoint (ResumableStatement stmt, int pc)
throw new InternalErrorException ("missed an intervening AddResumePoint?");
resume_points.Add (stmt);
+ return pc;
}
-
}
public class Lock : TryFinallyBlock
@@ -4871,17 +5569,13 @@ public override bool Resolve (BlockContext ec)
}
using (ec.Set (ResolveContext.Options.LockScope)) {
- ec.StartFlowBranching (this);
- Statement.Resolve (ec);
- ec.EndFlowBranching ();
+ base.Resolve (ec);
}
if (lv != null) {
lv.IsLockedByStatement = locked;
}
- base.Resolve (ec);
-
return true;
}
@@ -4992,6 +5686,17 @@ protected override void DoEmit (EmitContext ec)
Block.Emit (ec);
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return Block.FlowAnalysis (fc);
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+ return Block.MarkReachable (rc);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Unchecked target = (Unchecked) t;
@@ -5027,6 +5732,17 @@ protected override void DoEmit (EmitContext ec)
Block.Emit (ec);
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return Block.FlowAnalysis (fc);
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+ return Block.MarkReachable (rc);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Checked target = (Checked) t;
@@ -5064,6 +5780,17 @@ protected override void DoEmit (EmitContext ec)
Block.Emit (ec);
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return Block.FlowAnalysis (fc);
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+ return Block.MarkReachable (rc);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Unsafe target = (Unsafe) t;
@@ -5093,6 +5820,11 @@ protected Emitter (Expression expr, LocalVariable li)
}
public abstract void EmitExit (EmitContext ec);
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ }
}
class ExpressionEmitter : Emitter {
@@ -5306,19 +6038,20 @@ public Fixed (VariableDeclaration decl, Statement stmt, Location l)
#endregion
- public override bool Resolve (BlockContext ec)
+ public override bool Resolve (BlockContext bc)
{
- using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
- if (!decl.Resolve (ec))
+ using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
+ if (!decl.Resolve (bc))
return false;
}
- ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
- bool ok = statement.Resolve (ec);
- bool flow_unreachable = ec.EndFlowBranching ();
- has_ret = flow_unreachable;
+ return statement.Resolve (bc);
+ }
- return ok;
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ decl.FlowAnalysis (fc);
+ return statement.FlowAnalysis (fc);
}
protected override void DoEmit (EmitContext ec)
@@ -5348,6 +6081,19 @@ protected override void DoEmit (EmitContext ec)
}
}
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+
+ decl.MarkReachable (rc);
+
+ rc = statement.MarkReachable (rc);
+
+ // TODO: What if there is local exit?
+ has_ret = rc.IsUnreachable;
+ return rc;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Fixed target = (Fixed) t;
@@ -5364,13 +6110,13 @@ public override object Accept (StructuralVisitor visitor)
public class Catch : Statement
{
- Block block;
+ ExplicitBlock block;
LocalVariable li;
FullNamedExpression type_expr;
CompilerAssign assign;
TypeSpec type;
- public Catch (Block block, Location loc)
+ public Catch (ExplicitBlock block, Location loc)
{
this.block = block;
this.loc = loc;
@@ -5378,7 +6124,7 @@ public Catch (Block block, Location loc)
#region Properties
- public Block Block {
+ public ExplicitBlock Block {
get {
return block;
}
@@ -5447,7 +6193,7 @@ protected override void DoEmit (EmitContext ec)
public override bool Resolve (BlockContext ec)
{
- using (ec.With (ResolveContext.Options.CatchScope, true)) {
+ using (ec.Set (ResolveContext.Options.CatchScope)) {
if (type_expr != null) {
type = type_expr.ResolveAsType (ec);
if (type == null)
@@ -5472,10 +6218,27 @@ public override bool Resolve (BlockContext ec)
}
}
+ Block.SetCatchBlock ();
return Block.Resolve (ec);
}
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (li != null) {
+ fc.SetVariableAssigned (li.VariableInfo, true);
+ }
+
+ return block.FlowAnalysis (fc);
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+
+ return block.MarkReachable (rc);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Catch target = (Catch) t;
@@ -5483,47 +6246,44 @@ protected override void CloneTo (CloneContext clonectx, Statement t)
if (type_expr != null)
target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
- target.block = clonectx.LookupBlock (block);
+ target.block = (ExplicitBlock) clonectx.LookupBlock (block);
}
}
public class TryFinally : TryFinallyBlock
{
- Block fini;
+ ExplicitBlock fini;
+ List<DefiniteAssignmentBitSet> try_exit_dat;
- public TryFinally (Statement stmt, Block fini, Location loc)
+ public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
: base (stmt, loc)
{
this.fini = fini;
}
- public Block Finallyblock {
+ public ExplicitBlock FinallyBlock {
get {
return fini;
}
}
- public override bool Resolve (BlockContext ec)
+ public void RegisterOutParametersCheck (DefiniteAssignmentBitSet vector)
{
- bool ok = true;
+ if (try_exit_dat == null)
+ try_exit_dat = new List<DefiniteAssignmentBitSet> ();
- ec.StartFlowBranching (this);
-
- if (!stmt.Resolve (ec))
- ok = false;
+ try_exit_dat.Add (vector);
+ }
- if (ok)
- ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
+ public override bool Resolve (BlockContext bc)
+ {
+ bool ok = base.Resolve (bc);
- using (ec.With (ResolveContext.Options.FinallyScope, true)) {
- if (!fini.Resolve (ec))
- ok = false;
+ fini.SetFinallyBlock ();
+ using (bc.Set (ResolveContext.Options.FinallyScope)) {
+ ok &= fini.Resolve (bc);
}
- ec.EndFlowBranching ();
-
- ok &= base.Resolve (ec);
-
return ok;
}
@@ -5537,13 +6297,54 @@ public override void EmitFinallyBody (EmitContext ec)
fini.Emit (ec);
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ var da = fc.BranchDefiniteAssignment ();
+
+ var tf = fc.TryFinally;
+ fc.TryFinally = this;
+
+ var res_stmt = Statement.FlowAnalysis (fc);
+
+ fc.TryFinally = tf;
+
+ var try_da = fc.DefiniteAssignment;
+ fc.DefiniteAssignment = da;
+
+ var res_fin = fini.FlowAnalysis (fc);
+
+ if (try_exit_dat != null) {
+ //
+ // try block has global exit but we need to run definite assignment check
+ // for parameter block out parameter after finally block because it's always
+ // executed before exit
+ //
+ foreach (var try_da_part in try_exit_dat)
+ fc.ParametersBlock.CheckOutParametersAssignment (fc, fc.DefiniteAssignment | try_da_part);
+
+ try_exit_dat = null;
+ }
+
+ fc.DefiniteAssignment |= try_da;
+ return res_stmt | res_fin;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ //
+ // Mark finally block first for any exit statement in try block
+ // to know whether the code which follows finally is reachable
+ //
+ return fini.MarkReachable (rc) | base.MarkReachable (rc);
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
TryFinally target = (TryFinally) t;
target.stmt = stmt.Clone (clonectx);
if (fini != null)
- target.fini = clonectx.LookupBlock (fini);
+ target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
}
public override object Accept (StructuralVisitor visitor)
@@ -5578,23 +6379,28 @@ public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside
}
}
- public override bool Resolve (BlockContext ec)
+ public override bool Resolve (BlockContext bc)
{
- bool ok = true;
+ bool ok;
- ec.StartFlowBranching (this);
+ using (bc.Set (ResolveContext.Options.TryScope)) {
+ parent = bc.CurrentTryBlock;
- if (!Block.Resolve (ec))
- ok = false;
+ if (IsTryCatchFinally) {
+ ok = Block.Resolve (bc);
+ } else {
+ using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
+ bc.CurrentTryBlock = this;
+ ok = Block.Resolve (bc);
+ bc.CurrentTryBlock = parent;
+ }
+ }
+ }
for (int i = 0; i < clauses.Count; ++i) {
var c = clauses[i];
- ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
- if (!c.Resolve (ec)) {
- ok = false;
- continue;
- }
+ ok &= c.Resolve (bc);
TypeSpec resolved_type = c.CatchType;
for (int ii = 0; ii < clauses.Count; ++ii) {
@@ -5605,13 +6411,13 @@ public override bool Resolve (BlockContext ec)
if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
continue;
- if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
+ if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
continue;
- if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
+ if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
continue;
- ec.Report.Warning (1058, 1, c.loc,
+ bc.Report.Warning (1058, 1, c.loc,
"A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
continue;
@@ -5625,7 +6431,7 @@ public override bool Resolve (BlockContext ec)
continue;
if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
- ec.Report.Error (160, c.loc,
+ bc.Report.Error (160, c.loc,
"A previous catch clause already catches all exceptions of this or a super type `{0}'",
ct.GetSignatureForError ());
ok = false;
@@ -5633,9 +6439,7 @@ public override bool Resolve (BlockContext ec)
}
}
- ec.EndFlowBranching ();
-
- return base.Resolve (ec) && ok;
+ return base.Resolve (bc) && ok;
}
protected sealed override void DoEmit (EmitContext ec)
@@ -5652,6 +6456,45 @@ protected sealed override void DoEmit (EmitContext ec)
ec.EndExceptionBlock ();
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ var start_fc = fc.BranchDefiniteAssignment ();
+ var res = Block.FlowAnalysis (fc);
+
+ DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
+
+ foreach (var c in clauses) {
+ fc.DefiniteAssignment = new DefiniteAssignmentBitSet (start_fc);
+ if (!c.FlowAnalysis (fc)) {
+ if (try_fc == null)
+ try_fc = fc.DefiniteAssignment;
+ else
+ try_fc &= fc.DefiniteAssignment;
+
+ res = false;
+ }
+ }
+
+ fc.DefiniteAssignment = try_fc ?? start_fc;
+ parent = null;
+ return res;
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ if (rc.IsUnreachable)
+ return rc;
+
+ base.MarkReachable (rc);
+
+ var tc_rc = Block.MarkReachable (rc);
+
+ foreach (var c in clauses)
+ tc_rc &= c.MarkReachable (rc);
+
+ return tc_rc;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
TryCatch target = (TryCatch) t;
@@ -5881,6 +6724,18 @@ public override void EmitFinallyBody (EmitContext ec)
decl.EmitDispose (ec);
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ decl.FlowAnalysis (fc);
+ return stmt.FlowAnalysis (fc);
+ }
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ decl.MarkReachable (rc);
+ return base.MarkReachable (rc);
+ }
+
public override bool Resolve (BlockContext ec)
{
VariableReference vr;
@@ -5909,17 +6764,11 @@ public override bool Resolve (BlockContext ec)
}
}
- ec.StartFlowBranching (this);
-
- stmt.Resolve (ec);
-
- ec.EndFlowBranching ();
+ base.Resolve (ec);
if (vr != null)
vr.IsLockedByStatement = vr_locked;
- base.Resolve (ec);
-
return true;
}
@@ -5940,7 +6789,7 @@ public override object Accept (StructuralVisitor visitor)
/// <summary>
/// Implementation of the foreach C# statement
/// </summary>
- public class Foreach : Statement
+ public class Foreach : LoopStatement
{
abstract class IteratorStatement : Statement
{
@@ -5965,6 +6814,11 @@ public override void Emit (EmitContext ec)
base.Emit (ec);
}
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ throw new NotImplementedException ();
+ }
}
sealed class ArrayForeach : IteratorStatement
@@ -6046,22 +6900,7 @@ public override bool Resolve (BlockContext ec)
for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
- bool ok = true;
-
- ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
- ec.CurrentBranching.CreateSibling ();
-
- ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
- if (!for_each.body.Resolve (ec))
- ok = false;
- ec.EndFlowBranching ();
-
- // There's no direct control flow from the end of the embedded statement to the end of the loop
- ec.CurrentBranching.CurrentUsageVector.Goto ();
-
- ec.EndFlowBranching ();
-
- return ok;
+ return for_each.body.Resolve (ec);
}
protected override void DoEmit (EmitContext ec)
@@ -6431,15 +7270,14 @@ bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, Memb
Expression type;
LocalVariable variable;
Expression expr;
- Statement statement;
Block body;
public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
+ : base (stmt)
{
this.type = type;
this.variable = var;
this.expr = expr;
- this.statement = stmt;
this.body = body;
loc = l;
}
@@ -6448,10 +7286,6 @@ public Foreach (Expression type, LocalVariable var, Expression expr, Statement s
get { return expr; }
}
- public Statement Statement {
- get { return statement; }
- }
-
public Expression TypeExpression {
get { return type; }
}
@@ -6460,6 +7294,15 @@ public Foreach (Expression type, LocalVariable var, Expression expr, Statement s
get { return variable; }
}
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ base.MarkReachable (rc);
+
+ body.MarkReachable (rc);
+
+ return rc;
+ }
+
public override bool Resolve (BlockContext ec)
{
expr = expr.Resolve (ec);
@@ -6471,12 +7314,12 @@ public override bool Resolve (BlockContext ec)
return false;
}
- body.AddStatement (statement);
+ body.AddStatement (Statement);
if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
- statement = new ArrayForeach (this, 1);
+ Statement = new ArrayForeach (this, 1);
} else if (expr.Type is ArrayContainer) {
- statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
+ Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
} else {
if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
@@ -6484,10 +7327,11 @@ public override bool Resolve (BlockContext ec)
return false;
}
- statement = new CollectionForeach (this, variable, expr);
+ Statement = new CollectionForeach (this, variable, expr);
}
- return statement.Resolve (ec);
+ base.Resolve (ec);
+ return true;
}
protected override void DoEmit (EmitContext ec)
@@ -6496,20 +7340,30 @@ protected override void DoEmit (EmitContext ec)
ec.LoopBegin = ec.DefineLabel ();
ec.LoopEnd = ec.DefineLabel ();
- if (!(statement is Block))
+ if (!(Statement is Block))
ec.BeginCompilerScope ();
variable.CreateBuilder (ec);
- statement.Emit (ec);
+ Statement.Emit (ec);
- if (!(statement is Block))
+ if (!(Statement is Block))
ec.EndScope ();
ec.LoopBegin = old_begin;
ec.LoopEnd = old_end;
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+
+ var da = fc.BranchDefiniteAssignment ();
+ body.FlowAnalysis (fc);
+ fc.DefiniteAssignment = da;
+ return false;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement t)
{
Foreach target = (Foreach) t;
@@ -6517,7 +7371,7 @@ protected override void CloneTo (CloneContext clonectx, Statement t)
target.type = type.Clone (clonectx);
target.expr = expr.Clone (clonectx);
target.body = (Block) body.Clone (clonectx);
- target.statement = statement.Clone (clonectx);
+ target.Statement = Statement.Clone (clonectx);
}
public override object Accept (StructuralVisitor visitor)
Modified: mcs/tests/known-issues-net_4_5
===================================================================
@@ -5,4 +5,3 @@
# csXXXX.cs IGNORE : adds test to ignore list
gtest-230.cs
-test-872.cs
Modified: mcs/tests/test-519.cs
===================================================================
@@ -1,3 +1,5 @@
+using System;
+
class Foo {
public static int Main ()
{
@@ -5,9 +7,17 @@ public static int Main ()
f ();
return 1;
} catch {
- return 0;
}
+
+ try {
+ f2 ();
+ return 2;
+ } catch (ApplicationException) {
+ }
+
+ return 0;
}
+
static void f ()
{
try {
@@ -20,4 +30,15 @@ static void f ()
skip:
;
}
+
+ static void f2 ()
+ {
+ try {
+ goto FinallyExit;
+ } finally {
+ throw new ApplicationException ();
+ }
+ FinallyExit:
+ Console.WriteLine ("Too late");
+ }
}
Modified: mcs/tests/test-579.cs
===================================================================
@@ -4,6 +4,17 @@ public class TestCase
{
public static int Main ()
{
+ if (Test1 () != 0)
+ return 1;
+
+ if (Test2 () != 0)
+ return 2;
+
+ return 0;
+ }
+
+ static int Test1 ()
+ {
int i = 0;
{
goto A;
@@ -21,4 +32,46 @@ public static int Main ()
return 0;
}
+
+ static int Test2 ()
+ {
+ int i = 0;
+
+ while (true) {
+ {
+ goto A;
+ A:
+ i += 3;
+ break;
+ }
+ }
+
+ if (i != 3)
+ return 1;
+
+ return 0;
+ }
+
+ static int Test3 ()
+ {
+ int i = 0;
+
+ do {
+ {
+ goto A;
+ A:
+ i += 3;
+ goto X;
+ X:
+ break;
+ }
+#pragma warning disable 162, 429
+ } while (i > 0);
+#pragma warning restore 162, 429
+
+ if (i != 3)
+ return 1;
+
+ return 0;
+ }
}
Added: mcs/tests/test-874.cs
===================================================================
@@ -0,0 +1,18 @@
+using System;
+
+class X
+{
+ public static void Main ()
+ {
+ int a;
+ goto X;
+ A:
+ Console.WriteLine (a);
+ goto Y;
+ X:
+ a = 1;
+ goto A;
+ Y:
+ return;
+ }
+}
\ No newline at end of file
Added: mcs/tests/test-876.cs
===================================================================
@@ -0,0 +1,91 @@
+using System;
+
+class T
+{
+ public static int Main ()
+ {
+ Test1 ();
+ Test2 ();
+ Test3 (0, 1);
+ Test4 ();
+
+ switch (1) {
+ case 1:
+ return 0;
+ default:
+ break;
+ }
+ }
+
+ static void Test1 ()
+ {
+ int g = 9;
+ A:
+ switch (g) {
+ case 4:
+ return;
+ case 5:
+ goto A;
+ }
+
+ switch (g) {
+ case 9:
+ break;
+ }
+
+ return;
+ }
+
+ static void Test2 ()
+ {
+ int a,b;
+ int g = 9;
+ if (g > 0) {
+ a = 1;
+ goto X;
+ } else {
+ b = 2;
+ goto Y;
+ }
+
+ X:
+ Console.WriteLine (a);
+ return;
+ Y:
+ Console.WriteLine (b);
+ return;
+ }
+
+ static uint Test3 (int self, uint data)
+ {
+ uint rid;
+ switch (self) {
+ case 0:
+ rid = 2;
+ switch (data & 3) {
+ case 0:
+ goto ret;
+ default:
+ goto exit;
+ }
+ default:
+ goto exit;
+ }
+ ret:
+ return rid;
+ exit:
+ return 0;
+ }
+
+ static void Test4 ()
+ {
+ bool v;
+ try {
+ throw new NotImplementedException ();
+ } catch (System.Exception) {
+ v = false;
+ }
+
+ Console.WriteLine (v);
+ }
+}
Modified: mcs/tests/ver-il-net_4_5.xml
===================================================================
@@ -31086,7 +31086,7 @@
<size>10</size>
</method>
<method name="Int32 test40(Int32)" attrs="145">
- <size>27</size>
+ <size>20</size>
</method>
<method name="Void .ctor()" attrs="6278">
<size>7</size>
@@ -40380,14 +40380,17 @@
<test name="test-519.cs">
<type name="Foo">
<method name="Int32 Main()" attrs="150">
- <size>25</size>
+ <size>52</size>
</method>
<method name="Void f()" attrs="145">
- <size>21</size>
+ <size>23</size>
</method>
<method name="Void .ctor()" attrs="6278">
<size>7</size>
</method>
+ <method name="Void f2()" attrs="145">
+ <size>16</size>
+ </method>
</type>
</test>
<test name="test-52.cs">
@@ -41722,11 +41725,20 @@
<test name="test-579.cs">
<type name="TestCase">
<method name="Int32 Main()" attrs="150">
- <size>49</size>
+ <size>44</size>
</method>
<method name="Void .ctor()" attrs="6278">
<size>7</size>
</method>
+ <method name="Int32 Test1()" attrs="145">
+ <size>49</size>
+ </method>
+ <method name="Int32 Test2()" attrs="145">
+ <size>48</size>
+ </method>
+ <method name="Int32 Test3()" attrs="145">
+ <size>47</size>
+ </method>
</type>
</test>
<test name="test-58.cs">
@@ -43076,7 +43088,7 @@
<test name="test-634.cs">
<type name="Test">
<method name="Void TestFunc()" attrs="150">
- <size>7</size>
+ <size>13</size>
</method>
<method name="Void Main(System.String[])" attrs="150">
<size>7</size>
@@ -43099,6 +43111,11 @@
<size>0</size>
</method>
</type>
+ <type name="Test+<TestFunc>c__AnonStorey0">
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
</test>
<test name="test-635.cs">
<type name="ShortCircuitFold">
@@ -48329,6 +48346,16 @@
</method>
</type>
</test>
+ <test name="test-872.cs">
+ <type name="X">
+ <method name="Void Main()" attrs="150">
+ <size>66</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ </test>
<test name="test-873.cs">
<type name="Program">
<method name="Int32 Main()" attrs="145">
@@ -48339,6 +48366,16 @@
</method>
</type>
</test>
+ <test name="test-874.cs">
+ <type name="X">
+ <method name="Void Main()" attrs="150">
+ <size>30</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ </test>
<test name="test-875.cs">
<type name="Test">
<method name="Void Main()" attrs="150">
@@ -48349,6 +48386,28 @@
</method>
</type>
</test>
+ <test name="test-876.cs">
+ <type name="T">
+ <method name="Int32 Main()" attrs="150">
+ <size>34</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ <method name="Void Test1()" attrs="145">
+ <size>57</size>
+ </method>
+ <method name="Void Test2()" attrs="145">
+ <size>50</size>
+ </method>
+ <method name="UInt32 Test3(Int32, UInt32)" attrs="145">
+ <size>60</size>
+ </method>
+ <method name="Void Test4()" attrs="145">
+ <size>25</size>
+ </method>
+ </type>
+ </test>
<test name="test-88.cs">
<type name="X">
<method name="Void f(System.String)" attrs="145">
@@ -56284,12 +56343,12 @@
</type>
<type name="Program+<Main>c__AnonStorey1+<Main>c__async0">
<method name="Void MoveNext()" attrs="486">
- <size>226</size>
+ <size>225</size>
</method>
</type>
<type name="Program+<Main>c__AnonStorey1+<Main>c__async2">
<method name="Void MoveNext()" attrs="486">
- <size>226</size>
+ <size>225</size>
</method>
</type>
<type name="Program+<Main>c__AnonStorey1+<Main>c__async3">
@@ -56488,12 +56547,12 @@
</type>
<type name="AsyncTypeInference+<Main>c__async8">
<method name="Void MoveNext()" attrs="486">
- <size>157</size>
+ <size>156</size>
</method>
</type>
<type name="AsyncTypeInference+<Main>c__asyncB">
<method name="Void MoveNext()" attrs="486">
- <size>38</size>
+ <size>37</size>
</method>
</type>
<type name="AsyncTypeInference+<Main>c__async2">
@@ -59741,7 +59800,7 @@
</type>
<type name="Program+<Main>c__async1">
<method name="Void MoveNext()" attrs="486">
- <size>197</size>
+ <size>196</size>
</method>
<method name="Void SetStateMachine(IAsyncStateMachine)" attrs="486">
<size>13</size>
@@ -59836,7 +59895,7 @@
</type>
<type name="C+<Foo>c__async3">
<method name="Void MoveNext()" attrs="486">
- <size>169</size>
+ <size>168</size>
</method>
<method name="Void SetStateMachine(IAsyncStateMachine)" attrs="486">
<size>13</size>
@@ -59887,7 +59946,7 @@
</type>
<type name="C+<Foo>c__async0+<Foo>c__AnonStorey4+<Foo>c__async3">
<method name="Void MoveNext()" attrs="486">
- <size>185</size>
+ <size>184</size>
</method>
<method name="Void SetStateMachine(IAsyncStateMachine)" attrs="486">
<size>13</size>
@@ -59941,7 +60000,7 @@
</type>
<type name="C+<Foo>c__async0+<Foo>c__AnonStorey4+<Foo>c__async3">
<method name="Void MoveNext()" attrs="486">
- <size>214</size>
+ <size>213</size>
</method>
<method name="Void SetStateMachine(IAsyncStateMachine)" attrs="486">
<size>13</size>
@@ -60032,7 +60091,7 @@
</type>
<type name="C+<Test>c__async0">
<method name="Void MoveNext()" attrs="486">
- <size>61</size>
+ <size>60</size>
</method>
<method name="Void SetStateMachine(IAsyncStateMachine)" attrs="486">
<size>13</size>
@@ -60290,7 +60349,7 @@
</type>
<type name="X+<Foo>c__AnonStorey1+<Foo>c__async0">
<method name="Void MoveNext()" attrs="486">
- <size>44</size>
+ <size>43</size>
</method>
<method name="Void SetStateMachine(IAsyncStateMachine)" attrs="486">
<size>13</size>
_______________________________________________
Mono-patches maillist - [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches