X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fflowanalysis.cs;h=c9f58f9fc1b1e3267e5bbe16be1056193bfeb053;hb=844983bf87468ee3fc786ce8a6f8c7aeedb1f09a;hp=528c0fb506e6f915f7002e7cc764b56ef92660c8;hpb=769a3cfe4be5f4217bd57785501af28073bd7b1c;p=mono.git diff --git a/mcs/mcs/flowanalysis.cs b/mcs/mcs/flowanalysis.cs index 528c0fb506e..c9f58f9fc1b 100644 --- a/mcs/mcs/flowanalysis.cs +++ b/mcs/mcs/flowanalysis.cs @@ -5,7 +5,8 @@ // Martin Baulig (martin@ximian.com) // Raja R Harinath (rharinath@novell.com) // -// (C) 2001, 2002, 2003 Ximian, Inc. +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2003-2008 Novell, Inc. // using System; @@ -17,13 +18,6 @@ using System.Diagnostics; namespace Mono.CSharp { - public enum TriState : byte { - // Never < Sometimes < Always - Never, - Sometimes, - Always - } - // // 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. @@ -49,17 +43,20 @@ namespace Mono.CSharp // part of a block headed by a jump target Labeled, - // Try/Catch block. + // TryCatch block. + TryCatch, + + // TryFinally, Using, Lock, CollectionForeach Exception, // Switch block. Switch, - // Switch section. - SwitchSection, - // The toplevel block of a function - Toplevel + Toplevel, + + // An iterator block + Iterator } // @@ -74,147 +71,18 @@ namespace Mono.CSharp Finally } - public sealed class Reachability - { - TriState returns, throws, barrier; - - public TriState Returns { - get { return returns; } - } - public TriState Throws { - get { return throws; } - } - public TriState Barrier { - get { return barrier; } - } - - Reachability (TriState returns, TriState throws, TriState barrier) - { - this.returns = returns; - this.throws = throws; - this.barrier = barrier; - } - - public Reachability Clone () - { - return new Reachability (returns, throws, barrier); - } - - public static TriState TriState_Meet (TriState a, TriState b) - { - // (1) if both are Never, return Never - // (2) if both are Always, return Always - // (3) otherwise, return Sometimes - // note that (3) => (3') if both are Sometimes, return Sometimes - return a == b ? a : TriState.Sometimes; - } - - public static TriState TriState_Max (TriState a, TriState b) - { - return ((byte) a > (byte) b) ? a : b; - } - - public void Meet (Reachability b) - { - if ((AlwaysReturns && b.AlwaysHasBarrier) || (AlwaysHasBarrier && b.AlwaysReturns)) - returns = TriState.Always; - else - returns = TriState_Meet (returns, b.returns); - - throws = TriState_Meet (throws, b.throws); - barrier = TriState_Meet (barrier, b.barrier); - } - - public void Or (Reachability b) - { - returns = TriState_Max (returns, b.returns); - throws = TriState_Max (throws, b.throws); - barrier = TriState_Max (barrier, b.barrier); - } - - public static Reachability Always () - { - return new Reachability (TriState.Never, TriState.Never, TriState.Never); - } - - TriState Unreachable { - get { return TriState_Max (returns, TriState_Max (throws, barrier)); } - } - - TriState Reachable { - get { - TriState unreachable = Unreachable; - if (unreachable == TriState.Sometimes) - return TriState.Sometimes; - return unreachable == TriState.Always ? TriState.Never : TriState.Always; - } - } - - public bool AlwaysReturns { - get { return returns == TriState.Always; } - } - - public bool AlwaysThrows { - get { return throws == TriState.Always; } - } - - public bool AlwaysHasBarrier { - get { return barrier == TriState.Always; } - } - - public bool IsUnreachable { - get { return Unreachable == TriState.Always; } - } - - public void SetReturns () - { - returns = TriState.Always; - } - - public void SetThrows () - { - throws = TriState.Always; - } - - public void SetBarrier () - { - barrier = TriState.Always; - } - - static string ShortName (TriState returns) - { - switch (returns) { - case TriState.Never: - return "N"; - case TriState.Sometimes: - return "S"; - default: - return "A"; - } - } - - public override string ToString () - { - return String.Format ("[{0}:{1}:{2}:{3}]", - ShortName (returns), ShortName (throws), ShortName (barrier), - ShortName (Reachable)); - } - } - 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.SwitchSection: - return new FlowBranchingBlock (parent, type, SiblingType.Block, block, loc); - case BranchingType.Block: return new FlowBranchingBlock (parent, type, SiblingType.Block, block, loc); @@ -250,8 +118,6 @@ namespace Mono.CSharp // public readonly Location Location; - protected VariableMap param_map, local_map; - static int next_id = 0; int id; @@ -275,11 +141,6 @@ namespace Mono.CSharp // public readonly Block Block; - // - // The number of parameters in this block. - // - public readonly int CountParameters; - // // The number of locals in this block. // @@ -300,8 +161,8 @@ namespace Mono.CSharp // // Private. // - MyBitVector locals, parameters; - Reachability reachability; + MyBitVector locals; + bool is_unreachable; static int next_id = 0; int id; @@ -309,43 +170,41 @@ namespace Mono.CSharp // // Normally, you should not use any of these constructors. // - public UsageVector (SiblingType type, UsageVector parent, Block block, Location loc, int num_params, int num_locals) + 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.CountParameters = num_params; this.CountLocals = num_locals; locals = num_locals == 0 ? MyBitVector.Empty : new MyBitVector (parent == null ? MyBitVector.Empty : parent.locals, num_locals); - parameters = num_params == 0 - ? MyBitVector.Empty - : new MyBitVector (parent == null ? MyBitVector.Empty : parent.parameters, num_params); - - reachability = parent == null ? Reachability.Always () : parent.Reachability.Clone (); + 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.CountParameters, parent.CountLocals) + : this (type, parent, block, loc, parent.CountLocals) { } - public UsageVector (MyBitVector parameters, MyBitVector locals, Reachability reachability, Block block, Location loc) + private UsageVector (MyBitVector locals, bool is_unreachable, Block block, Location loc) { this.Type = SiblingType.Block; this.Location = loc; this.Block = block; - this.reachability = reachability; - this.parameters = parameters; + this.is_unreachable = is_unreachable; + this.locals = locals; id = ++next_id; + } // @@ -353,69 +212,58 @@ namespace Mono.CSharp // public UsageVector Clone () { - UsageVector retval = new UsageVector (Type, null, Block, Location, CountParameters, CountLocals); + UsageVector retval = new UsageVector (Type, null, Block, Location, CountLocals); retval.locals = locals.Clone (); - retval.parameters = parameters.Clone (); - retval.reachability = reachability.Clone (); + retval.is_unreachable = is_unreachable; return retval; } public bool IsAssigned (VariableInfo var, bool ignoreReachability) { - if (!ignoreReachability && !var.IsParameter && Reachability.IsUnreachable) + if (!ignoreReachability && !var.IsParameter && IsUnreachable) return true; - return var.IsAssigned (var.IsParameter ? parameters : locals); + return var.IsAssigned (locals); } public void SetAssigned (VariableInfo var) { - if (!var.IsParameter && Reachability.IsUnreachable) + if (!var.IsParameter && IsUnreachable) return; - var.SetAssigned (var.IsParameter ? parameters : locals); + var.SetAssigned (locals); } public bool IsFieldAssigned (VariableInfo var, string name) { - if (!var.IsParameter && Reachability.IsUnreachable) + if (!var.IsParameter && IsUnreachable) return true; - return var.IsFieldAssigned (var.IsParameter ? parameters : locals, name); + return var.IsFieldAssigned (locals, name); } public void SetFieldAssigned (VariableInfo var, string name) { - if (!var.IsParameter && Reachability.IsUnreachable) + if (!var.IsParameter && IsUnreachable) return; - var.SetFieldAssigned (var.IsParameter ? parameters : locals, name); - } - - public Reachability Reachability { - get { return reachability; } + var.SetFieldAssigned (locals, name); } - public void Return () - { - if (!reachability.IsUnreachable) - reachability.SetReturns (); + public bool IsUnreachable { + get { return is_unreachable; } } - public void Throw () + public void ResetBarrier () { - if (!reachability.IsUnreachable) { - reachability.SetThrows (); - reachability.SetBarrier (); - } + is_unreachable = false; } public void Goto () { - if (!reachability.IsUnreachable) - reachability.SetBarrier (); + is_unreachable = true; } public static UsageVector MergeSiblings (UsageVector sibling_list, Location loc) @@ -424,72 +272,19 @@ namespace Mono.CSharp return sibling_list; MyBitVector locals = null; - MyBitVector parameters = null; - Reachability reachability = null; - - for (UsageVector child = sibling_list; child != null; child = child.Next) { - Report.Debug (2, " MERGING SIBLING ", reachability, child); + bool is_unreachable = sibling_list.is_unreachable; - if (reachability == null) - reachability = child.Reachability.Clone (); - else - reachability.Meet (child.Reachability); - - // A local variable is initialized after a flow branching if it - // has been initialized in all its branches which do neither - // always return or always throw an exception. - // - // If a branch may return, but does not always return, then we - // can treat it like a never-returning branch here: control will - // only reach the code position after the branching if we did not - // return here. - // - // It's important to distinguish between always and sometimes - // returning branches here: - // - // 1 int a; - // 2 if (something) { - // 3 return; - // 4 a = 5; - // 5 } - // 6 Console.WriteLine (a); - // - // The if block in lines 3-4 always returns, so we must not look - // at the initialization of `a' in line 4 - thus it'll still be - // uninitialized in line 6. - // - // On the other hand, the following is allowed: - // - // 1 int a; - // 2 if (something) - // 3 a = 5; - // 4 else - // 5 return; - // 6 Console.WriteLine (a); - // - // Here, `a' is initialized in line 3 and we must not look at - // line 5 since it always returns. - // - bool unreachable = child.Reachability.IsUnreachable; + if (!sibling_list.IsUnreachable) + locals &= sibling_list.locals; - Report.Debug (2, " MERGING SIBLING #1", reachability, - child.Type, child.Reachability.IsUnreachable, unreachable); + for (UsageVector child = sibling_list.Next; child != null; child = child.Next) { + is_unreachable &= child.is_unreachable; - if (!unreachable) + if (!child.IsUnreachable) locals &= child.locals; - - // An `out' parameter must be assigned in all branches which do - // not always throw an exception. - if (!child.Reachability.AlwaysThrows) - parameters &= child.parameters; - - Report.Debug (2, " MERGING SIBLING #2", parameters, locals); } - - if (reachability == null) - throw new InternalErrorException ("Cannot happen: the loop above runs at least twice"); - return new UsageVector (parameters, locals, reachability, null, loc); + return new UsageVector (locals, is_unreachable, null, loc); } // @@ -497,9 +292,9 @@ namespace Mono.CSharp // public UsageVector MergeChild (UsageVector child, bool overwrite) { - Report.Debug (2, " MERGING CHILD EFFECTS", this, child, reachability, Type); + Report.Debug (2, " MERGING CHILD EFFECTS", this, child, Type); - Reachability new_r = child.Reachability; + bool new_isunr = child.is_unreachable; // // We've now either reached the point after the branching or we will @@ -510,7 +305,7 @@ namespace Mono.CSharp // we need to look at (see above). // - if ((Type == SiblingType.SwitchSection) && !new_r.IsUnreachable) { + if ((Type == SiblingType.SwitchSection) && !new_isunr) { Report.Error (163, Location, "Control cannot fall through from one " + "case label to another"); @@ -518,12 +313,15 @@ namespace Mono.CSharp } locals |= child.locals; - parameters |= child.parameters; + + // throw away un-necessary information about variables in child blocks + if (locals.Count != CountLocals) + locals = new MyBitVector (locals, CountLocals); if (overwrite) - reachability = new_r.Clone (); + is_unreachable = new_isunr; else - reachability.Or (new_r); + is_unreachable |= new_isunr; return child; } @@ -535,20 +333,15 @@ namespace Mono.CSharp if (o_vectors == null) return; - if (reachability.IsUnreachable) { - if (locals != null) - locals.SetAll (true); - if (parameters != null) - parameters.SetAll (true); - } + 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.Reachability.IsUnreachable) + if (vector.IsUnreachable) continue; locals &= vector.locals; - parameters &= vector.parameters; - reachability.Meet (vector.Reachability); + is_unreachable &= vector.is_unreachable; } Report.Debug (1, " MERGING BREAK ORIGINS DONE", this); @@ -560,7 +353,7 @@ namespace Mono.CSharp public override string ToString () { - return String.Format ("Vector ({0},{1},{2}-{3}-{4})", Type, id, reachability, parameters, locals); + return String.Format ("Vector ({0},{1},{2}-{3})", Type, id, is_unreachable, locals); } } @@ -581,18 +374,10 @@ namespace Mono.CSharp UsageVector vector; if (Block != null) { - param_map = Block.ParameterMap; - local_map = Block.LocalMap; - UsageVector parent_vector = parent != null ? parent.CurrentUsageVector : null; - vector = new UsageVector ( - stype, parent_vector, Block, loc, - param_map.Length, local_map.Length); + vector = new UsageVector (stype, parent_vector, Block, loc, Block.AssignableSlots); } else { - param_map = Parent.param_map; - local_map = Parent.local_map; - vector = new UsageVector ( - stype, Parent.CurrentUsageVector, null, loc); + vector = new UsageVector (stype, Parent.CurrentUsageVector, null, loc); } AddSibling (vector); @@ -623,22 +408,19 @@ namespace Mono.CSharp protected abstract UsageVector Merge (); - // - // Merge a child branching. - // public UsageVector MergeChild (FlowBranching child) { - bool overwrite = child.Type == BranchingType.Labeled || - (child.Type == BranchingType.Block && child.Block.Implicit); - Report.Debug (2, " MERGING CHILD", this, child); - UsageVector result = CurrentUsageVector.MergeChild (child.Merge (), overwrite); - Report.Debug (2, " MERGING CHILD DONE", this, result); - return result; + return CurrentUsageVector.MergeChild (child.Merge (), true); } - public virtual bool InTryWithCatch () + public virtual bool CheckRethrow (Location loc) { - return Parent.InTryWithCatch (); + return Parent.CheckRethrow (loc); + } + + public virtual bool AddResumePoint (ResumableStatement stmt, Location loc, out int pc) + { + return Parent.AddResumePoint (stmt, loc, out pc); } // returns true if we crossed an unwind-protected region (try/catch/finally, lock, using, ...) @@ -654,9 +436,9 @@ namespace Mono.CSharp } // returns true if we crossed an unwind-protected region (try/catch/finally, lock, using, ...) - public virtual bool AddReturnOrigin (UsageVector vector, Location loc) + public virtual bool AddReturnOrigin (UsageVector vector, ExitStatement stmt) { - return Parent.AddReturnOrigin (vector, loc); + return Parent.AddReturnOrigin (vector, stmt); } // returns true if we crossed an unwind-protected region (try/catch/finally, lock, using, ...) @@ -665,11 +447,6 @@ namespace Mono.CSharp return Parent.AddGotoOrigin (vector, goto_stmt); } - public virtual void StealFinallyClauses (ref ArrayList list) - { - Parent.StealFinallyClauses (ref list); - } - public bool IsAssigned (VariableInfo vi) { return CurrentUsageVector.IsAssigned (vi, false); @@ -680,6 +457,10 @@ namespace Mono.CSharp 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); @@ -733,6 +514,8 @@ namespace Mono.CSharp 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; } @@ -748,6 +531,12 @@ namespace Mono.CSharp 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 () { @@ -819,7 +608,7 @@ namespace Mono.CSharp actual = CurrentUsageVector.Clone (); // stand-in for backward jumps - CurrentUsageVector.Reachability.Meet (Reachability.Always ()); + CurrentUsageVector.ResetBarrier (); } public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt) @@ -838,7 +627,7 @@ namespace Mono.CSharp { UsageVector vector = base.Merge (); - if (actual.Reachability.IsUnreachable) + if (actual.IsUnreachable) Report.Warning (162, 2, stmt.loc, "Unreachable code detected"); actual.MergeChild (vector, false); @@ -846,6 +635,22 @@ namespace Mono.CSharp } } + public class FlowBranchingIterator : FlowBranchingBlock + { + Iterator iterator; + public FlowBranchingIterator (FlowBranching parent, Iterator iterator) + : base (parent, BranchingType.Iterator, SiblingType.Block, null, iterator.Location) + { + this.iterator = iterator; + } + + public override bool AddResumePoint (ResumableStatement stmt, Location loc, out int pc) + { + pc = iterator.AddResumePoint (stmt); + return false; + } + } + public class FlowBranchingToplevel : FlowBranchingBlock { UsageVector return_origins; @@ -855,30 +660,15 @@ namespace Mono.CSharp { } - // - // Check whether all `out' parameters have been assigned. - // - void CheckOutParameters (UsageVector vector, Location loc) + public override bool CheckRethrow (Location loc) { - if (vector.Reachability.IsUnreachable) - return; - for (int i = 0; i < param_map.Count; i++) { - VariableInfo var = param_map [i]; - - if (var == null) - continue; - - if (vector.IsAssigned (var, false)) - continue; - - Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method", - var.Name); - } + Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause"); + return false; } - public override bool InTryWithCatch () + public override bool AddResumePoint (ResumableStatement stmt, Location loc, out int pc) { - return false; + throw new InternalErrorException ("A yield in a non-iterator block"); } public override bool AddBreakOrigin (UsageVector vector, Location loc) @@ -893,20 +683,15 @@ namespace Mono.CSharp return false; } - public override bool AddReturnOrigin (UsageVector vector, Location loc) + public override bool AddReturnOrigin (UsageVector vector, ExitStatement stmt) { vector = vector.Clone (); - vector.Location = loc; + vector.Location = stmt.loc; vector.Next = return_origins; return_origins = vector; return false; } - public override void StealFinallyClauses (ref ArrayList list) - { - // nothing to do - } - public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt) { string name = goto_stmt.Target; @@ -915,7 +700,7 @@ namespace Mono.CSharp throw new InternalErrorException ("Shouldn't get here"); if (Parent == null) { - Report.Error (159, goto_stmt.loc, "No such label `{0}' in this scope", name); + Error_UnknownLabel (goto_stmt.loc, name, Report); return false; } @@ -929,17 +714,72 @@ namespace Mono.CSharp protected override UsageVector Merge () { for (UsageVector origin = return_origins; origin != null; origin = origin.Next) - CheckOutParameters (origin, origin.Location); + Block.Toplevel.CheckOutParameters (origin, origin.Location); UsageVector vector = base.Merge (); - CheckOutParameters (vector, Block.loc); + Block.Toplevel.CheckOutParameters (vector, Block.loc); // Note: we _do_not_ merge in the return origins return vector; } - public Reachability End () + public bool End () { - return Merge ().Reachability; + return Merge ().IsUnreachable; + } + } + + public class FlowBranchingTryCatch : FlowBranchingBlock + { + TryCatch stmt; + public FlowBranchingTryCatch (FlowBranching parent, TryCatch stmt) + : base (parent, BranchingType.Block, SiblingType.Try, null, stmt.loc) + { + this.stmt = stmt; + } + + public override bool CheckRethrow (Location loc) + { + return CurrentUsageVector.Next != null || Parent.CheckRethrow (loc); + } + + public override bool AddResumePoint (ResumableStatement stmt, Location loc, out int pc) + { + int errors = Report.Errors; + Parent.AddResumePoint (stmt, loc, out pc); + if (errors == Report.Errors) { + if (CurrentUsageVector.Next == null) + Report.Error (1626, loc, "Cannot yield a value in the body of a try block with a catch clause"); + else + Report.Error (1631, 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); + stmt.SomeCodeFollows (); + return true; + } + + public override bool AddContinueOrigin (UsageVector vector, Location loc) + { + Parent.AddContinueOrigin (vector, loc); + stmt.SomeCodeFollows (); + return true; + } + + public override bool AddReturnOrigin (UsageVector vector, ExitStatement exit_stmt) + { + Parent.AddReturnOrigin (vector, exit_stmt); + stmt.SomeCodeFollows (); + return true; + } + + public override bool AddGotoOrigin (UsageVector vector, Goto goto_stmt) + { + Parent.AddGotoOrigin (vector, goto_stmt); + return true; } } @@ -947,28 +787,87 @@ namespace Mono.CSharp { ExceptionStatement stmt; UsageVector current_vector; - UsageVector catch_vectors; + UsageVector try_vector; UsageVector finally_vector; - UsageVector break_origins; - UsageVector continue_origins; - UsageVector return_origins; - GotoOrigin goto_origins; - - class GotoOrigin { - public GotoOrigin Next; - public Goto GotoStmt; - public UsageVector Vector; + abstract class SavedOrigin { + public readonly SavedOrigin Next; + public readonly UsageVector Vector; - public GotoOrigin (UsageVector vector, Goto goto_stmt, GotoOrigin next) + protected SavedOrigin (SavedOrigin next, UsageVector vector) { - Vector = vector; - GotoStmt = goto_stmt; 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); } } - bool emit_finally; + 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 FlowBranchingException (FlowBranching parent, ExceptionStatement stmt) @@ -976,16 +875,13 @@ namespace Mono.CSharp null, stmt.loc) { this.stmt = stmt; - this.emit_finally = true; } protected override void AddSibling (UsageVector sibling) { switch (sibling.Type) { case SiblingType.Try: - case SiblingType.Catch: - sibling.Next = catch_vectors; - catch_vectors = sibling; + try_vector = sibling; break; case SiblingType.Finally: finally_vector = sibling; @@ -1000,65 +896,74 @@ namespace Mono.CSharp get { return current_vector; } } - public override bool InTryWithCatch () + public override bool CheckRethrow (Location loc) { - if (finally_vector == null) { - Try t = stmt as Try; - if (t != null && t.HasCatch) - return true; - } + 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; + } - return base.InTryWithCatch (); + public override bool AddResumePoint (ResumableStatement stmt, Location loc, out int pc) + { + int errors = Report.Errors; + Parent.AddResumePoint (this.stmt, loc, out pc); + if (errors == Report.Errors) { + if (finally_vector == null) + this.stmt.AddResumePoint (stmt, pc); + else + Report.Error (1625, loc, "Cannot yield in the body of a finally clause"); + } + return true; } public override bool AddBreakOrigin (UsageVector vector, Location loc) { - vector = vector.Clone (); if (finally_vector != null) { - vector.MergeChild (finally_vector, false); 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 { - vector.Location = loc; - vector.Next = break_origins; - break_origins = vector; + 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) { - vector = vector.Clone (); if (finally_vector != null) { - vector.MergeChild (finally_vector, false); 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 { - vector.Location = loc; - vector.Next = continue_origins; - continue_origins = vector; + 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, Location loc) + public override bool AddReturnOrigin (UsageVector vector, ExitStatement exit_stmt) { - vector = vector.Clone (); if (finally_vector != null) { - vector.MergeChild (finally_vector, false); int errors = Report.Errors; - Parent.AddReturnOrigin (vector, loc); + Parent.AddReturnOrigin (vector, exit_stmt); if (errors == Report.Errors) - Report.Error (157, loc, "Control cannot leave the body of a finally clause"); + exit_stmt.Error_FinallyClause (Report); } else { - vector.Location = loc; - vector.Next = return_origins; - return_origins = vector; + saved_origins = new ReturnOrigin (saved_origins, vector, exit_stmt); } + + // sets ec.NeedReturnLabel() + stmt.SomeCodeFollows (); return true; } @@ -1068,64 +973,26 @@ namespace Mono.CSharp if (s != null) throw new InternalErrorException ("Shouldn't get here"); - vector = vector.Clone (); if (finally_vector != null) { - vector.MergeChild (finally_vector, false); 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 { - goto_origins = new GotoOrigin (vector, goto_stmt, goto_origins); + saved_origins = new GotoOrigin (saved_origins, vector, goto_stmt); } return true; } - public override void StealFinallyClauses (ref ArrayList list) - { - if (list == null) - list = new ArrayList (); - list.Add (stmt); - emit_finally = false; - base.StealFinallyClauses (ref list); - } - - public bool EmitFinally { - get { return emit_finally; } - } - protected override UsageVector Merge () { - Report.Debug (2, " MERGING TRY/CATCH", Name); - UsageVector vector = UsageVector.MergeSiblings (catch_vectors, Location); - Report.Debug (2, " MERGING TRY/CATCH DONE", vector); + UsageVector vector = try_vector.Clone (); if (finally_vector != null) vector.MergeChild (finally_vector, false); - for (UsageVector origin = break_origins; origin != null; origin = origin.Next) { - if (finally_vector != null) - origin.MergeChild (finally_vector, false); - Parent.AddBreakOrigin (origin, origin.Location); - } - - for (UsageVector origin = continue_origins; origin != null; origin = origin.Next) { - if (finally_vector != null) - origin.MergeChild (finally_vector, false); - Parent.AddContinueOrigin (origin, origin.Location); - } - - for (UsageVector origin = return_origins; origin != null; origin = origin.Next) { - if (finally_vector != null) - origin.MergeChild (finally_vector, false); - Parent.AddReturnOrigin (origin, origin.Location); - } - - for (GotoOrigin origin = goto_origins; origin != null; origin = origin.Next) { - if (finally_vector != null) - origin.Vector.MergeChild (finally_vector, false); - Parent.AddGotoOrigin (origin.Vector, origin.GotoStmt); - } + for (SavedOrigin origin = saved_origins; origin != null; origin = origin.Next) + origin.PropagateFinally (finally_vector, Parent); return vector; } @@ -1175,8 +1042,19 @@ namespace Mono.CSharp // public TypeInfo[] SubStructInfo; - protected readonly StructInfo struct_info; - private static Hashtable type_hash = new Hashtable (); + readonly StructInfo struct_info; + private static Hashtable type_hash; + + static TypeInfo () + { + Reset (); + } + + public static void Reset () + { + type_hash = new Hashtable (); + StructInfo.field_type_hash = new Hashtable (); + } public static TypeInfo GetTypeInfo (Type type) { @@ -1234,7 +1112,7 @@ namespace Mono.CSharp } } - protected TypeInfo (StructInfo struct_info, int offset) + TypeInfo (StructInfo struct_info, int offset) { this.struct_info = struct_info; this.Offset = offset; @@ -1265,19 +1143,27 @@ namespace Mono.CSharp // A struct's constructor must always assign all fields. // This method checks whether it actually does so. // - public bool IsFullyInitialized (FlowBranching branching, VariableInfo vi, Location loc) + public bool IsFullyInitialized (BlockContext ec, 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++) { FieldInfo field = struct_info.Fields [i]; if (!branching.IsFieldAssigned (vi, field.Name)) { - Report.Error (171, loc, - "Field `{0}' must be fully assigned before control leaves the constructor", - TypeManager.GetFullNameSignature (field)); + FieldBase fb = TypeManager.GetField (field); + if (fb is Property.BackingField) { + ec.Report.Error (843, loc, + "An automatically implemented property `{0}' must be fully assigned before control leaves the constructor. Consider calling default contructor", + fb.GetSignatureForError ()); + } else { + ec.Report.Error (171, loc, + "Field `{0}' must be fully assigned before control leaves the constructor", + TypeManager.GetFullNameSignature (field)); + } ok = false; } } @@ -1291,7 +1177,7 @@ namespace Mono.CSharp Type, Offset, Length, TotalLength); } - protected class StructInfo { + class StructInfo { public readonly Type Type; public readonly FieldInfo[] Fields; public readonly TypeInfo[] StructFields; @@ -1302,7 +1188,7 @@ namespace Mono.CSharp public readonly int TotalLength; public readonly bool HasStructFields; - private static Hashtable field_type_hash = new Hashtable (); + public static Hashtable field_type_hash; private Hashtable struct_field_hash; private Hashtable field_hash; @@ -1316,18 +1202,20 @@ namespace Mono.CSharp field_type_hash.Add (type, this); - if (type is TypeBuilder) { - TypeContainer tc = TypeManager.LookupTypeContainer (type); - - ArrayList fields = null; - if (tc != null) - fields = tc.Fields; + if (TypeManager.IsBeingCompiled (type)) { + TypeContainer tc = TypeManager.LookupTypeContainer (TypeManager.DropGenericTypeArguments (type)); ArrayList public_fields = new ArrayList (); ArrayList non_public_fields = new ArrayList (); + // + // TODO: tc != null is needed because FixedBuffers are not cached + // + if (tc != null) { + ArrayList fields = tc.Fields; + if (fields != null) { - foreach (FieldMember field in fields) { + foreach (FieldBase field in fields) { if ((field.ModFlags & Modifiers.STATIC) != 0) continue; if ((field.ModFlags & Modifiers.PUBLIC) != 0) @@ -1336,6 +1224,7 @@ namespace Mono.CSharp non_public_fields.Add (field.FieldBuilder); } } + } CountPublic = public_fields.Count; CountNonPublic = non_public_fields.Count; @@ -1379,7 +1268,7 @@ namespace Mono.CSharp if (sinfo [i] == null) field_hash.Add (field.Name, ++Length); else if (sinfo [i].InTransit) { - Report.Error (523, String.Format ( + RootContext.ToplevelTypes.Compiler.Report.Error (523, String.Format ( "Struct member `{0}.{1}' of type `{2}' causes " + "a cycle in the structure layout", type, field.Name, sinfo [i].Type)); @@ -1426,6 +1315,9 @@ namespace Mono.CSharp TypeManager.IsBuiltinType (type)) return null; + if (TypeManager.IsGenericParameter (type)) + return null; + StructInfo info = (StructInfo) field_type_hash [type]; if (info != null) return info; @@ -1472,11 +1364,15 @@ namespace Mono.CSharp public readonly bool IsParameter; public readonly LocalInfo LocalInfo; - public readonly int ParameterIndex; readonly VariableInfo Parent; VariableInfo[] sub_info; + bool is_ever_assigned; + public bool IsEverAssigned { + get { return is_ever_assigned; } + } + protected VariableInfo (string name, Type type, int offset) { this.Name = name; @@ -1498,7 +1394,6 @@ namespace Mono.CSharp this.IsParameter = parent.IsParameter; this.LocalInfo = parent.LocalInfo; - this.ParameterIndex = parent.ParameterIndex; Initialize (); } @@ -1523,26 +1418,25 @@ namespace Mono.CSharp this.IsParameter = false; } - public VariableInfo (string name, Type type, int param_idx, int offset) - : this (name, type, offset) + public VariableInfo (ParametersCompiled ip, int i, int offset) + : this (ip.FixedParameters [i].Name, ip.Types [i], offset) { - this.ParameterIndex = param_idx; this.IsParameter = true; } - public bool IsAssigned (EmitContext ec) + public bool IsAssigned (ResolveContext ec) { return !ec.DoFlowAnalysis || ec.OmitStructFlowAnalysis && TypeInfo.IsStruct || ec.CurrentBranching.IsAssigned (this); } - public bool IsAssigned (EmitContext ec, Location loc) + public bool IsAssigned (ResolveContext ec, Location loc) { if (IsAssigned (ec)) return true; - Report.Error (165, loc, + ec.Report.Error (165, loc, "Use of unassigned local variable `" + Name + "'"); ec.CurrentBranching.SetAssigned (this); return false; @@ -1556,9 +1450,14 @@ namespace Mono.CSharp if (vector [Offset]) return true; - for (VariableInfo parent = Parent; parent != null; parent = parent.Parent) - if (vector [parent.Offset]) + // FIXME: Fix SetFieldAssigned to set the whole range like SetAssigned below. Then, get rid of this stanza + for (VariableInfo parent = Parent; parent != null; parent = parent.Parent) { + if (vector [parent.Offset]) { + // 'parent' is assigned, but someone forgot to note that all its components are assigned too + parent.SetAssigned (vector); return true; + } + } // Return unless this is a struct. if (!TypeInfo.IsStruct) @@ -1581,10 +1480,11 @@ namespace Mono.CSharp } vector [Offset] = true; + is_ever_assigned = true; return true; } - public void SetAssigned (EmitContext ec) + public void SetAssigned (ResolveContext ec) { if (ec.DoFlowAnalysis) ec.CurrentBranching.SetAssigned (this); @@ -1592,17 +1492,21 @@ namespace Mono.CSharp public void SetAssigned (MyBitVector vector) { - vector [Offset] = true; + if (Length == 1) + vector [Offset] = true; + else + vector.SetRange (Offset, Length); + is_ever_assigned = true; } - public bool IsFieldAssigned (EmitContext ec, string name, Location loc) + public bool IsFieldAssigned (ResolveContext ec, string name, Location loc) { if (!ec.DoFlowAnalysis || ec.OmitStructFlowAnalysis && TypeInfo.IsStruct || ec.CurrentBranching.IsFieldAssigned (this, name)) return true; - Report.Error (170, loc, + ec.Report.Error (170, loc, "Use of possibly unassigned field `" + name + "'"); ec.CurrentBranching.SetFieldAssigned (this, name); return false; @@ -1618,7 +1522,7 @@ namespace Mono.CSharp return vector [Offset + field_idx]; } - public void SetFieldAssigned (EmitContext ec, string name) + public void SetFieldAssigned (ResolveContext ec, string name) { if (ec.DoFlowAnalysis) ec.CurrentBranching.SetFieldAssigned (this, name); @@ -1632,6 +1536,7 @@ namespace Mono.CSharp return; vector [Offset + field_idx] = true; + is_ever_assigned = true; } public VariableInfo GetSubStruct (string name) @@ -1651,105 +1556,6 @@ namespace Mono.CSharp } } - // - // This is used by the flow code to hold the `layout' of the flow vector for - // all locals and all parameters (ie. we create one instance of this class for the - // locals and another one for the params). - // - public class VariableMap { - // - // The number of variables in the map. - // - public readonly int Count; - - // - // Total length of the flow vector for this map. - // - public readonly int Length; - - VariableInfo[] map; - - public VariableMap (Parameters ip) - { - Count = ip != null ? ip.Count : 0; - - // Dont bother allocating anything! - if (Count == 0) - return; - - Length = 0; - - for (int i = 0; i < Count; i++) { - Parameter.Modifier mod = ip.ParameterModifier (i); - - if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT) - continue; - - // Dont allocate till we find an out var. - if (map == null) - map = new VariableInfo [Count]; - - map [i] = new VariableInfo (ip.ParameterName (i), - TypeManager.GetElementType (ip.ParameterType (i)), i, Length); - - Length += map [i].Length; - } - } - - public VariableMap (LocalInfo[] locals) - : this (null, locals) - { } - - public VariableMap (VariableMap parent, LocalInfo[] locals) - { - int offset = 0, start = 0; - if (parent != null && parent.map != null) { - offset = parent.Length; - start = parent.Count; - } - - Count = locals.Length + start; - - if (Count == 0) - return; - - map = new VariableInfo [Count]; - Length = offset; - - if (parent != null && parent.map != null) { - parent.map.CopyTo (map, 0); - } - - for (int i = start; i < Count; i++) { - LocalInfo li = locals [i-start]; - - if (li.VariableType == null) - continue; - - map [i] = li.VariableInfo = new VariableInfo (li, Length); - Length += map [i].Length; - } - } - - // - // Returns the VariableInfo for variable @index or null if we don't need to - // compute assignment info for this variable. - // - public VariableInfo this [int index] { - get { - if (map == null) - return null; - - return map [index]; - } - } - - public override string ToString () - { - return String.Format ("VariableMap ({0}:{1})", Count, Length); - } - } - // // 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 @@ -1759,30 +1565,41 @@ namespace Mono.CSharp 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 BitArray vector, shared; MyBitVector () { - vector = null; shared = new BitArray (0, false); - Count = 0; } public MyBitVector (MyBitVector InheritsFrom, int Count) { - vector = null; - shared = InheritsFrom == null ? null : InheritsFrom.Shared; + if (InheritsFrom != null) + shared = InheritsFrom.MakeShared (Count); + this.Count = Count; } - BitArray Shared { - get { - if (shared == null) { - shared = vector; - vector = null; - } - return shared; + BitArray MakeShared (int new_count) + { + // 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; } // @@ -1791,7 +1608,9 @@ namespace Mono.CSharp public bool this [int index] { get { if (index >= Count) - throw new ArgumentOutOfRangeException (); + // FIXME: Disabled due to missing anonymous method flow analysis + // throw new ArgumentOutOfRangeException (); + return true; if (vector != null) return vector [index]; @@ -1818,28 +1637,86 @@ namespace Mono.CSharp // private MyBitVector Or (MyBitVector new_vector) { - int min = new_vector.Count; + if (Count == 0 || new_vector.Count == 0) + return this; + + BitArray 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; + } + + int min = o.Count; if (Count < min) min = Count; - for (int i = 0; i < min; i++) - this [i] |= new_vector [i]; + for (int i = 0; i < min; i++) { + if (o [i]) + this [i] = true; + } return this; } // - // Perfonrms an `and' operation on the bit vector. The `new_vector' may have + // Performs an `and' operation on the bit vector. The `new_vector' may have // a different size than the current one. // private MyBitVector And (MyBitVector new_vector) { - int min = new_vector.Count; + if (Count == 0) + return this; + + BitArray 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; + } + + 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++) - this [i] &= new_vector [i]; + for (int i = 0; i < min; i++) { + if (! o [i]) + this [i] = false; + } for (int i = min; i < Count; i++) this [i] = false; @@ -1849,8 +1726,8 @@ namespace Mono.CSharp public static MyBitVector operator & (MyBitVector a, MyBitVector b) { - if (a == null && b == null) - return null; + if (a == b) + return a; if (a == null) return b.Clone (); if (b == null) @@ -1863,8 +1740,8 @@ namespace Mono.CSharp public static MyBitVector operator | (MyBitVector a, MyBitVector b) { - if (a == null && b == null) - return null; + if (a == b) + return a; if (a == null) return new MyBitVector (null, b.Count); if (b == null) @@ -1880,31 +1757,50 @@ namespace Mono.CSharp return Count == 0 ? Empty : new MyBitVector (this, Count); } + public void SetRange (int offset, int length) + { + if (offset > Count || offset + length > Count) + throw new ArgumentOutOfRangeException (); + + 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 (); + } + for (; i < length; ++i) + vector [i+offset] = true; + + } + public void SetAll (bool value) { // Don't clobber Empty if (Count == 0) return; - shared = value ? null : Empty.Shared; + shared = value ? null : Empty.MakeShared (Count); vector = null; } void initialize_vector () { + // Post-condition: vector != null if (shared == null) { vector = new BitArray (Count, true); return; } - vector = new BitArray (Count, false); - - int min = shared.Count; - if (min > Count) - min = Count; - - for (int i = 0; i < min; i++) - vector [i] = shared [i]; - + vector = new BitArray (shared); + if (Count != vector.Count) + vector.Length = Count; shared = null; }