X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fstatement.cs;h=610e57d28a741f12b2d3740ff5d712c3efe75398;hb=2b808a73a435ae7c50a5edbd98eb12ba96fef4b5;hp=c64f6d50f540ece79b081a19b9ba011a6f9eff82;hpb=7d9e5440169f6c052567de43008bf824078d6c72;p=mono.git diff --git a/mcs/mcs/statement.cs b/mcs/mcs/statement.cs index c64f6d50f54..610e57d28a7 100755 --- a/mcs/mcs/statement.cs +++ b/mcs/mcs/statement.cs @@ -3,6 +3,7 @@ // // Author: // Miguel de Icaza (miguel@ximian.com) +// Martin Baulig (martin@gnome.org) // // (C) 2001, 2002 Ximian, Inc. // @@ -32,7 +33,17 @@ namespace Mono.CSharp { /// /// Return value indicates whether all code paths emitted return. /// - public abstract bool Emit (EmitContext ec); + protected abstract bool DoEmit (EmitContext ec); + + /// + /// Return value indicates whether all code paths emitted return. + /// + public virtual bool Emit (EmitContext ec) + { + ec.Mark (loc); + Report.Debug (8, "MARK", this, loc); + return DoEmit (ec); + } public static Expression ResolveBoolean (EmitContext ec, Expression e, Location loc) { @@ -50,8 +61,7 @@ namespace Mono.CSharp { 31, loc, "Can not convert the expression to a boolean"); } - if (CodeGen.SymbolWriter != null) - ec.Mark (loc); + ec.Mark (loc); return e; } @@ -113,7 +123,7 @@ namespace Mono.CSharp { return true; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { return false; } @@ -144,7 +154,7 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { - Report.Debug (1, "START IF BLOCK"); + Report.Debug (1, "START IF BLOCK", loc); expr = ResolveBoolean (ec, expr, loc); if (expr == null){ @@ -167,12 +177,12 @@ namespace Mono.CSharp { ec.EndFlowBranching (); - Report.Debug (1, "END IF BLOCK"); + Report.Debug (1, "END IF BLOCK", loc); return true; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { ILGenerator ig = ec.ig; Label false_target = ig.DefineLabel (); @@ -228,6 +238,7 @@ namespace Mono.CSharp { public class Do : Statement { public Expression expr; public readonly Statement EmbeddedStatement; + bool infinite, may_return; public Do (Statement statement, Expression boolExpr, Location l) { @@ -238,24 +249,37 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { + bool ok = true; + + ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc); + if (!EmbeddedStatement.Resolve (ec)) - return false; + ok = false; expr = ResolveBoolean (ec, expr, loc); if (expr == null) - return false; - - return true; + ok = false; + else if (expr is BoolConstant){ + bool res = ((BoolConstant) expr).Value; + + if (res) + infinite = true; + } + + ec.CurrentBranching.Infinite = infinite; + FlowReturns returns = ec.EndFlowBranching (); + may_return = returns != FlowReturns.NEVER; + + return ok; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { ILGenerator ig = ec.ig; Label loop = ig.DefineLabel (); Label old_begin = ec.LoopBegin; Label old_end = ec.LoopEnd; bool old_inloop = ec.InLoop; - bool old_breaks = ec.Breaks; int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel; ec.LoopBegin = ig.DefineLabel (); @@ -264,9 +288,7 @@ namespace Mono.CSharp { ec.LoopBeginTryCatchLevel = ec.TryCatchLevel; ig.MarkLabel (loop); - ec.Breaks = false; EmbeddedStatement.Emit (ec); - bool breaks = ec.Breaks; ig.MarkLabel (ec.LoopBegin); // @@ -286,25 +308,18 @@ namespace Mono.CSharp { ec.LoopBegin = old_begin; ec.LoopEnd = old_end; ec.InLoop = old_inloop; - ec.Breaks = old_breaks; - - // - // Inform whether we are infinite or not - // - if (expr is BoolConstant){ - BoolConstant bc = (BoolConstant) expr; - if (bc.Value == true) - return breaks == false; - } - - return false; + if (infinite) + return may_return == false; + else + return false; } } public class While : Statement { public Expression expr; public readonly Statement Statement; + bool may_return, empty, infinite; public While (Expression boolExpr, Statement statement, Location l) { @@ -315,21 +330,55 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { + bool ok = true; + expr = ResolveBoolean (ec, expr, loc); if (expr == null) return false; - - return Statement.Resolve (ec); + + ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc); + + // + // Inform whether we are infinite or not + // + if (expr is BoolConstant){ + BoolConstant bc = (BoolConstant) expr; + + if (bc.Value == false){ + Warning_DeadCodeFound (Statement.loc); + empty = true; + } else + infinite = true; + } else { + // + // We are not infinite, so the loop may or may not be executed. + // + ec.CurrentBranching.CreateSibling (); + } + + if (!Statement.Resolve (ec)) + ok = false; + + if (empty) + ec.KillFlowBranching (); + else { + ec.CurrentBranching.Infinite = infinite; + FlowReturns returns = ec.EndFlowBranching (); + may_return = returns != FlowReturns.NEVER; + } + + return ok; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { + if (empty) + return false; + ILGenerator ig = ec.ig; Label old_begin = ec.LoopBegin; Label old_end = ec.LoopEnd; bool old_inloop = ec.InLoop; - bool old_breaks = ec.Breaks; - Label while_loop = ig.DefineLabel (); int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel; bool ret; @@ -338,9 +387,6 @@ namespace Mono.CSharp { ec.InLoop = true; ec.LoopBeginTryCatchLevel = ec.TryCatchLevel; - ig.Emit (OpCodes.Br, ec.LoopBegin); - ig.MarkLabel (while_loop); - // // Inform whether we are infinite or not // @@ -348,25 +394,21 @@ namespace Mono.CSharp { BoolConstant bc = (BoolConstant) expr; ig.MarkLabel (ec.LoopBegin); - if (bc.Value == false){ - Warning_DeadCodeFound (Statement.loc); - ret = false; - } else { - bool breaks; - - ec.Breaks = false; - Statement.Emit (ec); - breaks = ec.Breaks; - ig.Emit (OpCodes.Br, ec.LoopBegin); + Statement.Emit (ec); + ig.Emit (OpCodes.Br, ec.LoopBegin); - // - // Inform that we are infinite (ie, `we return'), only - // if we do not `break' inside the code. - // - ret = breaks == false; - } + // + // Inform that we are infinite (ie, `we return'), only + // if we do not `break' inside the code. + // + ret = may_return == false; ig.MarkLabel (ec.LoopEnd); } else { + Label while_loop = ig.DefineLabel (); + + ig.Emit (OpCodes.Br, ec.LoopBegin); + ig.MarkLabel (while_loop); + Statement.Emit (ec); ig.MarkLabel (ec.LoopBegin); @@ -380,7 +422,6 @@ namespace Mono.CSharp { ec.LoopBegin = old_begin; ec.LoopEnd = old_end; ec.InLoop = old_inloop; - ec.Breaks = old_breaks; ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level; return ret; @@ -392,6 +433,7 @@ namespace Mono.CSharp { readonly Statement InitStatement; readonly Statement Increment; readonly Statement Statement; + bool may_return, infinite, empty; public For (Statement initStatement, Expression test, @@ -419,23 +461,50 @@ namespace Mono.CSharp { Test = ResolveBoolean (ec, Test, loc); if (Test == null) ok = false; - } + else if (Test is BoolConstant){ + BoolConstant bc = (BoolConstant) Test; + + if (bc.Value == false){ + Warning_DeadCodeFound (Statement.loc); + empty = true; + } else + infinite = true; + } + } else + infinite = true; if (Increment != null){ if (!Increment.Resolve (ec)) ok = false; } - - return Statement.Resolve (ec) && ok; + + ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc); + if (!infinite) + ec.CurrentBranching.CreateSibling (); + + if (!Statement.Resolve (ec)) + ok = false; + + if (empty) + ec.KillFlowBranching (); + else { + ec.CurrentBranching.Infinite = infinite; + FlowReturns returns = ec.EndFlowBranching (); + may_return = returns != FlowReturns.NEVER; + } + + return ok; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { + if (empty) + return false; + ILGenerator ig = ec.ig; Label old_begin = ec.LoopBegin; Label old_end = ec.LoopEnd; bool old_inloop = ec.InLoop; - bool old_breaks = ec.Breaks; int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel; Label loop = ig.DefineLabel (); Label test = ig.DefineLabel (); @@ -451,9 +520,7 @@ namespace Mono.CSharp { ig.Emit (OpCodes.Br, test); ig.MarkLabel (loop); - ec.Breaks = false; Statement.Emit (ec); - bool breaks = ec.Breaks; ig.MarkLabel (ec.LoopBegin); if (!(Increment is EmptyStatement)) @@ -473,22 +540,21 @@ namespace Mono.CSharp { ec.LoopBegin = old_begin; ec.LoopEnd = old_end; ec.InLoop = old_inloop; - ec.Breaks = old_breaks; ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level; // - // Inform whether we are infinite or not + // Inform whether we are infinite or not // if (Test != null){ if (Test is BoolConstant){ BoolConstant bc = (BoolConstant) Test; if (bc.Value) - return breaks == false; + return may_return == false; } return false; } else - return true; + return may_return == false; } } @@ -507,7 +573,7 @@ namespace Mono.CSharp { return expr != null; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { ILGenerator ig = ec.ig; @@ -551,14 +617,15 @@ namespace Mono.CSharp { if (ec.CurrentBranching.InTryBlock ()) ec.CurrentBranching.AddFinallyVector (vector); + else + vector.CheckOutParameters (ec.CurrentBranching); vector.Returns = FlowReturns.ALWAYS; vector.Breaks = FlowReturns.ALWAYS; - return true; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { if (ec.InFinally){ Report.Error (157,loc,"Control can not leave the body of the finally block"); @@ -568,14 +635,14 @@ namespace Mono.CSharp { if (ec.ReturnType == null){ if (Expr != null){ Report.Error (127, loc, "Return with a value not allowed here"); - return false; + return true; } } else { if (Expr == null){ Report.Error (126, loc, "An object of type `" + TypeManager.CSharpName (ec.ReturnType) + "' is " + "expected for the return statement"); - return false; + return true; } if (Expr.Type != ec.ReturnType) @@ -583,7 +650,7 @@ namespace Mono.CSharp { ec, Expr, ec.ReturnType, loc); if (Expr == null) - return false; + return true; Expr.Emit (ec); @@ -641,7 +708,7 @@ namespace Mono.CSharp { } } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { Label l = label.LabelTarget (ec); ec.ig.Emit (OpCodes.Br, l); @@ -651,15 +718,18 @@ namespace Mono.CSharp { } public class LabeledStatement : Statement { + public readonly Location Location; string label_name; bool defined; + bool referenced; Label label; ArrayList vectors; - public LabeledStatement (string label_name) + public LabeledStatement (string label_name, Location l) { this.label_name = label_name; + this.Location = l; } public Label LabelTarget (EmitContext ec) @@ -678,6 +748,12 @@ namespace Mono.CSharp { } } + public bool HasBeenReferenced { + get { + return referenced; + } + } + public void AddUsageVector (FlowBranching.UsageVector vector) { if (vectors == null) @@ -690,11 +766,17 @@ namespace Mono.CSharp { { if (vectors != null) ec.CurrentBranching.CurrentUsageVector.MergeJumpOrigins (vectors); + else { + ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.NEVER; + ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.NEVER; + } + + referenced = true; return true; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { LabelTarget (ec); ec.ig.MarkLabel (label); @@ -716,12 +798,11 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { - ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.UNREACHABLE; - ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS; + ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.UNREACHABLE; return true; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { if (ec.Switch == null){ Report.Error (153, loc, "goto default is only valid in a switch statement"); @@ -782,12 +863,11 @@ namespace Mono.CSharp { label = sl.ILLabelCode; - ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.UNREACHABLE; - ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS; + ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.UNREACHABLE; return true; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { ec.ig.Emit (OpCodes.Br, label); return true; @@ -809,6 +889,25 @@ namespace Mono.CSharp { expr = expr.Resolve (ec); if (expr == null) return false; + + ExprClass eclass = expr.eclass; + + if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess || + eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) { + expr.Error118 ("value, variable, property or indexer access "); + return false; + } + + Type t = expr.Type; + + if ((t != TypeManager.exception_type) && + !t.IsSubclassOf (TypeManager.exception_type) && + !(expr is NullLiteral)) { + Report.Error (155, loc, + "The type caught or thrown must be derived " + + "from System.Exception"); + return false; + } } ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.EXCEPTION; @@ -816,7 +915,7 @@ namespace Mono.CSharp { return true; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { if (expr == null){ if (ec.InCatch) @@ -847,11 +946,12 @@ namespace Mono.CSharp { public override bool Resolve (EmitContext ec) { + ec.CurrentBranching.MayLeaveLoop = true; ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS; return true; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { ILGenerator ig = ec.ig; @@ -860,7 +960,6 @@ namespace Mono.CSharp { return false; } - ec.Breaks = true; if (ec.InTry || ec.InCatch) ig.Emit (OpCodes.Leave, ec.LoopEnd); else @@ -883,7 +982,7 @@ namespace Mono.CSharp { return true; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { Label begin = ec.LoopBegin; @@ -1141,6 +1240,9 @@ namespace Mono.CSharp { // Normal (conditional or toplevel) block. BLOCK, + // A loop block. + LOOP_BLOCK, + // Try/Catch block. EXCEPTION, @@ -1183,11 +1285,22 @@ namespace Mono.CSharp { // public ArrayList Siblings; + // + // If this is an infinite loop. + // + public bool Infinite; + + // + // If we may leave the current loop. + // + public bool MayLeaveLoop; + // // Private // InternalParameters param_info; int[] param_map; + MyStructInfo[] struct_params; int num_params; ArrayList finally_vectors; @@ -1210,7 +1323,6 @@ namespace Mono.CSharp { return FlowReturns.NEVER; else return FlowReturns.SOMETIMES; - break; case FlowReturns.SOMETIMES: return FlowReturns.SOMETIMES; @@ -1220,7 +1332,6 @@ namespace Mono.CSharp { return FlowReturns.ALWAYS; else return FlowReturns.SOMETIMES; - break; case FlowReturns.EXCEPTION: if (b == FlowReturns.EXCEPTION) @@ -1229,7 +1340,6 @@ namespace Mono.CSharp { return FlowReturns.ALWAYS; else return FlowReturns.SOMETIMES; - break; } return b; @@ -1268,7 +1378,7 @@ namespace Mono.CSharp { // MyBitVector locals, parameters; FlowReturns real_returns, real_breaks; - bool returns_set, breaks_set, is_finally; + bool is_finally; static int next_id = 0; int id; @@ -1286,10 +1396,14 @@ namespace Mono.CSharp { if (parent != null) { locals = new MyBitVector (parent.locals, CountLocals); - parameters = new MyBitVector (parent.parameters, num_params); + if (num_params > 0) + parameters = new MyBitVector (parent.parameters, num_params); + real_returns = parent.Returns; + real_breaks = parent.Breaks; } else { locals = new MyBitVector (null, CountLocals); - parameters = new MyBitVector (null, num_params); + if (num_params > 0) + parameters = new MyBitVector (null, num_params); } id = ++next_id; @@ -1341,8 +1455,10 @@ namespace Mono.CSharp { // // State of the local variable `vi'. + // If the local variable is a struct, use a non-zero `field_idx' + // to check an individual field in it. // - public bool this [VariableInfo vi] + public bool this [VariableInfo vi, int field_idx] { get { if (vi.Number == -1) @@ -1350,7 +1466,7 @@ namespace Mono.CSharp { else if (vi.Number == 0) throw new ArgumentException (); - return locals [vi.Number - 1]; + return locals [vi.Number + field_idx - 1]; } set { @@ -1359,12 +1475,16 @@ namespace Mono.CSharp { else if (vi.Number == 0) throw new ArgumentException (); - locals [vi.Number - 1] = value; + locals [vi.Number + field_idx - 1] = value; } } // // Specifies when the current block returns. + // If this is FlowReturns.UNREACHABLE, then control can never reach the + // end of the method (so that we don't need to emit a return statement). + // The same applies for FlowReturns.EXCEPTION, but in this case the return + // value will never be used. // public FlowReturns Returns { get { @@ -1373,7 +1493,6 @@ namespace Mono.CSharp { set { real_returns = value; - returns_set = true; } } @@ -1381,6 +1500,8 @@ namespace Mono.CSharp { // Specifies whether control may return to our containing block // before reaching the end of this block. This happens if there // is a break/continue/goto/return in it. + // This can also be used to find out whether the statement immediately + // following the current block may be reached or not. // public FlowReturns Breaks { get { @@ -1389,7 +1510,34 @@ namespace Mono.CSharp { set { real_breaks = value; - breaks_set = true; + } + } + + public bool AlwaysBreaks { + get { + return (Breaks == FlowReturns.ALWAYS) || + (Breaks == FlowReturns.EXCEPTION) || + (Breaks == FlowReturns.UNREACHABLE); + } + } + + public bool MayBreak { + get { + return Breaks != FlowReturns.NEVER; + } + } + + public bool AlwaysReturns { + get { + return (Returns == FlowReturns.ALWAYS) || + (Returns == FlowReturns.EXCEPTION); + } + } + + public bool MayReturn { + get { + return (Returns == FlowReturns.SOMETIMES) || + (Returns == FlowReturns.ALWAYS); } } @@ -1404,39 +1552,39 @@ namespace Mono.CSharp { FlowReturns new_returns = FlowReturns.NEVER; FlowReturns new_breaks = FlowReturns.NEVER; bool new_returns_set = false, new_breaks_set = false; - FlowReturns breaks; - Report.Debug (1, "MERGING CHILDREN", branching, this); + Report.Debug (2, "MERGING CHILDREN", branching, branching.Type, + this, children.Count); foreach (UsageVector child in children) { - Report.Debug (1, " MERGING CHILD", child); - - // If Returns is already set, perform an `And' operation on it, - // otherwise just set just. - if (!new_returns_set) { - new_returns = child.Returns; - new_returns_set = true; - } else - new_returns = AndFlowReturns (new_returns, child.Returns); + Report.Debug (2, " MERGING CHILD", child, child.is_finally); + + if (!child.is_finally) { + if (child.Breaks != FlowReturns.UNREACHABLE) { + // If Returns is already set, perform an + // `And' operation on it, otherwise just set just. + if (!new_returns_set) { + new_returns = child.Returns; + new_returns_set = true; + } else + new_returns = AndFlowReturns ( + new_returns, child.Returns); + } - // If Breaks is already set, perform an `And' operation on it, - // otherwise just set just. - if (!new_breaks_set) { - new_breaks = child.Breaks; - new_breaks_set = true; - } else - new_breaks = AndFlowReturns (new_breaks, child.Breaks); + // If Breaks is already set, perform an + // `And' operation on it, otherwise just set just. + if (!new_breaks_set) { + new_breaks = child.Breaks; + new_breaks_set = true; + } else + new_breaks = AndFlowReturns ( + new_breaks, child.Breaks); + } // Ignore unreachable children. if (child.Returns == FlowReturns.UNREACHABLE) continue; - // If we're a switch section, `break' won't leave the current - // branching (NOTE: the type check here means that we're "a" - // switch section, not that we're "in" a switch section!). - breaks = (branching.Type == FlowBranchingType.SWITCH_SECTION) ? - child.Returns : child.Breaks; - // 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. @@ -1472,41 +1620,60 @@ namespace Mono.CSharp { // Here, `a' is initialized in line 3 and we must not look at // line 5 since it always returns. // - if ((breaks != FlowReturns.EXCEPTION) && - (breaks != FlowReturns.ALWAYS)) { - if (new_locals != null) - new_locals.And (child.locals); - else { + if (child.is_finally) { + if (new_locals == null) new_locals = locals.Clone (); - new_locals.Or (child.locals); - } - } + new_locals.Or (child.locals); - // An `out' parameter must be assigned in all branches which do - // not always throw an exception. - if (!child.is_finally && (child.Returns != FlowReturns.EXCEPTION)) { - if (new_params != null) - new_params.And (child.parameters); - else { - new_params = parameters.Clone (); + if (parameters != null) { + if (new_params == null) + new_params = parameters.Clone (); new_params.Or (child.parameters); } - } - // If we always return, check whether all `out' parameters have - // been assigned. - if (child.Returns == FlowReturns.ALWAYS) { - branching.CheckOutParameters ( - child.parameters, branching.Location); + } else { + if (!child.AlwaysReturns && !child.AlwaysBreaks) { + if (new_locals != null) + new_locals.And (child.locals); + else { + new_locals = locals.Clone (); + new_locals.Or (child.locals); + } + } else if (children.Count == 1) { + new_locals = locals.Clone (); + new_locals.Or (child.locals); + } + + // An `out' parameter must be assigned in all branches which do + // not always throw an exception. + if (parameters != null) { + if (child.Breaks != FlowReturns.EXCEPTION) { + if (new_params != null) + new_params.And (child.parameters); + else { + new_params = parameters.Clone (); + new_params.Or (child.parameters); + } + } else if (children.Count == 1) { + new_params = parameters.Clone (); + new_params.Or (child.parameters); + } + } } } - // Set new `Returns' status. - if (!returns_set) { - Returns = new_returns; - returns_set = true; - } else - Returns = AndFlowReturns (Returns, new_returns); + Returns = new_returns; + if ((branching.Type == FlowBranchingType.BLOCK) || + (branching.Type == FlowBranchingType.EXCEPTION) || + (new_breaks == FlowReturns.UNREACHABLE) || + (new_breaks == FlowReturns.EXCEPTION)) + Breaks = new_breaks; + else if (branching.Type == FlowBranchingType.SWITCH_SECTION) + Breaks = new_returns; + else if (branching.Type == FlowBranchingType.SWITCH){ + if (new_breaks == FlowReturns.ALWAYS) + Breaks = FlowReturns.ALWAYS; + } // // We've now either reached the point after the branching or we will @@ -1517,30 +1684,50 @@ namespace Mono.CSharp { // we need to look at (see above). // - breaks = (branching.Type == FlowBranchingType.SWITCH_SECTION) ? - Returns : Breaks; + if (((new_breaks != FlowReturns.ALWAYS) && + (new_breaks != FlowReturns.EXCEPTION) && + (new_breaks != FlowReturns.UNREACHABLE)) || + (children.Count == 1)) { + if (new_locals != null) + locals.Or (new_locals); - if ((new_locals != null) && - ((breaks == FlowReturns.NEVER) || (breaks == FlowReturns.SOMETIMES))) { - locals.Or (new_locals); + if (new_params != null) + parameters.Or (new_params); } - if ((new_params != null) && (Breaks == FlowReturns.NEVER)) - parameters.Or (new_params); + Report.Debug (2, "MERGING CHILDREN DONE", branching.Type, + new_params, new_locals, new_returns, new_breaks, + branching.Infinite, branching.MayLeaveLoop, this); + + if (branching.Type == FlowBranchingType.SWITCH_SECTION) { + if ((new_breaks != FlowReturns.ALWAYS) && + (new_breaks != FlowReturns.EXCEPTION) && + (new_breaks != FlowReturns.UNREACHABLE)) + Report.Error (163, branching.Location, + "Control cannot fall through from one " + + "case label to another"); + } - // - // If we may have returned (this only happens if there was a reachable - // `return' statement in one of the branches), then we may return to our - // parent block before reaching the end of the block, so set `Breaks'. - // + if (branching.Infinite && !branching.MayLeaveLoop) { + Report.Debug (1, "INFINITE", new_returns, new_breaks, + Returns, Breaks, this); - if ((Returns != FlowReturns.NEVER) && (Returns != FlowReturns.SOMETIMES)) { - real_breaks = Returns; - breaks_set = true; - } + // We're actually infinite. + if (new_returns == FlowReturns.NEVER) { + Breaks = FlowReturns.UNREACHABLE; + return FlowReturns.UNREACHABLE; + } - Report.Debug (1, "MERGING CHILDREN DONE", new_params, new_locals, - new_returns, new_breaks, this); + // If we're an infinite loop and do not break, the code after + // the loop can never be reached. However, if we may return + // from the loop, then we do always return (or stay in the loop + // forever). + if ((new_returns == FlowReturns.SOMETIMES) || + (new_returns == FlowReturns.ALWAYS)) { + Returns = FlowReturns.ALWAYS; + return FlowReturns.ALWAYS; + } + } return new_returns; } @@ -1570,14 +1757,16 @@ namespace Mono.CSharp { Report.Debug (1, "MERGING JUMP ORIGIN", this); real_breaks = FlowReturns.NEVER; - breaks_set = false; + real_returns = FlowReturns.NEVER; foreach (UsageVector vector in origin_vectors) { Report.Debug (1, " MERGING JUMP ORIGIN", vector); locals.And (vector.locals); - parameters.And (vector.parameters); + if (parameters != null) + parameters.And (vector.parameters); Breaks = AndFlowReturns (Breaks, vector.Breaks); + Returns = AndFlowReturns (Returns, vector.Returns); } Report.Debug (1, "MERGING JUMP ORIGIN DONE", this); @@ -1592,12 +1781,12 @@ namespace Mono.CSharp { Report.Debug (1, "MERGING FINALLY ORIGIN", this); real_breaks = FlowReturns.NEVER; - breaks_set = false; foreach (UsageVector vector in finally_vectors) { Report.Debug (1, " MERGING FINALLY ORIGIN", vector); - parameters.And (vector.parameters); + if (parameters != null) + parameters.And (vector.parameters); Breaks = AndFlowReturns (Breaks, vector.Breaks); } @@ -1606,13 +1795,20 @@ namespace Mono.CSharp { Report.Debug (1, "MERGING FINALLY ORIGIN DONE", this); } + public void CheckOutParameters (FlowBranching branching) + { + if (parameters != null) + branching.CheckOutParameters (parameters, branching.Location); + } + // // Performs an `or' operation on the locals and the parameters. // public void Or (UsageVector new_vector) { locals.Or (new_vector.locals); - parameters.Or (new_vector.parameters); + if (parameters != null) + parameters.Or (new_vector.parameters); } // @@ -1628,7 +1824,10 @@ namespace Mono.CSharp { // public MyBitVector Parameters { get { - return parameters.Clone (); + if (parameters != null) + return parameters.Clone (); + else + return null; } } @@ -1655,8 +1854,10 @@ namespace Mono.CSharp { sb.Append (Returns); sb.Append (","); sb.Append (Breaks); - sb.Append (" - "); - sb.Append (parameters); + if (parameters != null) { + sb.Append (" - "); + sb.Append (parameters); + } sb.Append (" - "); sb.Append (locals); sb.Append (")"); @@ -1685,17 +1886,26 @@ namespace Mono.CSharp { Block = block; Parent = null; + int count = (ip != null) ? ip.Count : 0; + param_info = ip; - param_map = new int [(param_info != null) ? param_info.Count : 0]; + param_map = new int [count]; + struct_params = new MyStructInfo [count]; num_params = 0; - for (int i = 0; i < param_map.Length; i++) { + for (int i = 0; i < count; i++) { Parameter.Modifier mod = param_info.ParameterModifier (i); if ((mod & Parameter.Modifier.OUT) == 0) continue; param_map [i] = ++num_params; + + Type param_type = param_info.ParameterType (i); + + struct_params [i] = MyStructInfo.GetStructInfo (param_type); + if (struct_params [i] != null) + num_params += struct_params [i].Count; } Siblings = new ArrayList (); @@ -1718,6 +1928,7 @@ namespace Mono.CSharp { if (parent != null) { param_info = parent.param_info; param_map = parent.param_map; + struct_params = parent.struct_params; num_params = parent.num_params; } @@ -1782,15 +1993,38 @@ namespace Mono.CSharp { return; for (int i = 0; i < param_map.Length; i++) { - if (param_map [i] == 0) + int index = param_map [i]; + + if (index == 0) continue; - if (!parameters [param_map [i] - 1]) { + if (parameters [index - 1]) + continue; + + // If it's a struct, we must ensure that all its fields have + // been assigned. If the struct has any non-public fields, this + // can only be done by assigning the whole struct. + + MyStructInfo struct_info = struct_params [index - 1]; + if ((struct_info == null) || struct_info.HasNonPublicFields) { Report.Error ( 177, loc, "The out parameter `" + - param_info.ParameterName (i) + "` must be " + + param_info.ParameterName (i) + "' must be " + "assigned before control leave the current method."); param_map [i] = 0; + continue; + } + + + for (int j = 0; j < struct_info.Count; j++) { + if (!parameters [index + j]) { + Report.Error ( + 177, loc, "The out parameter `" + + param_info.ParameterName (i) + "' must be " + + "assigned before control leave the current method."); + param_map [i] = 0; + break; + } } } } @@ -1800,9 +2034,16 @@ namespace Mono.CSharp { // public FlowReturns MergeChild (FlowBranching child) { - return CurrentUsageVector.MergeChildren (child, child.Siblings); - } + FlowReturns returns = CurrentUsageVector.MergeChildren (child, child.Siblings); + if (child.Type != FlowBranchingType.LOOP_BLOCK) + MayLeaveLoop |= child.MayLeaveLoop; + else + MayLeaveLoop = false; + + return returns; + } + // // Does the toplevel merging. // @@ -1813,17 +2054,21 @@ namespace Mono.CSharp { UsageVector vector = new UsageVector (null, num_params, Block.CountVariables); + Report.Debug (1, "MERGING TOP BLOCK", Location, vector); + vector.MergeChildren (this, Siblings); Siblings.Clear (); Siblings.Add (vector); - Report.Debug (1, "MERGING TOP BLOCK", vector); + Report.Debug (1, "MERGING TOP BLOCK DONE", Location, vector); - if (vector.Returns != FlowReturns.EXCEPTION) - CheckOutParameters (CurrentUsageVector.Parameters, Location); - - return vector.Returns; + if (vector.Breaks != FlowReturns.EXCEPTION) { + if (!vector.AlwaysBreaks) + CheckOutParameters (CurrentUsageVector.Parameters, Location); + return vector.AlwaysBreaks ? FlowReturns.ALWAYS : vector.Returns; + } else + return FlowReturns.EXCEPTION; } public bool InTryBlock () @@ -1851,46 +2096,135 @@ namespace Mono.CSharp { public bool IsVariableAssigned (VariableInfo vi) { - Report.Debug (2, "CHECK VARIABLE ACCESS", this, vi); + if (CurrentUsageVector.AlwaysBreaks) + return true; + else + return CurrentUsageVector [vi, 0]; + } - if (CurrentUsageVector.Breaks == FlowReturns.UNREACHABLE) + public bool IsVariableAssigned (VariableInfo vi, int field_idx) + { + if (CurrentUsageVector.AlwaysBreaks) return true; else - return CurrentUsageVector [vi]; + return CurrentUsageVector [vi, field_idx]; } public void SetVariableAssigned (VariableInfo vi) { - Report.Debug (2, "SET VARIABLE ACCESS", this, vi, CurrentUsageVector); + if (CurrentUsageVector.AlwaysBreaks) + return; + + CurrentUsageVector [vi, 0] = true; + } - if (CurrentUsageVector.Breaks == FlowReturns.UNREACHABLE) + public void SetVariableAssigned (VariableInfo vi, int field_idx) + { + if (CurrentUsageVector.AlwaysBreaks) return; - CurrentUsageVector [vi] = true; + CurrentUsageVector [vi, field_idx] = true; } public bool IsParameterAssigned (int number) { - Report.Debug (2, "IS PARAMETER ASSIGNED", this, number); + int index = param_map [number]; - if (param_map [number] == 0) + if (index == 0) return true; - else - return CurrentUsageVector [param_map [number]]; + + if (CurrentUsageVector [index]) + return true; + + // Parameter is not assigned, so check whether it's a struct. + // If it's either not a struct or a struct which non-public + // fields, return false. + MyStructInfo struct_info = struct_params [number]; + if ((struct_info == null) || struct_info.HasNonPublicFields) + return false; + + // Ok, so each field must be assigned. + for (int i = 0; i < struct_info.Count; i++) + if (!CurrentUsageVector [index + i]) + return false; + + return true; } - public void SetParameterAssigned (int number) + public bool IsParameterAssigned (int number, string field_name) { - Report.Debug (2, "SET PARAMETER ACCESS", this, number, param_map [number], - CurrentUsageVector); + int index = param_map [number]; + + if (index == 0) + return true; + + MyStructInfo info = (MyStructInfo) struct_params [number]; + if (info == null) + return true; + + int field_idx = info [field_name]; + + return CurrentUsageVector [index + field_idx]; + } + public void SetParameterAssigned (int number) + { if (param_map [number] == 0) return; - if (CurrentUsageVector.Breaks == FlowReturns.NEVER) + if (!CurrentUsageVector.AlwaysBreaks) CurrentUsageVector [param_map [number]] = true; } + public void SetParameterAssigned (int number, string field_name) + { + int index = param_map [number]; + + if (index == 0) + return; + + MyStructInfo info = (MyStructInfo) struct_params [number]; + if (info == null) + return; + + int field_idx = info [field_name]; + + if (!CurrentUsageVector.AlwaysBreaks) + CurrentUsageVector [index + field_idx] = true; + } + + public bool IsReachable () + { + bool reachable; + + switch (Type) { + case FlowBranchingType.SWITCH_SECTION: + // The code following a switch block is reachable unless the switch + // block always returns. + reachable = !CurrentUsageVector.AlwaysReturns; + break; + + case FlowBranchingType.LOOP_BLOCK: + // The code following a loop is reachable unless the loop always + // returns or it's an infinite loop without any `break's in it. + reachable = !CurrentUsageVector.AlwaysReturns && + (CurrentUsageVector.Breaks != FlowReturns.UNREACHABLE); + break; + + default: + // The code following a block or exception is reachable unless the + // block either always returns or always breaks. + reachable = !CurrentUsageVector.AlwaysBreaks && + !CurrentUsageVector.AlwaysReturns; + break; + } + + Report.Debug (1, "REACHABLE", Type, CurrentUsageVector.Returns, + CurrentUsageVector.Breaks, CurrentUsageVector, reachable); + + return reachable; + } + public override string ToString () { StringBuilder sb = new StringBuilder ("FlowBranching ("); @@ -1912,12 +2246,131 @@ namespace Mono.CSharp { return sb.ToString (); } } + + public class MyStructInfo { + public readonly Type Type; + public readonly FieldInfo[] Fields; + public readonly FieldInfo[] NonPublicFields; + public readonly int Count; + public readonly int CountNonPublic; + public readonly bool HasNonPublicFields; + + private static Hashtable field_type_hash = new Hashtable (); + private Hashtable field_hash; + + // Private constructor. To save memory usage, we only need to create one instance + // of this class per struct type. + private MyStructInfo (Type type) + { + this.Type = type; + + if (type is TypeBuilder) { + TypeContainer tc = TypeManager.LookupTypeContainer (type); + + ArrayList fields = tc.Fields; + if (fields != null) { + foreach (Field field in fields) { + if ((field.ModFlags & Modifiers.STATIC) != 0) + continue; + if ((field.ModFlags & Modifiers.PUBLIC) != 0) + ++Count; + else + ++CountNonPublic; + } + } + + Fields = new FieldInfo [Count]; + NonPublicFields = new FieldInfo [CountNonPublic]; + + Count = CountNonPublic = 0; + if (fields != null) { + foreach (Field field in fields) { + if ((field.ModFlags & Modifiers.STATIC) != 0) + continue; + if ((field.ModFlags & Modifiers.PUBLIC) != 0) + Fields [Count++] = field.FieldBuilder; + else + NonPublicFields [CountNonPublic++] = + field.FieldBuilder; + } + } + + } else { + Fields = type.GetFields (BindingFlags.Instance|BindingFlags.Public); + Count = Fields.Length; + + NonPublicFields = type.GetFields (BindingFlags.Instance|BindingFlags.NonPublic); + CountNonPublic = NonPublicFields.Length; + } + + Count += NonPublicFields.Length; + + int number = 0; + field_hash = new Hashtable (); + foreach (FieldInfo field in Fields) + field_hash.Add (field.Name, ++number); + + if (NonPublicFields.Length != 0) + HasNonPublicFields = true; + + foreach (FieldInfo field in NonPublicFields) + field_hash.Add (field.Name, ++number); + } + + public int this [string name] { + get { + if (field_hash.Contains (name)) + return (int) field_hash [name]; + else + return 0; + } + } + + public FieldInfo this [int index] { + get { + if (index >= Fields.Length) + return NonPublicFields [index - Fields.Length]; + else + return Fields [index]; + } + } + + public static MyStructInfo GetStructInfo (Type type) + { + if (!TypeManager.IsValueType (type) || TypeManager.IsEnumType (type)) + return null; + + if (!(type is TypeBuilder) && TypeManager.IsBuiltinType (type)) + return null; + + MyStructInfo info = (MyStructInfo) field_type_hash [type]; + if (info != null) + return info; + + info = new MyStructInfo (type); + field_type_hash.Add (type, info); + return info; + } + + public static MyStructInfo GetStructInfo (TypeContainer tc) + { + MyStructInfo info = (MyStructInfo) field_type_hash [tc.TypeBuilder]; + if (info != null) + return info; + + info = new MyStructInfo (tc.TypeBuilder); + field_type_hash.Add (tc.TypeBuilder, info); + return info; + } + } - public class VariableInfo { + public class VariableInfo : IVariable { public Expression Type; public LocalBuilder LocalBuilder; public Type VariableType; + public readonly string Name; public readonly Location Location; + public readonly int Block; public int Number; @@ -1925,13 +2378,115 @@ namespace Mono.CSharp { public bool Assigned; public bool ReadOnly; - public VariableInfo (Expression type, Location l) + public VariableInfo (Expression type, string name, int block, Location l) { Type = type; + Name = name; + Block = block; LocalBuilder = null; Location = l; } + public VariableInfo (TypeContainer tc, int block, Location l) + { + VariableType = tc.TypeBuilder; + struct_info = MyStructInfo.GetStructInfo (tc); + Block = block; + LocalBuilder = null; + Location = l; + } + + MyStructInfo struct_info; + public MyStructInfo StructInfo { + get { + return struct_info; + } + } + + public bool IsAssigned (EmitContext ec, Location loc) + { + if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this)) + return true; + + MyStructInfo struct_info = StructInfo; + if ((struct_info == null) || (struct_info.HasNonPublicFields && (Name != null))) { + Report.Error (165, loc, "Use of unassigned local variable `" + Name + "'"); + ec.CurrentBranching.SetVariableAssigned (this); + return false; + } + + int count = struct_info.Count; + + for (int i = 0; i < count; i++) { + if (!ec.CurrentBranching.IsVariableAssigned (this, i+1)) { + if (Name != null) { + Report.Error (165, loc, + "Use of unassigned local variable `" + + Name + "'"); + ec.CurrentBranching.SetVariableAssigned (this); + return false; + } + + FieldInfo field = struct_info [i]; + Report.Error (171, loc, + "Field `" + TypeManager.CSharpName (VariableType) + + "." + field.Name + "' must be fully initialized " + + "before control leaves the constructor"); + return false; + } + } + + return true; + } + + public bool IsFieldAssigned (EmitContext ec, string name, Location loc) + { + if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this) || + (struct_info == null)) + return true; + + int field_idx = StructInfo [name]; + if (field_idx == 0) + return true; + + if (!ec.CurrentBranching.IsVariableAssigned (this, field_idx)) { + Report.Error (170, loc, + "Use of possibly unassigned field `" + name + "'"); + ec.CurrentBranching.SetVariableAssigned (this, field_idx); + return false; + } + + return true; + } + + public void SetAssigned (EmitContext ec) + { + if (ec.DoFlowAnalysis) + ec.CurrentBranching.SetVariableAssigned (this); + } + + public void SetFieldAssigned (EmitContext ec, string name) + { + if (ec.DoFlowAnalysis && (struct_info != null)) + ec.CurrentBranching.SetVariableAssigned (this, StructInfo [name]); + } + + public bool Resolve (DeclSpace decl) + { + if (struct_info != null) + return true; + + if (VariableType == null) + VariableType = decl.ResolveType (Type, false, Location); + + if (VariableType == null) + return false; + + struct_info = MyStructInfo.GetStructInfo (VariableType); + + return true; + } + public void MakePinned () { TypeManager.MakePinned (LocalBuilder); @@ -2007,17 +2562,32 @@ namespace Mono.CSharp { : this (parent, implicit_block, Location.Null, Location.Null) { } + public Block (Block parent, bool implicit_block, Parameters parameters) + : this (parent, implicit_block, parameters, Location.Null, Location.Null) + { } + public Block (Block parent, Location start, Location end) : this (parent, false, start, end) { } + public Block (Block parent, Parameters parameters, Location start, Location end) + : this (parent, false, parameters, start, end) + { } + public Block (Block parent, bool implicit_block, Location start, Location end) + : this (parent, implicit_block, Parameters.EmptyReadOnlyParameters, + start, end) + { } + + public Block (Block parent, bool implicit_block, Parameters parameters, + Location start, Location end) { if (parent != null) parent.AddChild (this); this.Parent = parent; this.Implicit = implicit_block; + this.parameters = parameters; this.StartLocation = start; this.EndLocation = end; this.loc = start; @@ -2030,7 +2600,7 @@ namespace Mono.CSharp { return this_id; } } - + void AddChild (Block b) { if (children == null) @@ -2077,22 +2647,132 @@ namespace Mono.CSharp { return null; } + VariableInfo this_variable = null; + + // + // Returns the "this" instance variable of this block. + // See AddThisVariable() for more information. + // + public VariableInfo ThisVariable { + get { + if (this_variable != null) + return this_variable; + else if (Parent != null) + return Parent.ThisVariable; + else + return null; + } + } + + Hashtable child_variable_names; + + // + // Marks a variable with name @name as being used in a child block. + // If a variable name has been used in a child block, it's illegal to + // declare a variable with the same name in the current block. + // + public void AddChildVariableName (string name) + { + if (child_variable_names == null) + child_variable_names = new Hashtable (); + + if (!child_variable_names.Contains (name)) + child_variable_names.Add (name, true); + } + + // + // Marks all variables from block @block and all its children as being + // used in a child block. + // + public void AddChildVariableNames (Block block) + { + if (block.Variables != null) { + foreach (string name in block.Variables.Keys) + AddChildVariableName (name); + } + + foreach (Block child in block.children) { + if (child.Variables != null) { + foreach (string name in child.Variables.Keys) + AddChildVariableName (name); + } + } + } + + // + // Checks whether a variable name has already been used in a child block. + // + public bool IsVariableNameUsedInChildBlock (string name) + { + if (child_variable_names == null) + return false; + + return child_variable_names.Contains (name); + } + + // + // This is used by non-static `struct' constructors which do not have an + // initializer - in this case, the constructor must initialize all of the + // struct's fields. To do this, we add a "this" variable and use the flow + // analysis code to ensure that it's been fully initialized before control + // leaves the constructor. + // + public VariableInfo AddThisVariable (TypeContainer tc, Location l) + { + if (this_variable != null) + return this_variable; + + this_variable = new VariableInfo (tc, ID, l); + + if (variables == null) + variables = new Hashtable (); + variables.Add ("this", this_variable); + + return this_variable; + } + public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l) { if (variables == null) variables = new Hashtable (); - if (GetVariableType (name) != null) + VariableInfo vi = GetVariableInfo (name); + if (vi != null) { + if (vi.Block != ID) + Report.Error (136, l, "A local variable named `" + name + "' " + + "cannot be declared in this scope since it would " + + "give a different meaning to `" + name + "', which " + + "is already used in a `parent or current' scope to " + + "denote something else"); + else + Report.Error (128, l, "A local variable `" + name + "' is already " + + "defined in this scope"); + return null; + } + + if (IsVariableNameUsedInChildBlock (name)) { + Report.Error (136, l, "A local variable named `" + name + "' " + + "cannot be declared in this scope since it would " + + "give a different meaning to `" + name + "', which " + + "is already used in a `child' scope to denote something " + + "else"); return null; + } if (pars != null) { int idx = 0; Parameter p = pars.GetParameterByName (name, out idx); - if (p != null) + if (p != null) { + Report.Error (136, l, "A local variable named `" + name + "' " + + "cannot be declared in this scope since it would " + + "give a different meaning to `" + name + "', which " + + "is already used in a `parent or current' scope to " + + "denote something else"); return null; + } } - VariableInfo vi = new VariableInfo (type, l); + vi = new VariableInfo (type, name, ID, l); variables.Add (name, vi); @@ -2164,24 +2844,6 @@ namespace Mono.CSharp { return null; } - /// - /// True if the variable named @name has been defined - /// in this block - /// - public bool IsVariableDefined (string name) - { - // Console.WriteLine ("Looking up {0} in {1}", name, ID); - if (variables != null) { - if (variables.Contains (name)) - return true; - } - - if (Parent != null) - return Parent.IsVariableDefined (name); - - return false; - } - /// /// True if the variable named @name is a constant /// @@ -2203,6 +2865,16 @@ namespace Mono.CSharp { } } + Parameters parameters = null; + public Parameters Parameters { + get { + if (Parent != null) + return Parent.Parameters; + + return parameters; + } + } + /// /// A list of labels that were not used within this block /// @@ -2244,25 +2916,15 @@ namespace Mono.CSharp { count_variables = first_variable; if (variables != null) { foreach (VariableInfo vi in variables.Values) { - Report.Debug (2, "VARIABLE", vi); - - Type type = ds.ResolveType (vi.Type, false, vi.Location); - if (type == null) { + if (!vi.Resolve (ds)) { vi.Number = -1; continue; } - vi.VariableType = type; - - Report.Debug (2, "VARIABLE", vi, type, type.IsValueType, - TypeManager.IsValueType (type), - TypeManager.IsBuiltinType (type)); + vi.Number = ++count_variables; - // FIXME: we don't have support for structs yet. - if (TypeManager.IsValueType (type) && !TypeManager.IsBuiltinType (type)) - vi.Number = -1; - else - vi.Number = ++count_variables; + if (vi.StructInfo != null) + count_variables += vi.StructInfo.Count; } } @@ -2381,6 +3043,8 @@ namespace Mono.CSharp { b.UsageWarning (); } + bool has_ret = false; + public override bool Resolve (EmitContext ec) { Block prev_block = ec.CurrentBlock; @@ -2389,61 +3053,78 @@ namespace Mono.CSharp { ec.CurrentBlock = this; ec.StartFlowBranching (this); - Report.Debug (1, "RESOLVE BLOCK", StartLocation); + Report.Debug (1, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching); if (!variables_initialized) UpdateVariableInfo (ec); - foreach (Statement s in statements){ - if (s.Resolve (ec) == false) - ok = false; + ArrayList new_statements = new ArrayList (); + bool unreachable = false, warning_shown = false; + + foreach (Statement s in statements){ + if (unreachable && !(s is LabeledStatement)) { + if (!warning_shown && !(s is EmptyStatement)) { + warning_shown = true; + Warning_DeadCodeFound (s.loc); + } + + continue; + } + + if (s.Resolve (ec) == false) { + ok = false; + continue; + } + + if (s is LabeledStatement) + unreachable = false; + else + unreachable = ! ec.CurrentBranching.IsReachable (); + + new_statements.Add (s); } - Report.Debug (1, "RESOLVE BLOCK DONE", StartLocation); + statements = new_statements; - ec.EndFlowBranching (); + Report.Debug (1, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching); + + FlowReturns returns = ec.EndFlowBranching (); ec.CurrentBlock = prev_block; + + // 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_variable != null) && (returns != FlowReturns.EXCEPTION) && + !this_variable.IsAssigned (ec, loc)) + ok = false; + + if ((labels != null) && (RootContext.WarningLevel >= 2)) { + foreach (LabeledStatement label in labels.Values) + if (!label.HasBeenReferenced) + Report.Warning (164, label.Location, + "This label has not been referenced"); + } + + if ((returns == FlowReturns.ALWAYS) || + (returns == FlowReturns.EXCEPTION) || + (returns == FlowReturns.UNREACHABLE)) + has_ret = true; + return ok; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { - bool is_ret = false, this_ret = false; Block prev_block = ec.CurrentBlock; - bool warning_shown = false; ec.CurrentBlock = this; - if (CodeGen.SymbolWriter != null) { - ec.Mark (StartLocation); - - foreach (Statement s in statements) { - ec.Mark (s.loc); - - if (is_ret && !warning_shown){ - warning_shown = true; - Warning_DeadCodeFound (s.loc); - } - this_ret = s.Emit (ec); - if (this_ret) - is_ret = true; - } - - ec.Mark (EndLocation); - } else { - foreach (Statement s in statements){ - if (is_ret && !warning_shown){ - warning_shown = true; - Warning_DeadCodeFound (s.loc); - } - this_ret = s.Emit (ec); - if (this_ret) - is_ret = true; - } - } + ec.Mark (StartLocation); + foreach (Statement s in statements) + s.Emit (ec); + ec.Mark (EndLocation); ec.CurrentBlock = prev_block; - return is_ret; + return has_ret; } } @@ -2500,7 +3181,7 @@ namespace Mono.CSharp { if (e is StringConstant || e is NullLiteral){ if (required_type == TypeManager.string_type){ - converted = label; + converted = e; ILLabel = ec.ig.DefineLabel (); return true; } @@ -3049,7 +3730,8 @@ namespace Mono.CSharp { fFoundDefault = true; } } - fAllReturn &= ss.Block.Emit (ec); + bool returns = ss.Block.Emit (ec); + fAllReturn &= returns; //ig.Emit (OpCodes.Br, lblEnd); } @@ -3097,9 +3779,6 @@ namespace Mono.CSharp { ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string); ig.Emit (OpCodes.Stloc, val); } - - SwitchSection last_section; - last_section = (SwitchSection) Sections [Sections.Count-1]; foreach (SwitchSection ss in Sections){ Label sec_begin = ig.DefineLabel (); @@ -3152,15 +3831,17 @@ namespace Mono.CSharp { } } } - if (label_count != 1 && ss != last_section) + if (label_count != 1) ig.Emit (OpCodes.Br, next_test); if (null_found) ig.MarkLabel (null_target); ig.MarkLabel (sec_begin); - foreach (SwitchLabel sl in ss.Labels) + foreach (SwitchLabel sl in ss.Labels) ig.MarkLabel (sl.ILLabelCode); - if (ss.Block.Emit (ec)) + + bool returns = ss.Block.Emit (ec); + if (returns) pending_goto_end = false; else { all_return = false; @@ -3213,13 +3894,17 @@ namespace Mono.CSharp { return false; } + + if (!got_default) + ec.CurrentBranching.CreateSibling (); + ec.EndFlowBranching (); ec.Switch = old_switch; return true; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { // Store variable for comparission purposes LocalBuilder value = ec.ig.DeclareLocal (SwitchType); @@ -3276,7 +3961,7 @@ namespace Mono.CSharp { return Statement.Resolve (ec) && expr != null; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { Type type = expr.Type; bool val; @@ -3330,7 +4015,7 @@ namespace Mono.CSharp { return Block.Resolve (ec); } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { bool previous_state = ec.CheckState; bool previous_state_const = ec.ConstantCheckState; @@ -3368,7 +4053,7 @@ namespace Mono.CSharp { return ret; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { bool previous_state = ec.CheckState; bool previous_state_const = ec.ConstantCheckState; @@ -3404,7 +4089,7 @@ namespace Mono.CSharp { return val; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { bool previous_state = ec.InUnsafe; bool val; @@ -3523,7 +4208,7 @@ namespace Mono.CSharp { // and T* is implicitly convertible to the // pointer type given in the fixed statement. // - ArrayPtr array_ptr = new ArrayPtr (e); + ArrayPtr array_ptr = new ArrayPtr (e, loc); Expression converted = Expression.ConvertImplicitRequired ( ec, array_ptr, vi.VariableType, loc); @@ -3554,7 +4239,7 @@ namespace Mono.CSharp { return statement.Resolve (ec); } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { ILGenerator ig = ec.ig; @@ -3614,7 +4299,7 @@ namespace Mono.CSharp { data [i].expr.Emit (ec); ig.Emit (OpCodes.Stloc, pinned_string); - Expression sptr = new StringPtr (pinned_string); + Expression sptr = new StringPtr (pinned_string, loc); Expression converted = Expression.ConvertImplicitRequired ( ec, sptr, vi.VariableType, loc); @@ -3637,18 +4322,54 @@ namespace Mono.CSharp { } public class Catch { - public readonly Expression Type; public readonly string Name; public readonly Block Block; public readonly Location Location; + + Expression type_expr; + Type type; public Catch (Expression type, string name, Block block, Location l) { - Type = type; + type_expr = type; Name = name; Block = block; Location = l; } + + public Type CatchType { + get { + return type; + } + } + + public bool IsGeneral { + get { + return type_expr == null; + } + } + + public bool Resolve (EmitContext ec) + { + if (type_expr != null) { + type = ec.DeclSpace.ResolveType (type_expr, false, Location); + if (type == null) + return false; + + if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){ + Report.Error (155, Location, + "The type caught or thrown must be derived " + + "from System.Exception"); + return false; + } + } else + type = null; + + if (!Block.Resolve (ec)) + return false; + + return true; + } } public class Try : Statement { @@ -3680,9 +4401,14 @@ namespace Mono.CSharp { Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation); + bool old_in_try = ec.InTry; + ec.InTry = true; + if (!Block.Resolve (ec)) ok = false; + ec.InTry = old_in_try; + FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector; Report.Debug (1, "START OF CATCH BLOCKS", vector); @@ -3699,43 +4425,59 @@ namespace Mono.CSharp { vi.Number = -1; } - if (!c.Block.Resolve (ec)) + bool old_in_catch = ec.InCatch; + ec.InCatch = true; + + if (!c.Resolve (ec)) ok = false; + ec.InCatch = old_in_catch; + FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector; - if ((current.Returns == FlowReturns.NEVER) || - (current.Returns == FlowReturns.SOMETIMES)) { + if (!current.AlwaysReturns && !current.AlwaysBreaks) vector.AndLocals (current); - } } + Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching); + if (General != null){ ec.CurrentBranching.CreateSibling (); Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching); - if (!General.Block.Resolve (ec)) + bool old_in_catch = ec.InCatch; + ec.InCatch = true; + + if (!General.Resolve (ec)) ok = false; + ec.InCatch = old_in_catch; + FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector; - if ((current.Returns == FlowReturns.NEVER) || - (current.Returns == FlowReturns.SOMETIMES)) { + if (!current.AlwaysReturns && !current.AlwaysBreaks) vector.AndLocals (current); - } } - ec.CurrentBranching.CreateSiblingForFinally (); - Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector); + Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching); + + if (Fini != null) { + ec.CurrentBranching.CreateSiblingForFinally (); + Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector); + + bool old_in_finally = ec.InFinally; + ec.InFinally = true; - if (Fini != null) if (!Fini.Resolve (ec)) ok = false; - FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector; + ec.InFinally = old_in_finally; + } FlowReturns returns = ec.EndFlowBranching (); + FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector; + Report.Debug (1, "END OF FINALLY", ec.CurrentBranching, returns, vector, f_vector); if ((returns == FlowReturns.SOMETIMES) || (returns == FlowReturns.ALWAYS)) { @@ -3749,7 +4491,7 @@ namespace Mono.CSharp { return ok; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { ILGenerator ig = ec.ig; Label end; @@ -3772,13 +4514,9 @@ namespace Mono.CSharp { DeclSpace ds = ec.DeclSpace; foreach (Catch c in Specific){ - Type catch_type = ds.ResolveType (c.Type, false, c.Location); VariableInfo vi; - if (catch_type == null) - return false; - - ig.BeginCatchBlock (catch_type); + ig.BeginCatchBlock (c.CatchType); if (c.Name != null){ vi = c.Block.GetVariableInfo (c.Name); @@ -3829,10 +4567,6 @@ namespace Mono.CSharp { } } - // - // FIXME: We still do not support the expression variant of the using - // statement. - // public class Using : Statement { object expression_or_block; Statement Statement; @@ -3876,7 +4610,7 @@ namespace Mono.CSharp { if (var == null) return false; - converted_vars [i] = Expression.ConvertImplicit ( + converted_vars [i] = Expression.ConvertImplicitRequired ( ec, var, TypeManager.idisposable_type, loc); if (converted_vars [i] == null) @@ -3909,7 +4643,7 @@ namespace Mono.CSharp { bool ResolveExpression (EmitContext ec) { if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){ - conv = Expression.ConvertImplicit ( + conv = Expression.ConvertImplicitRequired ( ec, expr, TypeManager.idisposable_type, loc); if (conv == null) @@ -4017,7 +4751,7 @@ namespace Mono.CSharp { return Statement.Resolve (ec); } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { if (expression_or_block is DictionaryEntry) return EmitLocalVariableDecls (ec); @@ -4069,7 +4803,7 @@ namespace Mono.CSharp { // out to return values in ExprClass? I think they should. // if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value || - expr.eclass == ExprClass.PropertyAccess)){ + expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){ error1579 (expr.Type); return false; } @@ -4092,6 +4826,10 @@ namespace Mono.CSharp { empty = new EmptyExpression (hm.element_type); } + ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc); + ec.CurrentBranching.CreateSibling (); + + // // // FIXME: maybe we can apply the same trick we do in the // array handling to avoid creating empty and conv in some cases. @@ -4109,6 +4847,8 @@ namespace Mono.CSharp { if (!statement.Resolve (ec)) return false; + FlowReturns returns = ec.EndFlowBranching (); + return true; } @@ -4117,13 +4857,13 @@ namespace Mono.CSharp { // static MethodInfo FetchMethodMoveNext (Type t) { - MemberInfo [] move_next_list; + MemberList move_next_list; move_next_list = TypeContainer.FindMembers ( t, MemberTypes.Method, BindingFlags.Public | BindingFlags.Instance, Type.FilterName, "MoveNext"); - if (move_next_list == null || move_next_list.Length == 0) + if (move_next_list.Count == 0) return null; foreach (MemberInfo m in move_next_list){ @@ -4144,13 +4884,13 @@ namespace Mono.CSharp { // static MethodInfo FetchMethodGetCurrent (Type t) { - MemberInfo [] move_next_list; + MemberList move_next_list; move_next_list = TypeContainer.FindMembers ( t, MemberTypes.Method, BindingFlags.Public | BindingFlags.Instance, Type.FilterName, "get_Current"); - if (move_next_list == null || move_next_list.Length == 0) + if (move_next_list.Count == 0) return null; foreach (MemberInfo m in move_next_list){ @@ -4173,11 +4913,15 @@ namespace Mono.CSharp { public MethodInfo move_next; public MethodInfo get_current; public Type element_type; + public Type enumerator_type; + public bool is_disposable; public ForeachHelperMethods (EmitContext ec) { this.ec = ec; this.element_type = TypeManager.object_type; + this.enumerator_type = TypeManager.ienumerator_type; + this.is_disposable = true; } } @@ -4259,6 +5003,9 @@ namespace Mono.CSharp { return false; hm.element_type = hm.get_current.ReturnType; + hm.enumerator_type = return_type; + hm.is_disposable = TypeManager.ImplementsInterface ( + hm.enumerator_type, TypeManager.idisposable_type); return true; } @@ -4284,14 +5031,14 @@ namespace Mono.CSharp { static bool TryType (Type t, ForeachHelperMethods hm) { - MemberInfo [] mi; + MemberList mi; mi = TypeContainer.FindMembers (t, MemberTypes.Method, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, FilterEnumerator, hm); - if (mi == null || mi.Length == 0) + if (mi.Count == 0) return false; hm.get_enumerator = (MethodInfo) mi [0]; @@ -4344,8 +5091,11 @@ namespace Mono.CSharp { ILGenerator ig = ec.ig; LocalBuilder enumerator, disposable; - enumerator = ig.DeclareLocal (TypeManager.ienumerator_type); - disposable = ig.DeclareLocal (TypeManager.idisposable_type); + enumerator = ig.DeclareLocal (hm.enumerator_type); + if (hm.is_disposable) + disposable = ig.DeclareLocal (TypeManager.idisposable_type); + else + disposable = null; // // Instantiate the enumerator @@ -4369,9 +5119,13 @@ namespace Mono.CSharp { // Protect the code in a try/finalize block, so that // if the beast implement IDisposable, we get rid of it // - Label l = ig.BeginExceptionBlock (); + Label l; bool old_in_try = ec.InTry; - ec.InTry = true; + + if (hm.is_disposable) { + l = ig.BeginExceptionBlock (); + ec.InTry = true; + } Label end_try = ig.DefineLabel (); @@ -4393,25 +5147,27 @@ namespace Mono.CSharp { // // Now the finally block // - Label end_finally = ig.DefineLabel (); - bool old_in_finally = ec.InFinally; - ec.InFinally = true; - ig.BeginFinallyBlock (); + if (hm.is_disposable) { + Label end_finally = ig.DefineLabel (); + bool old_in_finally = ec.InFinally; + ec.InFinally = true; + ig.BeginFinallyBlock (); - ig.Emit (OpCodes.Ldloc, enumerator); - ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type); - ig.Emit (OpCodes.Stloc, disposable); - ig.Emit (OpCodes.Ldloc, disposable); - ig.Emit (OpCodes.Brfalse, end_finally); - ig.Emit (OpCodes.Ldloc, disposable); - ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); - ig.MarkLabel (end_finally); - ec.InFinally = old_in_finally; + ig.Emit (OpCodes.Ldloc, enumerator); + ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type); + ig.Emit (OpCodes.Stloc, disposable); + ig.Emit (OpCodes.Ldloc, disposable); + ig.Emit (OpCodes.Brfalse, end_finally); + ig.Emit (OpCodes.Ldloc, disposable); + ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void); + ig.MarkLabel (end_finally); + ec.InFinally = old_in_finally; - // The runtime generates this anyways. - // ig.Emit (OpCodes.Endfinally); + // The runtime generates this anyways. + // ig.Emit (OpCodes.Endfinally); - ig.EndExceptionBlock (); + ig.EndExceptionBlock (); + } ig.MarkLabel (ec.LoopEnd); return false; @@ -4534,7 +5290,7 @@ namespace Mono.CSharp { return false; } - public override bool Emit (EmitContext ec) + protected override bool DoEmit (EmitContext ec) { bool ret_val; @@ -4562,4 +5318,3 @@ namespace Mono.CSharp { } } } -