{
if (reachable) {
fc.UnreachableReported = false;
- return DoFlowAnalysis (fc);
+ var res = DoFlowAnalysis (fc);
+ fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
+ return res;
}
//
// Special handling cases
//
- if (this is Block || this is SwitchLabel) {
+ if (this is Block) {
return DoFlowAnalysis (fc);
}
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
{
+ fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
+
expr.FlowAnalysis (fc);
- var da = fc.BranchDefiniteAssignment ();
+ var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
+
+ fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
+ fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
var res = TrueStatement.FlowAnalysis (fc);
if (FalseStatement == null) {
- fc.DefiniteAssignment = da;
+ if (true_returns)
+ fc.DefiniteAssignment = da_false;
+ else
+ fc.DefiniteAssignment &= da_false;
+
return false;
}
- var da_true = fc.DefiniteAssignment;
if (true_returns) {
- fc.DefiniteAssignment = da;
+ fc.DefiniteAssignment = da_false;
return FalseStatement.FlowAnalysis (fc);
}
- fc.DefiniteAssignment = new DefiniteAssignmentBitSet (da);
+ var da_true = fc.DefiniteAssignment;
+
+ fc.DefiniteAssignment = da_false;
res &= FalseStatement.FlowAnalysis (fc);
- if (false_returns)
- fc.DefiniteAssignment = da_true;
- else
- fc.DefiniteAssignment &= da_true;
+ if (!TrueStatement.IsUnreachable) {
+ if (false_returns || FalseStatement.IsUnreachable)
+ fc.DefiniteAssignment = da_true;
+ else
+ fc.DefiniteAssignment &= da_true;
+ }
return res;
}
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
{
- var dat = fc.BranchDefiniteAssignment ();
-
var res = Statement.FlowAnalysis (fc);
+ fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
expr.FlowAnalysis (fc);
- fc.DefiniteAssignment = dat | fc.DefiniteAssignment;
+ fc.DefiniteAssignment = fc.DefiniteAssignmentOnFalse;
if (res && !iterator_reachable)
return !end_reachable;
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
{
+ fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
+
expr.FlowAnalysis (fc);
- DefiniteAssignmentBitSet das;
-
- das = fc.BranchDefiniteAssignment ();
+ fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
+ var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
+ fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
Statement.FlowAnalysis (fc);
// Special case infinite while with breaks
//
if (end_reachable_das != null) {
- das = DefiniteAssignmentBitSet.And (end_reachable_das);
+ da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
end_reachable_das = null;
}
- fc.DefiniteAssignment = das;
+ fc.DefiniteAssignment = da_false;
if (infinite && !end_reachable)
return true;
public class For : LoopStatement
{
bool infinite, empty, iterator_reachable, end_reachable;
- List<DefiniteAssignmentBitSet> end_reachability;
+ List<DefiniteAssignmentBitSet> end_reachable_das;
public For (Location l)
: base (null)
{
Initializer.FlowAnalysis (fc);
- if (Condition != null)
- Condition.FlowAnalysis (fc);
+ DefiniteAssignmentBitSet da_false;
+ if (Condition != null) {
+ fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
- var das = fc.BranchDefiniteAssignment ();
+ Condition.FlowAnalysis (fc);
+ fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
+ da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
+ fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
+ } else {
+ da_false = fc.BranchDefiniteAssignment ();
+ }
Statement.FlowAnalysis (fc);
Iterator.FlowAnalysis (fc);
- fc.DefiniteAssignment = das;
+ //
+ // Special case infinite for with breaks
+ //
+ if (end_reachable_das != null) {
+ da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
+ end_reachable_das = null;
+ }
+
+ fc.DefiniteAssignment = da_false;
+
+ if (infinite && !end_reachable)
+ return true;
- return infinite && !end_reachable;
+ return false;
}
public override Reachability MarkReachable (Reachability rc)
if (!infinite)
return;
- if (end_reachability == null)
- end_reachability = new List<DefiniteAssignmentBitSet> ();
+ if (end_reachable_das == null)
+ end_reachable_das = new List<DefiniteAssignmentBitSet> ();
- end_reachability.Add (fc.DefiniteAssignment);
+ end_reachable_das.Add (fc.DefiniteAssignment);
}
public override void SetEndReachable ()
return true;
if (fc.TryFinally != null) {
- fc.TryFinally.RegisterOutParametersCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
+ fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
} else {
- fc.ParametersBlock.CheckOutParametersAssignment (fc);
+ fc.ParametersBlock.CheckControlExit (fc);
}
return true;
expr = EmptyExpression.Null;
return true;
}
+
+ if (storey.ReturnType.IsGenericTask)
+ block_return_type = storey.ReturnType.TypeArguments[0];
}
if (ec.CurrentIterator != null) {
Error_ReturnFromIterator (ec);
- } else if (ec.ReturnType != InternalType.ErrorType) {
+ } else if (block_return_type != InternalType.ErrorType) {
ec.Report.Error (126, loc,
"An object of a type convertible to `{0}' is required for the return statement",
- ec.ReturnType.GetSignatureForError ());
+ block_return_type.GetSignatureForError ());
}
return false;
protected override void CloneTo (CloneContext clonectx, Statement target)
{
- // nothing to clone
+ var t = (LabeledStatement) target;
+
+ t.block = clonectx.RemapBlockCopy (block);
}
public override bool Resolve (BlockContext bc)
return;
referenced = true;
+ MarkReachable (rc);
//
// Label is final target when goto jumps out of try block with
// explicit label not just marker
//
if (finalTarget) {
- MarkReachable (rc);
this.finalTarget = true;
return;
}
{
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
}
-
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ if (!rc.IsUnreachable) {
+ var label = switch_statement.DefaultLabel;
+ if (label.IsUnreachable) {
+ label.MarkReachable (rc);
+ switch_statement.Block.ScanGotoJump (label);
+ }
+ }
+
+ return base.MarkReachable (rc);
+ }
+
public override object Accept (StructuralVisitor visitor)
{
return visitor.Visit (this);
ec.Switch.RegisterGotoCase (this, res);
base.Resolve (ec);
+ expr = res;
return true;
}
target.expr = expr.Clone (clonectx);
}
+
+ public override Reachability MarkReachable (Reachability rc)
+ {
+ if (!rc.IsUnreachable) {
+ var label = switch_statement.FindLabel ((Constant) expr);
+ if (label.IsUnreachable) {
+ label.MarkReachable (rc);
+ switch_statement.Block.ScanGotoJump (label);
+ }
+ }
+
+ return base.MarkReachable (rc);
+ }
public override object Accept (StructuralVisitor visitor)
{
public abstract class SwitchGoto : Statement
{
protected bool unwind_protect;
+ protected Switch switch_statement;
protected SwitchGoto (Location loc)
{
CheckExitBoundaries (bc, bc.Switch.Block);
unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
+ switch_statement = bc.Switch;
return true;
}
statements.Add (s);
}
- public int AssignableSlots {
- get {
- // FIXME: HACK, we don't know the block available variables count now, so set this high enough
- return 4096;
-// return assignable_slots;
- }
- }
-
public LabeledStatement LookupLabel (string name)
{
- return ParametersBlock.TopBlock.GetLabel (name, this);
+ return ParametersBlock.GetLabel (name, this);
}
public override Reachability MarkReachable (Reachability rc)
end_unreachable = s.FlowAnalysis (fc);
if (s.IsUnreachable) {
- //
- // This is kind of a hack, switch label is unreachable because we need to mark
- // switch section end but at the same time we need to run Emit on it
- //
- if (s is SwitchLabel)
- continue;
-
statements[startIndex] = new EmptyStatement (s.loc);
continue;
}
// X label is reachable only via goto not as another statement after if. We need
// this for flow-analysis only to carry variable info correctly.
//
- if (end_unreachable) { // s is ExitStatement) {
+ if (end_unreachable) {
for (++startIndex; startIndex < statements.Count; ++startIndex) {
s = statements[startIndex];
if (s is SwitchLabel) {
}
var rc = new Reachability ();
- for (; i < statements.Count; ++i) {
+ for (++i; i < statements.Count; ++i) {
var s = statements[i];
rc = s.MarkReachable (rc);
if (rc.IsUnreachable)
#if DEBUG
public override string ToString ()
{
- return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
+ return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
}
#endif
{
Block target = (Block) t;
#if DEBUG
- target.clone_id = clone_id_counter++;
+ target.clone_id = ++clone_id_counter;
#endif
clonectx.AddBlockMap (this, target);
protected bool resolved;
protected ToplevelBlock top_block;
protected StateMachine state_machine;
+ protected Dictionary<string, object> labels;
public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
: base (parent, 0, start, start)
//
// Checks whether all `out' parameters have been assigned.
//
- public void CheckOutParametersAssignment (FlowAnalysisContext fc)
+ public void CheckControlExit (FlowAnalysisContext fc)
{
- CheckOutParametersAssignment (fc, fc.DefiniteAssignment);
+ CheckControlExit (fc, fc.DefiniteAssignment);
}
- public void CheckOutParametersAssignment (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
+ public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
{
if (parameter_info == null)
return;
}
}
+ protected override void CloneTo (CloneContext clonectx, Statement t)
+ {
+ base.CloneTo (clonectx, t);
+
+ var target = (ParametersBlock) t;
+
+ //
+ // Clone label statements as well as they contain block reference
+ //
+ var pb = this;
+ while (true) {
+ if (pb.labels != null) {
+ target.labels = new Dictionary<string, object> ();
+
+ foreach (var entry in pb.labels) {
+ var list = entry.Value as List<LabeledStatement>;
+
+ if (list != null) {
+ var list_clone = new List<LabeledStatement> ();
+ foreach (var lentry in list) {
+ list_clone.Add (RemapLabeledStatement (lentry, lentry.Block, clonectx.RemapBlockCopy (lentry.Block)));
+ }
+
+ target.labels.Add (entry.Key, list_clone);
+ } else {
+ var labeled = (LabeledStatement) entry.Value;
+ target.labels.Add (entry.Key, RemapLabeledStatement (labeled, labeled.Block, clonectx.RemapBlockCopy (labeled.Block)));
+ }
+ }
+
+ break;
+ }
+
+ if (pb.Parent == null)
+ break;
+
+ pb = pb.Parent.ParametersBlock;
+ }
+ }
+
public override Expression CreateExpressionTree (ResolveContext ec)
{
if (statements.Count == 1) {
var res = base.DoFlowAnalysis (fc);
if (HasReachableClosingBrace)
- CheckOutParametersAssignment (fc);
+ CheckControlExit (fc);
return res;
}
+ public LabeledStatement GetLabel (string name, Block block)
+ {
+ //
+ // Cloned parameters blocks can have their own cloned version of top-level labels
+ //
+ if (labels == null) {
+ if (Parent != null)
+ return Parent.ParametersBlock.GetLabel (name, block);
+
+ return null;
+ }
+
+ object value;
+ if (!labels.TryGetValue (name, out value)) {
+ return null;
+ }
+
+ var label = value as LabeledStatement;
+ Block b = block;
+ if (label != null) {
+ do {
+ if (label.Block == b)
+ return label;
+ b = b.Parent;
+ } while (b != null);
+ } else {
+ List<LabeledStatement> list = (List<LabeledStatement>) value;
+ for (int i = 0; i < list.Count; ++i) {
+ label = list[i];
+ if (label.Block == b)
+ return label;
+ }
+ }
+
+ return null;
+ }
+
public ParameterInfo GetParameterInfo (Parameter p)
{
for (int i = 0; i < parameters.Count; ++i) {
}
}
+ static LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block src, Block dst)
+ {
+ var src_stmts = src.Statements;
+ for (int i = 0; i < src_stmts.Count; ++i) {
+ if (src_stmts[i] == stmt)
+ return (LabeledStatement) dst.Statements[i];
+ }
+
+ throw new InternalErrorException ("Should never be reached");
+ }
+
public override bool Resolve (BlockContext bc)
{
// TODO: if ((flags & Flags.Resolved) != 0)
return false;
} catch (Exception e) {
- if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter)
+ if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
throw;
if (bc.CurrentBlock != null) {
} else {
bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
}
-
- if (bc.Module.Compiler.Settings.DebugFlags > 0)
- throw;
}
//
LocalVariable this_variable;
CompilerContext compiler;
Dictionary<string, object> names;
- Dictionary<string, object> labels;
List<ExplicitBlock> this_references;
return false;
}
- public LabeledStatement GetLabel (string name, Block block)
- {
- if (labels == null)
- return null;
-
- object value;
- if (!labels.TryGetValue (name, out value)) {
- return null;
- }
-
- var label = value as LabeledStatement;
- Block b = block;
- if (label != null) {
- do {
- if (label.Block == b.Original)
- return label;
- b = b.Parent;
- } while (b != null);
- } else {
- List<LabeledStatement> list = (List<LabeledStatement>) value;
- for (int i = 0; i < list.Count; ++i) {
- label = list[i];
- if (label.Block == b.Original)
- return label;
- }
- }
-
- return null;
- }
-
// <summary>
// This is used by non-static `struct' constructors which do not have an
// initializer - in this case, the constructor must initialize all of the
this_variable.PrepareAssignmentAnalysis (bc);
}
- public bool IsThisAssigned (FlowAnalysisContext fc)
+ public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
{
- return this_variable == null || this_variable.IsThisAssigned (fc, this);
- }
-
- protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
- {
- var res = base.DoFlowAnalysis (fc);
-
//
// If we're a non-static struct constructor which doesn't have an
// initializer, then we must initialize all of the struct's fields.
//
- IsThisAssigned (fc);
- return res;
+ if (this_variable != null)
+ this_variable.IsThisAssigned (fc, this);
+
+ base.CheckControlExit (fc, dat);
}
public override void Emit (EmitContext ec)
if ((flags & Flags.NoFlowAnalysis) != 0)
return true;
- var fc = new FlowAnalysisContext (bc.Module.Compiler, this);
+ var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
try {
FlowAnalysis (fc);
} catch (Exception e) {
if (!SectionStart)
return false;
- if (IsUnreachable) {
- fc.DefiniteAssignment = new DefiniteAssignmentBitSet (fc.SwitchInitialDefinitiveAssignment);
- } else {
- fc.Report.Error (163, Location,
- "Control cannot fall through from one case label `{0}' to another", GetSignatureForError ());
- }
-
+ fc.DefiniteAssignment = new DefiniteAssignmentBitSet (fc.SwitchInitialDefinitiveAssignment);
return false;
}
return visitor.Visit (this);
}
- string GetSignatureForError ()
+ public string GetSignatureForError ()
{
string label;
if (converted == null)
}
}
+ class MissingBreak : Statement
+ {
+ SwitchLabel label;
+
+ public MissingBreak (SwitchLabel sl)
+ {
+ this.label = sl;
+ this.loc = sl.loc;
+ }
+
+ protected override void DoEmit (EmitContext ec)
+ {
+ }
+
+ protected override void CloneTo (CloneContext clonectx, Statement target)
+ {
+ }
+
+ protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
+ {
+ fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
+ label.GetSignatureForError ());
+
+ return true;
+ }
+ }
+
public Expression Expr;
//
}
}
- SwitchLabel FindLabel (Constant value)
+ public SwitchLabel FindLabel (Constant value)
{
SwitchLabel sl = null;
base.MarkReachable (rc);
+ if (block.Statements.Count == 0)
+ return rc;
+
SwitchLabel constant_label = null;
var constant = new_expr as Constant;
+
if (constant != null) {
constant_label = FindLabel (constant) ?? case_default;
if (constant_label == null) {
}
}
- var switch_rc = rc;
var section_rc = new Reachability ();
SwitchLabel prev_label = null;
var s = block.Statements[i];
var sl = s as SwitchLabel;
- if (sl != null) {
- if (!sl.SectionStart) {
- sl.MarkReachable (section_rc);
+ if (sl != null && sl.SectionStart) {
+ //
+ // Section is marked already via constant switch or goto case
+ //
+ if (!sl.IsUnreachable) {
+ section_rc = new Reachability ();
continue;
}
- if (prev_label == null) {
- prev_label = sl;
- switch_rc = Reachability.CreateUnreachable ();
-
- if (constant_label != null && sl != constant_label)
- section_rc = Reachability.CreateUnreachable ();
-
- continue;
+ if (section_rc.IsUnreachable) {
+ section_rc = new Reachability ();
+ } else {
+ if (prev_label != null) {
+ sl.SectionStart = false;
+ s = new MissingBreak (prev_label);
+ s.MarkReachable (rc);
+ block.Statements.Insert (i - 1, s);
+ ++i;
+ }
}
- //
- // Small trick, using unreachable flag for label means
- // the label section does not fallthrough
- //
- prev_label.MarkReachable (section_rc);
-
prev_label = sl;
- switch_rc &= section_rc;
- section_rc = new Reachability ();
- if (constant_label != null && sl != constant_label)
+ if (constant_label != null && constant_label != sl)
section_rc = Reachability.CreateUnreachable ();
-
- continue;
}
section_rc = s.MarkReachable (section_rc);
}
- if (prev_label != null) {
- prev_label.MarkReachable (section_rc);
- switch_rc &= section_rc;
+ if (!section_rc.IsUnreachable && prev_label != null) {
+ prev_label.SectionStart = false;
+ var s = new MissingBreak (prev_label);
+ s.MarkReachable (rc);
+ block.Statements.Add (s);
}
//
if (end_reachable)
return rc;
- return switch_rc;
+ return Reachability.CreateUnreachable ();
}
public void RegisterGotoCase (GotoCase gotoCase, Constant value)
{
if (value == null) {
//
- // Constant switch, we already done the work
+ // Constant switch, we've already done the work if there is only 1 label
+ // referenced
//
+ int reachable = 0;
+ foreach (var sl in case_labels) {
+ if (sl.IsUnreachable)
+ continue;
+
+ if (reachable++ > 0) {
+ var constant = (Constant) new_expr;
+ var constant_label = FindLabel (constant) ?? case_default;
+
+ ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
+ break;
+ }
+ }
+
return;
}
}
}
- public void RegisterOutParametersCheck (DefiniteAssignmentBitSet vector)
+ public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
{
if (try_exit_dat == null)
try_exit_dat = new List<DefiniteAssignmentBitSet> ();
// executed before exit
//
foreach (var try_da_part in try_exit_dat)
- fc.ParametersBlock.CheckOutParametersAssignment (fc, fc.DefiniteAssignment | try_da_part);
+ fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
try_exit_dat = null;
}
return;
}
- bc.Report.SymbolRelatedToPreviousError (type);
- var loc = type_expr == null ? initializer.Location : type_expr.Location;
- bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
- type.GetSignatureForError ());
+ if (type != InternalType.ErrorType) {
+ bc.Report.SymbolRelatedToPreviousError (type);
+ var loc = type_expr == null ? initializer.Location : type_expr.Location;
+ bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
+ type.GetSignatureForError ());
+ }
return;
}