return false;
}
- if (ec.Switch.DefaultLabel == null) {
- FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
- return false;
- }
+ ec.Switch.RegisterGotoCase (null, null);
return true;
}
/// </summary>
public class GotoCase : Statement {
Expression expr;
- SwitchLabel sl;
public GotoCase (Expression e, Location l)
{
return this.expr;
}
}
+
+ public SwitchLabel Label { get; set; }
public override bool Resolve (BlockContext ec)
{
ec.CurrentBranching.CurrentUsageVector.Goto ();
- expr = expr.Resolve (ec);
- if (expr == null)
- return false;
-
- Constant c = expr as Constant;
+ Constant c = expr.ResolveLabelConstant (ec);
if (c == null) {
- ec.Report.Error (150, expr.Location, "A constant value is expected");
return false;
}
}
- sl = ec.Switch.ResolveGotoCase (ec, res);
+ ec.Switch.RegisterGotoCase (this, res);
return true;
}
protected override void DoEmit (EmitContext ec)
{
- ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
+ ec.Emit (OpCodes.Br, Label.GetILLabel (ec));
}
protected override void CloneTo (CloneContext clonectx, Statement t)
public override bool Resolve (BlockContext bc)
{
+ if (ResolveAndReduce (bc))
+ bc.Switch.RegisterLabel (bc, this);
+
bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
return base.Resolve (bc);
// Resolves the expression, reduces it to a literal if possible
// and then converts it to the requested type.
//
- public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
- {
- Expression e = label.Resolve (ec);
-
- if (e == null)
- return false;
+ bool ResolveAndReduce (ResolveContext rc)
+ {
+ if (IsDefault)
+ return true;
- Constant c = e as Constant;
- if (c == null){
- ec.Report.Error (150, loc, "A constant value is expected");
+ var c = label.ResolveLabelConstant (rc);
+ if (c == null)
return false;
- }
- if (allow_nullable && c is NullLiteral) {
+ if (rc.Switch.IsNullable && c is NullLiteral) {
converted = c;
return true;
}
- converted = c.ImplicitConversionRequired (ec, required_type, loc);
+ converted = c.ImplicitConversionRequired (rc, rc.Switch.SwitchType, loc);
return converted != null;
}
- public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
+ public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
{
string label;
if (converted == null)
Dictionary<string, SwitchLabel> string_labels;
List<SwitchLabel> case_labels;
+ List<Tuple<GotoCase, Constant>> goto_cases;
+
/// <summary>
/// The governing switch type
/// </summary>
};
}
- //
- // Performs the basic sanity checks on the switch statement
- // (looks for duplicate keys and non-constant expressions).
- //
- // It also returns a hashtable with the keys that we will later
- // use to compute the switch tables
- //
- bool ResolveLabels (ResolveContext ec, Constant value)
+ public void RegisterLabel (ResolveContext rc, SwitchLabel sl)
{
- bool error = false;
- if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
- string_labels = new Dictionary<string, SwitchLabel> ();
- } else {
- labels = new Dictionary<long, SwitchLabel> ();
- }
-
- case_labels = new List<SwitchLabel> ();
- int default_label_index = -1;
- bool constant_label_found = false;
-
- for (int i = 0; i < block.Statements.Count; ++i) {
- var s = block.Statements[i];
-
- var sl = s as SwitchLabel;
- if (sl == null) {
- continue;
- }
-
- case_labels.Add (sl);
-
- if (sl.IsDefault) {
- if (case_default != null) {
- sl.Error_AlreadyOccurs (ec, SwitchType, case_default);
- error = true;
- }
+ case_labels.Add (sl);
- default_label_index = i;
+ if (sl.IsDefault) {
+ if (case_default != null) {
+ sl.Error_AlreadyOccurs (rc, case_default);
+ } else {
case_default = sl;
- continue;
}
- if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
- error = true;
- continue;
- }
+ return;
+ }
- try {
- if (string_labels != null) {
- string string_value = sl.Converted.GetValue () as string;
- if (string_value == null)
- case_null = sl;
- else
- string_labels.Add (string_value, sl);
+ try {
+ if (string_labels != null) {
+ string string_value = sl.Converted.GetValue () as string;
+ if (string_value == null)
+ case_null = sl;
+ else
+ string_labels.Add (string_value, sl);
+ } else {
+ if (sl.Converted is NullLiteral) {
+ case_null = sl;
} else {
- if (sl.Converted is NullLiteral) {
- case_null = sl;
- } else {
- labels.Add (sl.Converted.GetValueAsLong (), sl);
- }
+ labels.Add (sl.Converted.GetValueAsLong (), sl);
}
- } catch (ArgumentException) {
- if (string_labels != null)
- sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
- else
- sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
-
- error = true;
- }
-
- if (value != null) {
- var constant_label = constant_label_found ? null : FindLabel (value);
- if (constant_label == null || constant_label != sl)
- block.Statements[i] = new EmptyStatement (s.loc);
- else
- constant_label_found = true;
}
+ } catch (ArgumentException) {
+ if (string_labels != null)
+ sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
+ else
+ sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
}
-
- if (value != null && constant_label_found && default_label_index >= 0)
- block.Statements[default_label_index] = new EmptyStatement (case_default.loc);
-
- return !error;
}
//
if (block.Statements.Count == 0)
return true;
- var constant = new_expr as Constant;
+ if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
+ string_labels = new Dictionary<string, SwitchLabel> ();
+ } else {
+ labels = new Dictionary<long, SwitchLabel> ();
+ }
- if (!ResolveLabels (ec, constant))
- return false;
+ case_labels = new List<SwitchLabel> ();
+
+ var constant = new_expr as Constant;
//
// Don't need extra variable for constant switch or switch with
// only default case
//
- if (constant == null && (case_labels.Count - (case_default != null ? 1 : 0)) != 0) {
+ if (constant == null) {
//
// Store switch expression for comparison purposes
//
value = new_expr as VariableReference;
- if (value == null) {
- // Create temporary variable inside switch scope
+ if (value == null && !HasOnlyDefaultSection ()) {
var current_block = ec.CurrentBlock;
ec.CurrentBlock = Block;
+ // Create temporary variable inside switch scope
value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
value.Resolve (ec);
ec.CurrentBlock = current_block;
ec.EndFlowBranching ();
ec.Switch = old_switch;
+ //
+ // Check if all goto cases are valid. Needs to be done after switch
+ // is resolved becuase goto can jump forward in the scope.
+ //
+ if (goto_cases != null) {
+ foreach (var gc in goto_cases) {
+ if (gc.Item1 == null) {
+ if (DefaultLabel == null) {
+ FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
+ }
+
+ continue;
+ }
+
+ var sl = FindLabel (gc.Item2);
+ if (sl == null) {
+ FlowBranchingBlock.Error_UnknownLabel (loc, "case " + gc.Item2.GetValueAsLiteral (), ec.Report);
+ } else {
+ gc.Item1.Label = sl;
+ }
+ }
+ }
+
+ if (constant != null) {
+ ResolveUnreachableSections (ec, constant);
+ }
+
if (!ok)
return false;
return true;
}
- public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
+ bool HasOnlyDefaultSection ()
{
- var sl = FindLabel (value);
+ for (int i = 0; i < block.Statements.Count; ++i) {
+ var s = block.Statements[i] as SwitchLabel;
- if (sl == null) {
- FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
+ if (s == null || s.IsDefault)
+ continue;
+
+ return false;
}
- return sl;
+ return true;
+ }
+
+ public void RegisterGotoCase (GotoCase gotoCase, Constant value)
+ {
+ if (goto_cases == null)
+ goto_cases = new List<Tuple<GotoCase, Constant>> ();
+
+ goto_cases.Add (Tuple.Create (gotoCase, value));
}
//
string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
}
+ void ResolveUnreachableSections (BlockContext bc, Constant value)
+ {
+ var constant_label = FindLabel (value) ?? case_default;
+
+ bool found = false;
+ bool unreachable_reported = false;
+ for (int i = 0; i < block.Statements.Count; ++i) {
+ var s = block.Statements[i];
+
+ if (s is SwitchLabel) {
+ if (unreachable_reported) {
+ found = unreachable_reported = false;
+ }
+
+ found |= s == constant_label;
+ continue;
+ }
+
+ if (found) {
+ unreachable_reported = true;
+ continue;
+ }
+
+ if (!unreachable_reported) {
+ unreachable_reported = true;
+ bc.Report.Warning (162, 2, s.loc, "Unreachable code detected");
+ }
+
+ block.Statements[i] = new EmptyStatement (s.loc);
+ }
+ }
+
void DoEmitStringSwitch (EmitContext ec)
{
Label l_initialized = ec.DefineLabel ();