[mcs] type pattern expression
authorMarek Safar <marek.safar@gmail.com>
Wed, 27 Aug 2014 15:56:47 +0000 (17:56 +0200)
committerMarek Safar <marek.safar@gmail.com>
Thu, 28 Aug 2014 14:35:59 +0000 (16:35 +0200)
mcs/mcs/cs-parser.jay
mcs/mcs/expression.cs
mcs/tests/test-pattern-01.cs [new file with mode: 0644]

index b3cd88ad6c4b30610eb56753a33b6f7a9032645d..726b1f6dcdd3a442f01b3273228c570a4dc5f031 100644 (file)
@@ -4262,10 +4262,20 @@ additive_expression
          {
                $$ = new As ((Expression) $1, (Expression) $3, GetLocation ($2));
          }
-       | additive_expression IS type
+       | additive_expression IS type opt_identifier
          {
-               $$ = new Is ((Expression) $1, (Expression) $3, GetLocation ($2));
-         }       
+               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");
+
+                       var lt = (LocatedToken) $4;
+                       is_expr.Variable = new LocalVariable (current_block, lt.Value, lt.Location);
+                       current_block.AddLocalName (is_expr.Variable);
+               }
+
+               $$ = is_expr;
+         }
        | additive_expression PLUS error 
          {
                Error_SyntaxError (yyToken);
index 5443ab37ac7f2b7482b938d79c26c0ca9c53308a..4fa922c0110f8f4278cecea17e40d6b036d503ac 100644 (file)
@@ -1502,64 +1502,125 @@ namespace Mono.CSharp
                        get { return "is"; }
                }
 
+               public LocalVariable Variable { get; set; }
+
                public override Expression CreateExpressionTree (ResolveContext ec)
                {
+                       if (Variable != null)
+                               throw new NotSupportedException ();
+
                        Arguments args = Arguments.CreateForExpressionTree (ec, null,
                                expr.CreateExpressionTree (ec),
                                new TypeOf (probe_type_expr, loc));
 
                        return CreateExpressionFactoryCall (ec, "TypeIs", args);
                }
-               
-               public override void Emit (EmitContext ec)
+
+               Expression CreateConstantResult (ResolveContext rc, bool result)
                {
-                       if (expr_unwrap != null) {
-                               expr_unwrap.EmitCheck (ec);
-                               return;
-                       }
+                       if (result)
+                               rc.Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
+                                       probe_type_expr.GetSignatureForError ());
+                       else
+                               rc.Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
+                                       probe_type_expr.GetSignatureForError ());
 
-                       expr.Emit (ec);
+                       var c = new BoolConstant (rc.BuiltinTypes, result, loc);
+                       return expr.IsSideEffectFree ?
+                               ReducedExpression.Create (c, this) :
+                               new SideEffectConstant (c, this, loc);
+               }
 
-                       // Only to make verifier happy
-                       if (probe_type_expr.IsGenericParameter && TypeSpec.IsValueType (expr.Type))
-                               ec.Emit (OpCodes.Box, expr.Type);
+               public override void Emit (EmitContext ec)
+               {
+                       EmitLoad (ec);
 
-                       ec.Emit (OpCodes.Isinst, probe_type_expr);
-                       ec.EmitNull ();
-                       ec.Emit (OpCodes.Cgt_Un);
+                       if (expr_unwrap == null) {
+                               ec.EmitNull ();
+                               ec.Emit (OpCodes.Cgt_Un);
+                       }
                }
 
                public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
                {
+                       EmitLoad (ec);
+                       ec.Emit (on_true ? OpCodes.Brtrue : OpCodes.Brfalse, target);
+               }
+
+               void EmitLoad (EmitContext ec)
+               {
+                       Label no_value_label = new Label ();
+
                        if (expr_unwrap != null) {
                                expr_unwrap.EmitCheck (ec);
+
+                               if (Variable == null)
+                                       return;
+
+                               ec.Emit (OpCodes.Dup);
+                               no_value_label = ec.DefineLabel ();
+                               ec.Emit (OpCodes.Brfalse_S, no_value_label);
+                               expr_unwrap.Emit (ec);
                        } else {
                                expr.Emit (ec);
+
+                               // Only to make verifier happy
+                               if (probe_type_expr.IsGenericParameter && TypeSpec.IsValueType (expr.Type))
+                                       ec.Emit (OpCodes.Box, expr.Type);
+
                                ec.Emit (OpCodes.Isinst, probe_type_expr);
-                       }                       
-                       ec.Emit (on_true ? OpCodes.Brtrue : OpCodes.Brfalse, target);
+                       }
+
+                       if (Variable != null) {
+                               bool value_on_stack;
+                               if (probe_type_expr.IsGenericParameter || probe_type_expr.IsNullableType) {
+                                       ec.Emit (OpCodes.Dup);
+                                       ec.Emit (OpCodes.Unbox_Any, probe_type_expr);
+                                       value_on_stack = true;
+                               } else {
+                                       value_on_stack = false;
+                               }
+
+                               Variable.CreateBuilder (ec);
+                               Variable.EmitAssign (ec);
+
+                               if (expr_unwrap != null) {
+                                       ec.MarkLabel (no_value_label);
+                               } else if (!value_on_stack) {
+                                       Variable.Emit (ec);
+                               }
+                       }
                }
 
-               Expression CreateConstantResult (ResolveContext rc, bool result)
+               protected override Expression DoResolve (ResolveContext rc)
                {
-                       if (result)
-                               rc.Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
-                                       probe_type_expr.GetSignatureForError ());
-                       else
-                               rc.Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
-                                       probe_type_expr.GetSignatureForError ());
+                       if (base.DoResolve (rc) == null)
+                               return null;
 
-                       var c = new BoolConstant (rc.BuiltinTypes, result, loc);
-                       return expr.IsSideEffectFree ?
-                               ReducedExpression.Create (c, this) :
-                               new SideEffectConstant (c, this, loc);
+                       var res = ResolveResultExpression (rc);
+                       if (Variable != null) {
+                               if (res is Constant)
+                                       throw new NotImplementedException ("constant in type pattern matching");
+
+                               Variable.Type = probe_type_expr;
+                               var bc = rc as BlockContext;
+                               if (bc != null)
+                                       Variable.PrepareAssignmentAnalysis (bc);
+                       }
+
+                       return res;
                }
 
-               protected override Expression DoResolve (ResolveContext ec)
+               public override void FlowAnalysis (FlowAnalysisContext fc)
                {
-                       if (base.DoResolve (ec) == null)
-                               return null;
+                       base.FlowAnalysis (fc);
+
+                       if (Variable != null)
+                               fc.SetVariableAssigned (Variable.VariableInfo, true);
+               }
 
+               Expression ResolveResultExpression (ResolveContext ec)
+               {
                        TypeSpec d = expr.Type;
                        bool d_is_nullable = false;
 
@@ -1596,7 +1657,7 @@ namespace Mono.CSharp
                                        // D and T are the same value types but D can be null
                                        //
                                        if (d_is_nullable && !t_is_nullable) {
-                                               expr_unwrap = Nullable.Unwrap.Create (expr, false);
+                                               expr_unwrap = Nullable.Unwrap.Create (expr, true);
                                                return this;
                                        }
                                        
diff --git a/mcs/tests/test-pattern-01.cs b/mcs/tests/test-pattern-01.cs
new file mode 100644 (file)
index 0000000..f9d627f
--- /dev/null
@@ -0,0 +1,43 @@
+// Compiler options: -langversion:experimental
+
+using System;
+
+class TypePattern
+{
+       public static int Main ()
+       {
+               object o = 3;
+               bool r = o is 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;
+
+               Console.WriteLine ("ok");
+               return 0;
+       }
+}
\ No newline at end of file