[mcs] C#7 type pattern matching
authorMarek Safar <marek.safar@gmail.com>
Sun, 11 Jun 2017 07:51:44 +0000 (09:51 +0200)
committerMarek Safar <marek.safar@gmail.com>
Sun, 11 Jun 2017 07:52:26 +0000 (09:52 +0200)
20 files changed:
mcs/errors/cs0128-4.cs [new file with mode: 0644]
mcs/errors/cs0136-20.cs [new file with mode: 0644]
mcs/errors/cs0165-56.cs [new file with mode: 0644]
mcs/errors/cs1644-52.cs [new file with mode: 0644]
mcs/errors/cs8116.cs [new file with mode: 0644]
mcs/errors/cs8117.cs [new file with mode: 0644]
mcs/errors/cs8122.cs [new file with mode: 0644]
mcs/errors/cs8200-4.cs [new file with mode: 0644]
mcs/errors/cs8200-5.cs [new file with mode: 0644]
mcs/errors/cs8200-6.cs [new file with mode: 0644]
mcs/errors/cs8208.cs [new file with mode: 0644]
mcs/mcs/context.cs
mcs/mcs/cs-parser.jay
mcs/mcs/ecore.cs
mcs/mcs/expression.cs
mcs/mcs/statement.cs
mcs/tests/known-issues-net_4_x
mcs/tests/test-pattern-01.cs
mcs/tests/test-pattern-02.cs
mcs/tests/ver-il-net_4_x.xml

diff --git a/mcs/errors/cs0128-4.cs b/mcs/errors/cs0128-4.cs
new file mode 100644 (file)
index 0000000..4a38920
--- /dev/null
@@ -0,0 +1,13 @@
+// CS0128: A local variable named `s' is already defined in this scope
+// Line: 12
+
+class C
+{
+       public static void Main ()
+       {
+               object o = null;
+
+               var x1 = o is string s;
+               var x2 = o is string s;
+       }
+}
diff --git a/mcs/errors/cs0136-20.cs b/mcs/errors/cs0136-20.cs
new file mode 100644 (file)
index 0000000..0a7e680
--- /dev/null
@@ -0,0 +1,13 @@
+// CS0136: A local variable named `s' cannot be declared in this scope because it would give a different meaning to `s', which is already used in a `parent or current' scope to denote something else
+// Line: 10
+
+internal class Program
+{
+       public static void Main ()
+       {
+               object o = null;
+               if (o is string s) {
+                       int s = 1;
+               }
+       }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs0165-56.cs b/mcs/errors/cs0165-56.cs
new file mode 100644 (file)
index 0000000..06eb6cc
--- /dev/null
@@ -0,0 +1,14 @@
+// CS0165: Use of unassigned local variable `s'
+// Line: 12
+
+class X
+{
+    static string Foo (object arg)
+    {
+        if (arg is string s) {
+
+        }
+
+        return s;
+    }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs1644-52.cs b/mcs/errors/cs1644-52.cs
new file mode 100644 (file)
index 0000000..039ad7d
--- /dev/null
@@ -0,0 +1,13 @@
+// CS1644: Feature `pattern matching' cannot be used because it is not part of the C# 6.0 language specification
+// Line: 9
+// Compiler options: -langversion:6
+
+class Class
+{
+       static void Foo (object arg)
+       {
+               if (arg is Type v) {
+                       return;
+               }
+       }       
+}
diff --git a/mcs/errors/cs8116.cs b/mcs/errors/cs8116.cs
new file mode 100644 (file)
index 0000000..02fa062
--- /dev/null
@@ -0,0 +1,13 @@
+// CS8116: The nullable type `byte?' pattern matching is not allowed. Consider using underlying type `byte'
+// Line: 11
+
+using System;
+
+class C
+{
+       public static void Main ()
+       {
+               object o2 = null;
+               bool r2 = o2 is Nullable<byte> t3;
+       }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs8117.cs b/mcs/errors/cs8117.cs
new file mode 100644 (file)
index 0000000..de26499
--- /dev/null
@@ -0,0 +1,10 @@
+// CS8117: Cannot use null as pattern matching operand
+// Line: 8
+
+class C
+{
+       static object Test ()
+       {
+               return null is object res;
+       }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs8122.cs b/mcs/errors/cs8122.cs
new file mode 100644 (file)
index 0000000..3f31be0
--- /dev/null
@@ -0,0 +1,14 @@
+// CS8122: An expression tree cannot contain a pattern matching operator
+// Line: 12
+
+using System;
+using System.Linq.Expressions;
+
+class X
+{
+       public static void Main ()
+       {
+               object o = 1;
+               Expression<Func<bool>> e = () => o is int y;
+       }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs8200-4.cs b/mcs/errors/cs8200-4.cs
new file mode 100644 (file)
index 0000000..fbba19f
--- /dev/null
@@ -0,0 +1,15 @@
+// CS8200: Out variable and pattern variable declarations are not allowed within constructor initializers, field initializers, or property initializers
+// Line: 8
+
+using System;
+
+public class C
+{
+       event Action H = Foo (out var res);
+
+       static Action Foo (out int arg)
+       {
+               arg = 2;
+               return null;
+       }
+}
diff --git a/mcs/errors/cs8200-5.cs b/mcs/errors/cs8200-5.cs
new file mode 100644 (file)
index 0000000..ce4e647
--- /dev/null
@@ -0,0 +1,12 @@
+// CS8200: Out variable and pattern variable declarations are not allowed within constructor initializers, field initializers, or property initializers
+// Line: 6
+
+public class C
+{
+       bool res = Foo () is string s;
+
+       static object Foo ()
+       {
+               return null;
+       }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs8200-6.cs b/mcs/errors/cs8200-6.cs
new file mode 100644 (file)
index 0000000..b93764c
--- /dev/null
@@ -0,0 +1,12 @@
+// CS8200: Out variable and pattern variable declarations are not allowed within constructor initializers, field initializers, or property initializers
+// Line: 6
+
+class X
+{
+    public static bool Test { get; } = Foo () is bool x;
+
+    static object Foo ()
+    {
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/mcs/errors/cs8208.cs b/mcs/errors/cs8208.cs
new file mode 100644 (file)
index 0000000..ca6cd1d
--- /dev/null
@@ -0,0 +1,12 @@
+// CS8208: The type `dynamic' pattern matching is not allowed
+// Line: 9
+
+static class Program
+{
+       public static void Main ()
+       {
+               object o = null;            
+               if (o is dynamic res) {
+               }
+       }
+}
index b36fc7aeaa1042af6b9772f1bd9d9d6b60cde98e..b7e773cffed98e88eb596dd77d51a3d06149de2a 100644 (file)
@@ -565,6 +565,11 @@ namespace Mono.CSharp
                        variable.SetAssigned (DefiniteAssignment, generatedAssignment);
                }
 
+               public void SetVariableAssigned (VariableInfo variable, DefiniteAssignmentBitSet da)
+               {
+                       variable.SetAssigned (da, false);
+               }
+
                public void SetStructFieldAssigned (VariableInfo variable, string name)
                {
                        variable.SetStructFieldAssigned (DefiniteAssignment, name);
index f505da1768599ccb6b44b9202205a55af2766385..2df53e916640209092711ab75dee2489d0e8f64d 100644 (file)
@@ -2642,15 +2642,10 @@ event_declarator
                $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), null);
                lbag.AddLocation ($$, GetLocation ($1));
          }
-       | COMMA IDENTIFIER ASSIGN
-         {
-               ++lexer.parsing_block;
-         }
-         event_variable_initializer
+       | COMMA IDENTIFIER ASSIGN event_variable_initializer
          {
-               --lexer.parsing_block;
                var lt = (LocatedToken) $2;       
-               $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (Expression) $5);
+               $$ = new FieldDeclarator (new SimpleMemberName (lt.Value, lt.Location), (Expression) $4);
                lbag.AddLocation ($$, GetLocation ($1), GetLocation ($3));
          }
        ;
@@ -2665,11 +2660,19 @@ event_variable_initializer
                if ((current_event_field.ModFlags & Modifiers.ABSTRACT) != 0) {
                        report.Error (74, lexer.Location, "`{0}': abstract event cannot have an initializer",
                                current_event_field.GetSignatureForError ());
-               }               
+               }
+
+               ++lexer.parsing_block;
+               current_local_parameters = ParametersCompiled.EmptyReadOnlyParameters;
+               start_block (lexer.Location);
          }
          variable_initializer
          {
                $$ = $2;
+
+               --lexer.parsing_block;
+               end_block (lexer.Location);
+               current_local_parameters = null;
          }
        ;
        
@@ -4464,12 +4467,13 @@ additive_expression
          {
                var is_expr = new Is ((Expression) $1, (Expression) $3, GetLocation ($2));
                if ($4 != null) {
-                       if (lang_version != LanguageVersion.Experimental)
-                               FeatureIsNotAvailable (GetLocation ($4), "type pattern matching");
+                       if (lang_version < LanguageVersion.V_7)
+                               FeatureIsNotAvailable (GetLocation ($4), "pattern matching");
 
                        var lt = (LocatedToken) $4;
-                       is_expr.Variable = new LocalVariable (current_block, lt.Value, lt.Location);
-                       current_block.AddLocalName (is_expr.Variable);
+                       var lv = new LocalVariable (current_block, lt.Value, lt.Location);
+                       is_expr.Variable = lv;
+                       current_block.AddLocalName (lv);
                }
 
                $$ = is_expr;
@@ -7702,8 +7706,7 @@ void start_block (Location loc)
        }
 }
 
-Block
-end_block (Location loc)
+Block end_block (Location loc)
 {
        Block retval = current_block.Explicit;
        retval.SetEndLocation (loc);
index a6c575ecc9e33267efd52365b2cc275d044cb915..73e50c4068f62902fd8f04dd4062e324366dd717 100644 (file)
@@ -239,6 +239,15 @@ namespace Mono.CSharp {
                        return null;
                }
 
+               protected void CheckExpressionVariable (ResolveContext rc)
+               {
+                       if (rc.HasAny (ResolveContext.Options.BaseInitializer | ResolveContext.Options.FieldInitializerScope)) {
+                               rc.Report.Error (8200, loc, "Out variable and pattern variable declarations are not allowed within constructor initializers, field initializers, or property initializers");
+                       } else if (rc.HasSet (ResolveContext.Options.QueryClauseScope)) {
+                               rc.Report.Error (8201, loc, "Out variable and pattern variable declarations are not allowed within a query clause");
+                       }
+               }
+
                public static void ErrorIsInaccesible (IMemberContext rc, string member, Location loc)
                {
                        rc.Module.Compiler.Report.Error (122, loc, "`{0}' is inaccessible due to its protection level", member);
@@ -638,6 +647,10 @@ namespace Mono.CSharp {
                        ec.Emit (OpCodes.Pop);
                }
 
+               public virtual void EmitPrepare (EmitContext ec)
+               {
+               }
+
                //
                // Emits the expression into temporary field variable. The method
                // should be used for await expressions only
index bb6255ce48740f0641084d38c0b30a7c82497e7f..da37657867ed0e59f43a1d9eefdd010e977e9ae8 100644 (file)
@@ -1490,6 +1490,11 @@ namespace Mono.CSharp
                        expr.EmitSideEffect (ec);
                }
 
+               public override void EmitPrepare (EmitContext ec)
+               {
+                       expr.EmitPrepare (ec);
+               }
+
                public override void FlowAnalysis (FlowAnalysisContext fc)
                {
                        expr.FlowAnalysis (fc);
@@ -1535,7 +1540,7 @@ namespace Mono.CSharp
                public override Expression CreateExpressionTree (ResolveContext ec)
                {
                        if (Variable != null)
-                               throw new NotSupportedException ();
+                               ec.Report.Error (8122, loc, "An expression tree cannot contain a pattern matching operator");
 
                        Arguments args = Arguments.CreateForExpressionTree (ec, null,
                                expr.CreateExpressionTree (ec),
@@ -1590,6 +1595,14 @@ namespace Mono.CSharp
                        ec.Emit (on_true ? OpCodes.Brtrue : OpCodes.Brfalse, target);
                }
 
+               public override void EmitPrepare (EmitContext ec)
+               {
+                       base.EmitPrepare (ec);
+
+                       if (Variable != null)
+                               Variable.CreateBuilder (ec);
+               }
+
                void EmitPatternMatch (EmitContext ec)
                {
                        var no_match = ec.DefineLabel ();
@@ -1695,7 +1708,13 @@ namespace Mono.CSharp
                                        value_on_stack = false;
                                }
 
-                               Variable.CreateBuilder (ec);
+                               //
+                               // It's ok to have variable builder create out of order. It simplified emit
+                               // of statements like while (condition) { }
+                               //
+                               if (!Variable.Created)
+                                       Variable.CreateBuilder (ec);
+                               
                                Variable.EmitAssign (ec);
 
                                if (expr_unwrap != null) {
@@ -1739,6 +1758,21 @@ namespace Mono.CSharp
                                fc.SetVariableAssigned (Variable.VariableInfo, true);
                }
 
+               public override void FlowAnalysisConditional (FlowAnalysisContext fc)
+               {
+                       if (Variable == null) {
+                               base.FlowAnalysisConditional (fc);
+                               return;
+                       }
+
+                       expr.FlowAnalysis (fc);
+
+                       fc.DefiniteAssignmentOnTrue = fc.BranchDefiniteAssignment ();
+                       fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
+
+                       fc.SetVariableAssigned (Variable.VariableInfo, fc.DefiniteAssignmentOnTrue);
+               }
+
                protected override void ResolveProbeType (ResolveContext rc)
                {
                        if (!(ProbeType is TypeExpr) && rc.Module.Compiler.Settings.Version == LanguageVersion.Experimental) {
@@ -1849,6 +1883,15 @@ namespace Mono.CSharp
 
                Expression ResolveResultExpression (ResolveContext ec)
                {
+                       if (Variable != null) {
+                               if (expr is NullLiteral) {
+                                       ec.Report.Error (8117, loc, "Cannot use null as pattern matching operand");
+                                       return this;
+                               }
+
+                               CheckExpressionVariable (ec);
+                       }
+
                        TypeSpec d = expr.Type;
                        bool d_is_nullable = false;
 
@@ -1856,7 +1899,7 @@ namespace Mono.CSharp
                        // If E is a method group or the null literal, or if the type of E is a reference
                        // type or a nullable type and the value of E is null, the result is false
                        //
-                       if (expr.IsNull || expr.eclass == ExprClass.MethodGroup)
+                       if (expr.IsNull)
                                return CreateConstantResult (ec, false);
 
                        if (d.IsNullableType) {
@@ -1870,6 +1913,11 @@ namespace Mono.CSharp
                        TypeSpec t = probe_type_expr;
                        bool t_is_nullable = false;
                        if (t.IsNullableType) {
+                               if (Variable != null) {
+                                       ec.Report.Error (8116, loc, "The nullable type `{0}' pattern matching is not allowed. Consider using underlying type `{1}'",
+                                                                        t.GetSignatureForError (), Nullable.NullableInfo.GetUnderlyingType (t).GetSignatureForError ());
+                               }
+
                                var ut = Nullable.NullableInfo.GetUnderlyingType (t);
                                if (!ut.IsGenericParameter) {
                                        t = ut;
@@ -1914,9 +1962,13 @@ namespace Mono.CSharp
                                        return ResolveGenericParameter (ec, d, tps);
 
                                if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
-                                       ec.Report.Warning (1981, 3, loc,
-                                               "Using `{0}' to test compatibility with `{1}' is identical to testing compatibility with `object'",
-                                               OperatorName, t.GetSignatureForError ());
+                                       if (Variable != null) {
+                                               ec.Report.Error (8208, loc, "The type `{0}' pattern matching is not allowed", t.GetSignatureForError ());
+                                       } else {
+                                               ec.Report.Warning (1981, 3, loc,
+                                                       "Using `{0}' to test compatibility with `{1}' is identical to testing compatibility with `object'",
+                                                       OperatorName, t.GetSignatureForError ());
+                                       }
                                }
 
                                if (TypeManager.IsGenericParameter (d))
@@ -2568,11 +2620,7 @@ namespace Mono.CSharp
 
                bool DoResolveCommon (ResolveContext rc)
                {
-                       if (rc.HasAny (ResolveContext.Options.BaseInitializer | ResolveContext.Options.FieldInitializerScope)) {
-                               rc.Report.Error (8200, loc, "Out variable and pattern variable declarations are not allowed within constructor initializers, field initializers, or property initializers");
-                       } else if (rc.HasSet (ResolveContext.Options.QueryClauseScope)) {
-                               rc.Report.Error (8201, loc, "Out variable and pattern variable declarations are not allowed within a query clause");
-                       }
+                       CheckExpressionVariable (rc);
 
                        var var_expr = VariableType as VarExpr;
                        if (var_expr != null) {
index dd02d894a8b480a97188b3d7d53a33f88d17ad2b..dd7f737139ea372f64af929377608936fd582120 100644 (file)
@@ -558,6 +558,8 @@ namespace Mono.CSharp {
                                ec.Emit (OpCodes.Br, ec.LoopBegin);
                                ec.MarkLabel (while_loop);
 
+                               expr.EmitPrepare (ec);
+
                                Statement.Emit (ec);
                        
                                ec.MarkLabel (ec.LoopBegin);
@@ -2433,6 +2435,12 @@ namespace Mono.CSharp {
                        }
                }
 
+               public bool Created {
+                       get {
+                               return builder != null;
+                       }
+               }
+
                public bool IsDeclared {
                        get {
                                return type != null;
index 51d0b2086fda73f48a672f0c03f5224f8d26104f..eac11ac15f91607127af3ecdd47cab1e7ce9215f 100644 (file)
@@ -5,3 +5,4 @@
 # csXXXX.cs IGNORE     : adds test to ignore list
 
 gtest-230.cs
+test-pattern-02.cs
index 6db67fb16726fcc16c64a92e004451554f8ff008..84517a6a57431956468f3a253352f44a53de5e93 100644 (file)
@@ -1,5 +1,3 @@
-// Compiler options: -langversion:experimental
-
 using System;
 
 class TypePattern
@@ -8,31 +6,14 @@ class TypePattern
        {
                object o = 3;
                bool r = o is System.String t1;
-               if (t1 != null)
-                       return 1;
-
                if (r)
                        return 2;
 
                if (o is string t2)
                        return 3;
 
-               if (t2 != null)
-                       return 4;
-
-               object o2 = (int?) 4;
-               bool r2 = o2 is byte? t3;
-
-               if (t3 != null)
-                       return 5;
-
-               if (r2)
-                       return 6;
-
                long? l = 5;
                bool r3 = l is long t4;
-               if (t4 != 5)
-                       return 7;
 
                if (!r3)
                        return 8;
@@ -40,4 +21,22 @@ class TypePattern
                Console.WriteLine ("ok");
                return 0;
        }
+
+       static void Test1 (object arg)
+       {
+               while (arg is int b) {
+                       b = 2;
+               }
+       }
+
+       static string Test2 (object arg)
+       {
+               if (arg is string s) {
+                       return s;
+               } else {
+                       s = "";
+               }
+               
+               return s;
+       }
 }
\ No newline at end of file
index 695775ec17939daa00e130e0fa5aeced6da913d1..2858457b2dfffaa352829ba7594b962d4f15fb5f 100644 (file)
@@ -48,14 +48,14 @@ class ConstantPattern
 
                object o4 = (byte?)255;
                var ggg = o4 is 255;
-               if (!ggg)
+               if (ggg)
                        return 8;
 
                if (o4 is null)
                        return 9;
 
                object o5 = (double)-255;
-               if (!(o5 is -byte.MaxValue))
+               if (o5 is -byte.MaxValue)
                        return 10;
 
                object o6 = MyEnum.V_4;
index fe7d9733555ec7a953d5e7666a97be586fdccfdf..5b1f686ddd765817d716aebf3bdc031df02dba1f 100644 (file)
   <test name="test-pattern-01.cs">
     <type name="TypePattern">
       <method name="Int32 Main()" attrs="150">
-        <size>227</size>
-      </method>
-      <method name="Void .ctor()" attrs="6278">
-        <size>7</size>
-      </method>
-    </type>
-  </test>
-  <test name="test-pattern-02.cs">
-    <type name="ConstantPattern">
-      <method name="Int32 Main()" attrs="150">
-        <size>609</size>
+        <size>118</size>
       </method>
       <method name="Void .ctor()" attrs="6278">
         <size>7</size>
       </method>
-    </type>
-    <type name="&lt;PatternMatchingHelper&gt;">
-      <method name="Boolean NumberMatcher(System.Object, System.Object, Boolean)" attrs="150">
-        <size>69</size>
+      <method name="Void Test1(System.Object)" attrs="145">
+        <size>24</size>
       </method>
-    </type>
-    <type name="ConstantPattern">
-      <method name="Boolean Generic[T](T)" attrs="145">
-        <size>28</size>
+      <method name="System.String Test2(System.Object)" attrs="145">
+        <size>39</size>
       </method>
     </type>
   </test>