Resolve switch cases without look aheah look-up. Fixes #12583
authorMarek Safar <marek.safar@gmail.com>
Fri, 7 Jun 2013 16:39:33 +0000 (18:39 +0200)
committerMarek Safar <marek.safar@gmail.com>
Fri, 7 Jun 2013 16:40:37 +0000 (18:40 +0200)
mcs/errors/cs0161-4.cs [new file with mode: 0644]
mcs/mcs/ecore.cs
mcs/mcs/statement.cs
mcs/tests/test-285.cs
mcs/tests/ver-il-net_4_5.xml

diff --git a/mcs/errors/cs0161-4.cs b/mcs/errors/cs0161-4.cs
new file mode 100644 (file)
index 0000000..faeaf57
--- /dev/null
@@ -0,0 +1,15 @@
+// CS0161: `T.Main()': not all code paths return a value
+// Line: 6
+// CSC bug: The error is not reported even if it should as in other unreachable cases
+
+class T {
+       public static int Main ()
+       {
+               switch (1) {
+               case 1:
+                       return 0;
+               default:
+                       break;
+               }
+       }
+}
index abc646c2298ca53887b38152f4f35582c938539f..dd3681529e0eb04c33be636a1d283bfb610cab05 100644 (file)
@@ -511,6 +511,23 @@ namespace Mono.CSharp {
                        return e;
                }
 
+               public Constant ResolveLabelConstant (ResolveContext rc)
+               {
+                       var expr = Resolve (rc);
+                       if (expr == null)
+                               return null;
+
+                       Constant c = expr as Constant;
+                       if (c == null) {
+                               if (c.type != InternalType.ErrorType)
+                                       rc.Report.Error (150, StartLocation, "A constant value is expected");
+
+                               return null;
+                       }
+
+                       return c;
+               }
+
                public virtual void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType)
                {
                        rc.Module.Compiler.Report.Error (182, loc,
index 28744899edb7df1bb69ecd8bf515344db9233b8c..ab0b2d7bbd3f121587924327e63d0f39f4f62494 100644 (file)
@@ -1198,10 +1198,7 @@ namespace Mono.CSharp {
                                return false;
                        }
 
-                       if (ec.Switch.DefaultLabel == null) {
-                               FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
-                               return false;
-                       }
+                       ec.Switch.RegisterGotoCase (null, null);
 
                        return true;
                }
@@ -1222,7 +1219,6 @@ namespace Mono.CSharp {
        /// </summary>
        public class GotoCase : Statement {
                Expression expr;
-               SwitchLabel sl;
                
                public GotoCase (Expression e, Location l)
                {
@@ -1235,6 +1231,8 @@ namespace Mono.CSharp {
                                return this.expr;
                        }
                }
+
+               public SwitchLabel Label { get; set; }
                
                public override bool Resolve (BlockContext ec)
                {
@@ -1245,13 +1243,8 @@ namespace Mono.CSharp {
 
                        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;
                        }
 
@@ -1273,13 +1266,13 @@ namespace Mono.CSharp {
 
                        }
 
-                       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)
@@ -3629,6 +3622,9 @@ namespace Mono.CSharp {
 
                public override bool Resolve (BlockContext bc)
                {
+                       if (ResolveAndReduce (bc))
+                               bc.Switch.RegisterLabel (bc, this);
+
                        bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
 
                        return base.Resolve (bc);
@@ -3638,29 +3634,25 @@ namespace Mono.CSharp {
                // 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)
@@ -3766,6 +3758,8 @@ namespace Mono.CSharp {
                Dictionary<string, SwitchLabel> string_labels;
                List<SwitchLabel> case_labels;
 
+               List<Tuple<GotoCase, Constant>> goto_cases;
+
                /// <summary>
                ///   The governing switch type
                /// </summary>
@@ -3885,88 +3879,40 @@ namespace Mono.CSharp {
                        };
                }
 
-               //
-               // 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;
                }
                
                //
@@ -4159,24 +4105,29 @@ namespace Mono.CSharp {
                        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;
@@ -4199,6 +4150,33 @@ namespace Mono.CSharp {
                        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;
 
@@ -4215,15 +4193,26 @@ namespace Mono.CSharp {
                        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));
                }
 
                //
@@ -4284,6 +4273,38 @@ namespace Mono.CSharp {
                        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 ();
index fe9b37174e837bc68eae74c2956985b7fbc5a76b..c49949b239ac575543d245d6b734788fd7c941be 100644 (file)
@@ -1,11 +1,42 @@
-class T {
-       public static int Main ()
+class Test
+{
+       static int test1 ()
        {
-               switch (1) {
-               case 1:
-                       return 0;
-               default:
-                       break;
+               var s = "Nice";
+               switch (s) {
+               case "HI":
+                       const string x = "Nice";
+                       return 1;
+               case x:
+                       return 2;
                }
+
+               return 3;
        }
-}
+
+       public const string xx = "Not";
+       static int test2 ()
+       {
+               var s = "Nice";
+               switch (s) {
+               case "HI":
+                       const string xx = "Nice";
+                       return 1;
+               case xx:
+                       return 2;
+               }
+
+               return 3;
+       }
+
+       static int Main ()
+       {
+               if (test1 () != 2)
+                       return 1;
+
+               if (test2 () != 2)
+                       return 2;
+
+               return 0;
+       }
+}
\ No newline at end of file
index 3e0968ad09dc2b2b9a29d9751c8939749f3e2904..4306d974d6d3d80da4efd615072296b4b44efb3b 100644 (file)
     </type>\r
   </test>\r
   <test name="test-285.cs">\r
-    <type name="T">\r
-      <method name="Int32 Main()" attrs="150">\r
-        <size>11</size>\r
+    <type name="Test">\r
+      <method name="Int32 test1()" attrs="145">\r
+        <size>73</size>\r
+      </method>\r
+      <method name="Int32 test2()" attrs="145">\r
+        <size>73</size>\r
+      </method>\r
+      <method name="Int32 Main()" attrs="145">\r
+        <size>46</size>\r
       </method>\r
       <method name="Void .ctor()" attrs="6278">\r
         <size>7</size>\r
   <test name="test-510.cs">\r
     <type name="Foo">\r
       <method name="Void test39(Int32 ByRef)" attrs="145">\r
-        <size>37</size>\r
+        <size>41</size>\r
       </method>\r
       <method name="Void Main()" attrs="150">\r
         <size>29</size>\r