+++ /dev/null
-// 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;
- }
- }
-}
+++ /dev/null
-// CS0162: Unreachable code detected
-// Line: 10
-// Compiler options: -warnaserror
-
-public class X
-{
- public static void Main ()
- {
- return;
- 1+1;
- }
-}
\ No newline at end of file
+++ /dev/null
-// CS0162: Unreachable code detected
-// Line: 12
-// Compiler options: -warnaserror
-
-public class X
-{
- static void test (int stop)
- {
- int pos = 0;
- do {
- break;
- } while (pos < stop);
- }
-}
--- /dev/null
+// CS0162: Unreachable code detected
+// Line: 14
+// Compiler options: -warnaserror
+
+using System;
+
+class X
+{
+
+ public static void Main ()
+ {
+ goto X;
+ A:
+ bool b = false;
+ if (b) {
+ goto A;
+ }
+ X:
+ return;
+ }
+}
\ No newline at end of file
// 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 ()
{
--- /dev/null
+// 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
-// 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
{
--- /dev/null
+// 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;
+ }
+}
+
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// CS0429: Unreachable expression code detected
+// Line: 12
+// Compiler options: -warnaserror
+
+public class X
+{
+ static void test (int stop)
+ {
+ int pos = 0;
+ do {
+ break;
+ } while (pos < stop);
+ }
+}
--- /dev/null
+// 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;
+ };
+ }
+ }
+}
--- /dev/null
+// 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;
+ }
+ }
+}
--- /dev/null
+// 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;
+ }
+ }
+}
# Parser problems
cs0080.cs
-cs0162-7.cs NO ERROR
-cs0165-3.cs
-
# Operators
cs0457-2.cs
cs0457.cs
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
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;
}
}
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);
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;
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)
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;
}
}
return null;
}
+ public void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ foreach (var arg in args)
+ arg.FlowAnalysis (fc);
+ }
+
public List<Argument>.Enumerator GetEnumerator ()
{
return args.GetEnumerator ();
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;
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
else
base.EmitStatement (ec);
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ source.FlowAnalysis (fc);
+ }
public bool IsDefaultInitializer {
get {
return base.DoResolve (ec);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ target.FlowAnalysis (fc);
+ source.FlowAnalysis (fc);
+ }
+
protected override Expression ResolveConversions (ResolveContext ec)
{
//
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");
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);
public AwaitStatement (Expression expr, Location loc)
: base (expr, loc)
{
+ unwind_protect = true;
}
#region Properties
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;
}
}
+ 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;
#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;
}
storey.EmitInitializer (ec);
ec.Emit (OpCodes.Ret);
}
+
+ public override void MarkReachable (Reachability rc)
+ {
+ //
+ // Reachability has been done in AsyncInitializerStatement
+ //
+ }
}
class AsyncTaskStorey : StateMachine
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)
//
public class BlockContext : ResolveContext
{
- FlowBranching current_flow_branching;
-
readonly TypeSpec return_type;
//
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
}
//
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.
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)
}
}
- public virtual FlowBranching CurrentBranching {
- get { return null; }
- }
-
//
// The current iterator
//
get { return (flags & Options.ConstantCheckState) != 0; }
}
- public bool DoFlowAnalysis {
- get { return (flags & Options.DoFlowAnalysis) != 0; }
- }
-
public bool IsInProbingMode {
get {
return (flags & Options.ProbingMode) != 0;
public bool IsVariableCapturingRequired {
get {
- return !IsInProbingMode && (CurrentBranching == null || !CurrentBranching.CurrentUsageVector.IsUnreachable);
+ return !IsInProbingMode;
}
}
}
}
- public bool OmitStructFlowAnalysis {
- get { return (flags & Options.OmitStructFlowAnalysis) != 0; }
- }
-
public Report Report {
get {
return Module.Compiler.Report;
#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.
}
| 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
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) {
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;
}
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ arguments.FlowAnalysis (fc);
+ }
+
public static MemberAccess GetBinderNamespace (Location loc)
{
return new MemberAccess (new MemberAccess (
stmt.Emit (ec);
}
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ invoke.FlowAnalysis (fc);
+ }
}
class DynamicConversion : DynamicExpressionStatement, IDynamicBinder
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.
/// </summary>
public abstract class ExpressionStatement : Expression
{
+ public virtual void MarkReachable (Reachability rc)
+ {
+ }
+
public ExpressionStatement ResolveStatement (BlockContext ec)
{
Expression e = Resolve (ec);
child.Emit (ec);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ child.FlowAnalysis (fc);
+ }
+
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
#if STATIC
{
stm.EmitStatement (ec);
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ stm.FlowAnalysis (fc);
+ }
}
readonly Expression expr, orig_expr;
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);
{
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)
+ {
+ }
}
//
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)
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))
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;
}
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
//
// "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)
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)) {
//
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;
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);
}
}
rc.Report.Error (1649, loc, "Members of readonly field `{0}' cannot be passed ref or out (except in a constructor)",
GetSignatureForError ());
}
-
return null;
}
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 ();
DoEmit (ec);
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return false;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement target)
{
// Nothing
public override VariableInfo VariableInfo {
get { return null; }
}
-
- public override void VerifyDefiniteAssignment (ResolveContext rc)
- {
- }
}
///
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;
call.EmitPredefined (ec, oper, arguments, loc);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ arguments.FlowAnalysis (fc);
+ }
+
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
#if STATIC
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
//
EmitCode (ec, false);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ }
+
//
// Converts operator to System.Linq.Expressions.ExpressionType enum name
//
return this;
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ }
+
protected abstract string OperatorName { get; }
protected override void CloneTo (CloneContext clonectx, Expression t)
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
//
}
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ arguments.FlowAnalysis (fc);
+ }
+
public override SLE.Expression MakeExpression (BuilderContext ctx)
{
if (arguments.Count != 2)
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;
true_type.GetSignatureForError (), false_type.GetSignatureForError ());
return null;
}
- }
+ }
+ Constant c = expr as Constant;
if (c != null) {
bool is_false = c.IsDefaultValue;
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;
#region Abstract
public abstract HoistedVariable GetHoistedVariable (AnonymousExpression ae);
public abstract void SetHasAddressTaken ();
- public abstract void VerifyDefiniteAssignment (ResolveContext rc);
public abstract bool IsLockedByStatement { get; set; }
#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 ()
{
local_info.SetIsUsed ();
- VerifyDefiniteAssignment (ec);
-
DoResolveBase (ec);
return this;
}
}
ec.Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
}
- } else if (VariableInfo != null) {
- VariableInfo.SetAssigned (ec);
}
if (eclass == ExprClass.Unresolved)
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)
if (!DoResolveBase (ec))
return null;
- VerifyDefiniteAssignment (ec);
return this;
}
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);
}
}
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 ();
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);
{
throw new InternalErrorException ("Missing Resolve call");
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ throw new InternalErrorException ("Missing Resolve call");
+ }
public override object Accept (StructuralVisitor visitor)
{
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)
#endregion
- public void CheckStructThisDefiniteAssignment (ResolveContext rc)
+ void CheckStructThisDefiniteAssignment (FlowAnalysisContext fc)
{
//
// It's null for all cases when we don't need to check `this'
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)
}
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ CheckStructThisDefiniteAssignment (fc);
+ }
+
public override HoistedVariable GetHoistedVariable (AnonymousExpression ae)
{
if (ae == null)
protected override Expression DoResolve (ResolveContext ec)
{
ResolveBase (ec);
-
- CheckStructThisDefiniteAssignment (ec);
-
return this;
}
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");
{
// Nothing
}
-
- public override void VerifyDefiniteAssignment (ResolveContext rc)
- {
- }
public override object Accept (StructuralVisitor visitor)
{
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;
}
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)
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);
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);
}
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;
}
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)) {
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;
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 ();
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.
//
}
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ // TODO: Check the order
+ base.FlowAnalysis (fc);
+ arguments.FlowAnalysis (fc);
+ }
+
public override string GetSignatureForError ()
{
return best_candidate.GetSignatureForError ();
ec.Emit (OpCodes.Call, method);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ source.FlowAnalysis (fc);
+ }
+
public override string GetSignatureForError ()
{
return TypeManager.CSharpSignature (method);
e.EmitStatement (ec);
}
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ foreach (var initializer in initializers)
+ initializer.FlowAnalysis (fc);
+ }
}
//
return instance;
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ base.FlowAnalysis (fc);
+ initializers.FlowAnalysis (fc);
+ }
+
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
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.
//
// 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 ());
}
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;
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);
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);
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])
// 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)
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;
}
}
}
protected bool unwind_protect;
protected T machine_initializer;
int resume_pc;
+ ExceptionStatement inside_try_block;
protected YieldStatement (Expression expr, Location l)
{
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);
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>
{
}
- 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;
}
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;
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)
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)
{
if (new_storey != null)
new_storey = Convert.ImplicitConversionRequired (ec, new_storey, host_method.MemberType, loc);
- ec.CurrentBranching.CurrentUsageVector.Goto ();
return true;
}
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)
{
}
var m = new GetEnumeratorMethod (host, returnType, name);
var stmt = statement ?? new GetEnumeratorStatement (host, m);
m.block.AddStatement (stmt);
- m.block.IsCompilerGenerated = true;
return m;
}
}
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;
}
}
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);
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)
{
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)
// 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;
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);
}
throw new NotSupportedException ();
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ throw new NotSupportedException ();
+ }
+
protected override void DoEmit (EmitContext ec)
{
//
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
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;
}
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)
{
Emit (ec);
}
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ if (argument_list != null)
+ argument_list.FlowAnalysis (fc);
+ }
}
public class ConstructorBaseInitializer : ConstructorInitializer {
}
}
- 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);
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);
return uw != null && expr.Equals (uw.expr);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ }
+
public Expression Original {
get {
return expr;
ec.MarkLabel (end_label);
}
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ }
+
public void AddressOf (EmitContext ec, AddressOp mode)
{
unwrap.AddressOf (ec, mode);
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);
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;
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
return true;
}
- /// <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>
{
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
{
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;
}
throw new NotSupportedException ();
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return false;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement target)
{
// nothing needed.
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)
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;
}
bool branch_emitted = false;
end = ec.DefineLabel ();
- if (!is_true_ret){
+ if (!true_returns){
ec.Emit (OpCodes.Br, end);
branch_emitted = true;
}
}
}
+ 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;
}
}
- 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;
}
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;
}
ec.LoopEnd = ec.DefineLabel ();
ec.MarkLabel (loop);
- EmbeddedStatement.Emit (ec);
+ Statement.Emit (ec);
ec.MarkLabel (ec.LoopBegin);
// Mark start of while condition
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);
}
{
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;
}
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;
{
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;
}
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)
{
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
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)
throw new NotSupportedException ();
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return false;
+ }
+
protected override void CloneTo (CloneContext clonectx, Statement target)
{
var t = (StatementErrorExpression) target;
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;
}
}
- // 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;
}
}
}
}
+ 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;
//
}
expr = expr.Resolve (ec);
- TypeSpec block_return_type = ec.ReturnType;
AnonymousExpression am = ec.CurrentAnonymousMethod;
if (am == null) {
ec.EmitEpilogue ();
}
- ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
+ ec.Emit (OpCodes.Leave, async_body.BodyEnd);
return;
}
}
}
+ 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;
}
}
- 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;
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)
{
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);
}
string name;
bool defined;
bool referenced;
+ bool finalTarget;
Label label;
Block block;
-
- FlowBranching.UsageVector vectors;
public LabeledStatement (string name, Block block, Location l)
{
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);
/// <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)
/// <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;
}
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)
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;
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;
}
}
+ 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;
}
}
- 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)
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;
}
}
}
}
+ 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;
//
// The information about a user-perceived local variable
//
- public class LocalVariable : INamedBlockVariable, ILocalVariable
+ public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
{
[Flags]
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
}
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)
public enum Flags
{
Unchecked = 1,
- HasRet = 8,
+ ReachableEnd = 8,
Unsafe = 16,
HasCapturedVariable = 64,
HasCapturedThis = 1 << 7,
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;
#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;
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; }
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++){
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 ()
{
}
}
+ 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;
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);
}
}
}
}
+ public void SetCatchBlock ()
+ {
+ flags |= Flags.CatchBlock;
+ }
+
+ public void SetFinallyBlock ()
+ {
+ flags |= Flags.FinallyBlock;
+ }
+
public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
{
tryBlock.statements = statements;
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)
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 ();
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;
#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)
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) {
}
}
- 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;
}
}
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;
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;
}
}
{
}
- 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 ();
}
{
this.compiler = source.TopBlock.compiler;
top_block = this;
- flags |= Flags.HasRet;
}
public bool IsIterator {
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) {
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)
// 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);
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;
return converted;
}
set {
- converted = value;
+ converted = value;
}
}
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;
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)
{
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>
throw new NotImplementedException ();
}
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ return false;
+ }
+
protected override void DoEmit (EmitContext ec)
{
body.EmitDispatch (ec);
List<SwitchLabel> case_labels;
List<Tuple<GotoCase, Constant>> goto_cases;
+ List<DefiniteAssignmentBitSet> end_reachable_das;
/// <summary>
/// The governing switch type
ExpressionStatement string_dictionary;
FieldExpr switch_cache_field;
ExplicitBlock block;
+ bool end_reachable;
//
// Nullable Types support
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;
}
}
+ 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
};
}
- public void RegisterLabel (ResolveContext rc, SwitchLabel sl)
+ public void RegisterLabel (BlockContext rc, SwitchLabel sl)
{
case_labels.Add (sl);
}
}
- 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)
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;
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;
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)
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 ();
protected override void DoEmit (EmitContext ec)
{
- // Workaround broken flow-analysis
- block.HasUnreachableClosingBrace = true;
-
//
// Setup the codegen context
//
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;
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
}
}
- return base.Resolve (bc);
+ return base.Resolve (bc) && ok;
}
}
//
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)
{
}
}
- 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;
throw new InternalErrorException ("missed an intervening AddResumePoint?");
resume_points.Add (stmt);
+ return pc;
}
-
}
public class Lock : TryFinallyBlock
}
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;
}
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;
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;
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;
}
public abstract void EmitExit (EmitContext ec);
+
+ public override void FlowAnalysis (FlowAnalysisContext fc)
+ {
+ expr.FlowAnalysis (fc);
+ }
}
class ExpressionEmitter : Emitter {
#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)
}
}
+ 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;
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;
#region Properties
- public Block Block {
+ public ExplicitBlock Block {
get {
return block;
}
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)
}
}
+ 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;
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;
}
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)
}
}
- 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) {
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;
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;
}
}
- ec.EndFlowBranching ();
-
- return base.Resolve (ec) && ok;
+ return base.Resolve (bc) && ok;
}
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;
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;
}
}
- ec.StartFlowBranching (this);
-
- stmt.Resolve (ec);
-
- ec.EndFlowBranching ();
+ base.Resolve (ec);
if (vr != null)
vr.IsLockedByStatement = vr_locked;
- base.Resolve (ec);
-
return true;
}
/// <summary>
/// Implementation of the foreach C# statement
/// </summary>
- public class Foreach : Statement
+ public class Foreach : LoopStatement
{
abstract class IteratorStatement : Statement
{
base.Emit (ec);
}
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ throw new NotImplementedException ();
+ }
}
sealed class ArrayForeach : IteratorStatement
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)
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;
}
get { return expr; }
}
- public Statement Statement {
- get { return statement; }
- }
-
public Expression TypeExpression {
get { return type; }
}
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);
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}'",
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)
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;
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)
# csXXXX.cs IGNORE : adds test to ignore list
gtest-230.cs
-test-872.cs
+using System;
+
class Foo {
public static int Main ()
{
f ();
return 1;
} catch {
- return 0;
}
+
+ try {
+ f2 ();
+ return 2;
+ } catch (ApplicationException) {
+ }
+
+ return 0;
}
+
static void f ()
{
try {
skip:
;
}
+
+ static void f2 ()
+ {
+ try {
+ goto FinallyExit;
+ } finally {
+ throw new ApplicationException ();
+ }
+ FinallyExit:
+ Console.WriteLine ("Too late");
+ }
}
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;
{
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;
+ }
}
--- /dev/null
+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
--- /dev/null
+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);
+ }
+}
<size>10</size>\r
</method>\r
<method name="Int32 test40(Int32)" attrs="145">\r
- <size>27</size>\r
+ <size>20</size>\r
</method>\r
<method name="Void .ctor()" attrs="6278">\r
<size>7</size>\r
<test name="test-519.cs">\r
<type name="Foo">\r
<method name="Int32 Main()" attrs="150">\r
- <size>25</size>\r
+ <size>52</size>\r
</method>\r
<method name="Void f()" attrs="145">\r
- <size>21</size>\r
+ <size>23</size>\r
</method>\r
<method name="Void .ctor()" attrs="6278">\r
<size>7</size>\r
</method>\r
+ <method name="Void f2()" attrs="145">\r
+ <size>16</size>\r
+ </method>\r
</type>\r
</test>\r
<test name="test-52.cs">\r
<test name="test-579.cs">\r
<type name="TestCase">\r
<method name="Int32 Main()" attrs="150">\r
- <size>49</size>\r
+ <size>44</size>\r
</method>\r
<method name="Void .ctor()" attrs="6278">\r
<size>7</size>\r
</method>\r
+ <method name="Int32 Test1()" attrs="145">\r
+ <size>49</size>\r
+ </method>\r
+ <method name="Int32 Test2()" attrs="145">\r
+ <size>48</size>\r
+ </method>\r
+ <method name="Int32 Test3()" attrs="145">\r
+ <size>47</size>\r
+ </method>\r
</type>\r
</test>\r
<test name="test-58.cs">\r
<test name="test-634.cs">\r
<type name="Test">\r
<method name="Void TestFunc()" attrs="150">\r
- <size>7</size>\r
+ <size>13</size>\r
</method>\r
<method name="Void Main(System.String[])" attrs="150">\r
<size>7</size>\r
<size>0</size>\r
</method>\r
</type>\r
+ <type name="Test+<TestFunc>c__AnonStorey0">\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
</test>\r
<test name="test-635.cs">\r
<type name="ShortCircuitFold">\r
</method>\r
</type>\r
</test>\r
+ <test name="test-872.cs">\r
+ <type name="X">\r
+ <method name="Void Main()" attrs="150">\r
+ <size>66</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ </test>\r
<test name="test-873.cs">\r
<type name="Program">\r
<method name="Int32 Main()" attrs="145">\r
</method>\r
</type>\r
</test>\r
+ <test name="test-874.cs">\r
+ <type name="X">\r
+ <method name="Void Main()" attrs="150">\r
+ <size>30</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ </test>\r
<test name="test-875.cs">\r
<type name="Test">\r
<method name="Void Main()" attrs="150">\r
</method>\r
</type>\r
</test>\r
+ <test name="test-876.cs">\r
+ <type name="T">\r
+ <method name="Int32 Main()" attrs="150">\r
+ <size>34</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ <method name="Void Test1()" attrs="145">\r
+ <size>57</size>\r
+ </method>\r
+ <method name="Void Test2()" attrs="145">\r
+ <size>50</size>\r
+ </method>\r
+ <method name="UInt32 Test3(Int32, UInt32)" attrs="145">\r
+ <size>60</size>\r
+ </method>\r
+ <method name="Void Test4()" attrs="145">\r
+ <size>25</size>\r
+ </method>\r
+ </type>\r
+ </test>\r
<test name="test-88.cs">\r
<type name="X">\r
<method name="Void f(System.String)" attrs="145">\r
</type>\r
<type name="Program+<Main>c__AnonStorey1+<Main>c__async0">\r
<method name="Void MoveNext()" attrs="486">\r
- <size>226</size>\r
+ <size>225</size>\r
</method>\r
</type>\r
<type name="Program+<Main>c__AnonStorey1+<Main>c__async2">\r
<method name="Void MoveNext()" attrs="486">\r
- <size>226</size>\r
+ <size>225</size>\r
</method>\r
</type>\r
<type name="Program+<Main>c__AnonStorey1+<Main>c__async3">\r
</type>\r
<type name="AsyncTypeInference+<Main>c__async8">\r
<method name="Void MoveNext()" attrs="486">\r
- <size>157</size>\r
+ <size>156</size>\r
</method>\r
</type>\r
<type name="AsyncTypeInference+<Main>c__asyncB">\r
<method name="Void MoveNext()" attrs="486">\r
- <size>38</size>\r
+ <size>37</size>\r
</method>\r
</type>\r
<type name="AsyncTypeInference+<Main>c__async2">\r
</type>\r
<type name="Program+<Main>c__async1">\r
<method name="Void MoveNext()" attrs="486">\r
- <size>197</size>\r
+ <size>196</size>\r
</method>\r
<method name="Void SetStateMachine(IAsyncStateMachine)" attrs="486">\r
<size>13</size>\r
</type>\r
<type name="C+<Foo>c__async3">\r
<method name="Void MoveNext()" attrs="486">\r
- <size>169</size>\r
+ <size>168</size>\r
</method>\r
<method name="Void SetStateMachine(IAsyncStateMachine)" attrs="486">\r
<size>13</size>\r
</type>\r
<type name="C+<Foo>c__async0+<Foo>c__AnonStorey4+<Foo>c__async3">\r
<method name="Void MoveNext()" attrs="486">\r
- <size>185</size>\r
+ <size>184</size>\r
</method>\r
<method name="Void SetStateMachine(IAsyncStateMachine)" attrs="486">\r
<size>13</size>\r
</type>\r
<type name="C+<Foo>c__async0+<Foo>c__AnonStorey4+<Foo>c__async3">\r
<method name="Void MoveNext()" attrs="486">\r
- <size>214</size>\r
+ <size>213</size>\r
</method>\r
<method name="Void SetStateMachine(IAsyncStateMachine)" attrs="486">\r
<size>13</size>\r
</type>\r
<type name="C+<Test>c__async0">\r
<method name="Void MoveNext()" attrs="486">\r
- <size>61</size>\r
+ <size>60</size>\r
</method>\r
<method name="Void SetStateMachine(IAsyncStateMachine)" attrs="486">\r
<size>13</size>\r
</type>\r
<type name="X+<Foo>c__AnonStorey1+<Foo>c__async0">\r
<method name="Void MoveNext()" attrs="486">\r
- <size>44</size>\r
+ <size>43</size>\r
</method>\r
<method name="Void SetStateMachine(IAsyncStateMachine)" attrs="486">\r
<size>13</size>\r