Implement postfix operations involving an implicit user operator.
authorMarek Safar <marek.safar@gmail.com>
Mon, 20 Sep 2010 19:12:46 +0000 (20:12 +0100)
committerMarek Safar <marek.safar@gmail.com>
Tue, 21 Sep 2010 09:50:08 +0000 (10:50 +0100)
mcs/errors/cs0029-13.cs [new file with mode: 0644]
mcs/mcs/driver.cs
mcs/mcs/ecore.cs
mcs/mcs/expression.cs
mcs/tests/test-450.cs [new file with mode: 0644]
mcs/tests/ver-il-gmcs.xml

diff --git a/mcs/errors/cs0029-13.cs b/mcs/errors/cs0029-13.cs
new file mode 100644 (file)
index 0000000..9d705b2
--- /dev/null
@@ -0,0 +1,16 @@
+// CS0029: Cannot implicitly convert type `int' to `A'
+// Line: 14
+
+class A
+{
+       public static implicit operator int (A x)
+       {
+               return 1;
+       }
+       
+       public static void Main ()
+       {
+               var a = new A ();
+               a++;
+       }
+}
index 372c06c738952fb17f033dca343a5864fbdeef78..d9b0ce0f0a6f9e59e91da962827b8ee4d091a341 100644 (file)
@@ -2037,6 +2037,7 @@ namespace Mono.CSharp
                        Parameter.Reset ();
 
                        Unary.Reset ();
+                       UnaryMutator.Reset ();
                        Binary.Reset ();
                        ConstantFold.Reset ();
                        CastFromDecimal.Reset ();
index 1547bdf467cbbfea66775e9e2c6c370a94b00600..03fb0abb0ecf2cb268e68ca03ac0cd34b01c9465 100644 (file)
@@ -366,6 +366,11 @@ namespace Mono.CSharp {
                        ec.Report.Error (131, loc, "The left-hand side of an assignment must be a variable, a property or an indexer");
                }
 
+               protected void Error_VoidPointerOperation (ResolveContext rc)
+               {
+                       rc.Report.Error (242, loc, "The operation in question is undefined on void pointers");
+               }
+
                public ResolveFlags ExprClassToResolveFlags {
                        get {
                                switch (eclass) {
index 3bdd95d8332b3a2df2c34fb43c99782f07239e47..5f4de1e464b45923a32059f0cb2cd50dd77f22a0 100644 (file)
@@ -857,7 +857,7 @@ namespace Mono.CSharp {
                        }
 
                        if (expr.Type == TypeManager.void_ptr_type) {
-                               ec.Report.Error (242, loc, "The operation in question is undefined on void pointers");
+                               Error_VoidPointerOperation (ec);
                                return null;
                        }
 
@@ -974,6 +974,8 @@ namespace Mono.CSharp {
                // Holds the real operation
                Expression operation;
 
+               static TypeSpec[] predefined;
+
                public UnaryMutator (Mode m, Expression e, Location loc)
                {
                        mode = m;
@@ -986,6 +988,29 @@ namespace Mono.CSharp {
                        return new SimpleAssign (this, this).CreateExpressionTree (ec);
                }
 
+               void CreatePredefinedOperators ()
+               {
+                       //
+                       // Predefined ++ and -- operators exist for the following types: 
+                       // sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal
+                       //
+                       predefined = new TypeSpec[] {
+                               TypeManager.int32_type,
+
+                               TypeManager.sbyte_type,
+                               TypeManager.byte_type,
+                               TypeManager.short_type,
+                               TypeManager.ushort_type,
+                               TypeManager.uint32_type,
+                               TypeManager.int64_type,
+                               TypeManager.uint64_type,
+                               TypeManager.char_type,
+                               TypeManager.float_type,
+                               TypeManager.double_type,
+                               TypeManager.decimal_type
+                       };
+               }
+
                protected override Expression DoResolve (ResolveContext ec)
                {
                        expr = expr.Resolve (ec);
@@ -1011,7 +1036,108 @@ namespace Mono.CSharp {
 
                        eclass = ExprClass.Value;
                        type = expr.Type;
-                       return ResolveOperator (ec);
+
+                       if (expr is RuntimeValueExpression) {
+                               operation = expr;
+                       } else {
+                               // Use itself at the top of the stack
+                               operation = new EmptyExpression (type);
+                       }
+
+                       //
+                       // The operand of the prefix/postfix increment decrement operators
+                       // should be an expression that is classified as a variable,
+                       // a property access or an indexer access
+                       //
+                       // TODO: Move to parser, expr is ATypeNameExpression
+                       if (expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess) {
+                               expr = expr.ResolveLValue (ec, expr);
+                       } else {
+                               ec.Report.Error (1059, loc, "The operand of an increment or decrement operator must be a variable, property or indexer");
+                       }
+
+                       //
+                       // Step 1: Try to find a user operator, it has priority over predefined ones
+                       //
+                       var user_op = IsDecrement ? Operator.OpType.Decrement : Operator.OpType.Increment;
+                       var methods = MemberCache.GetUserOperator (type, user_op, false);
+
+                       if (methods != null) {
+                               Arguments args = new Arguments (1);
+                               args.Add (new Argument (expr));
+
+                               var res = new OverloadResolver (methods, OverloadResolver.Restrictions.BaseMembersIncluded | OverloadResolver.Restrictions.NoBaseMembers, loc);
+                               var method = res.ResolveOperator (ec, ref args);
+                               if (method == null)
+                                       return null;
+
+                               args[0].Expr = operation;
+                               operation = new UserOperatorCall (method, args, null, loc);
+                               operation = Convert.ImplicitConversionRequired (ec, operation, type, loc);
+                               return this;
+                       }
+
+                       //
+                       // Step 2: Try predefined types
+                       //
+                       if (predefined == null)
+                               CreatePredefinedOperators ();
+
+                       // Predefined without user conversion first for speed-up
+                       Expression source = null;
+                       bool primitive_type = false;
+                       foreach (var t in predefined) {
+                               if (t == type) {
+                                       source = operation;
+                                       primitive_type = true;
+                                       break;
+                               }
+                       }
+
+                       // ++/-- on pointer variables of all types except void*
+                       if (source == null && type.IsPointer) {
+                               if (type == TypeManager.void_ptr_type) {
+                                       Error_VoidPointerOperation (ec);
+                                       return null;
+                               }
+
+                               source = operation;
+                       }
+
+                       if (source == null) {
+                               // LAMESPEC: It should error on ambiguous operators but that would make us incompatible
+                               foreach (var t in predefined) {
+                                       source = Convert.ImplicitUserConversion (ec, operation, t, loc);
+                                       if (source != null) {
+                                               break;
+                                       }
+                               }
+                       }
+
+                       // ++/-- on enum types
+                       if (source == null && type.IsEnum)
+                               source = operation;
+
+                       if (source == null) {
+                               Unary.Error_OperatorCannotBeApplied (ec, loc, Operator.GetName (user_op), type);
+                               return null;
+                       }
+
+                       var one = new IntConstant (1, loc);
+                       var op = IsDecrement ? Binary.Operator.Subtraction : Binary.Operator.Addition;
+                       operation = new Binary (op, source, one, loc);
+                       operation = operation.Resolve (ec);
+                       if (operation == null)
+                               throw new NotImplementedException ("should not be reached");
+
+                       if (operation.Type != type) {
+                               if (primitive_type)
+                                       operation = Convert.ExplicitNumericConversion (operation, type);
+                               else
+                                       operation = Convert.ImplicitConversionRequired (ec, operation, type, loc);
+                       }
+
+                       return this;
                }
 
                void EmitCode (EmitContext ec, bool is_expr)
@@ -1057,18 +1183,6 @@ namespace Mono.CSharp {
                        get { return (mode & Mode.IsDecrement) != 0; }
                }
 
-               //
-               //   Returns whether an object of type `t' can be incremented
-               //   or decremented with add/sub (ie, basically whether we can
-               //   use pre-post incr-decr operations on it, but it is not a
-               //   System.Decimal, which we require operator overloading to catch)
-               //
-               static bool IsPredefinedOperator (TypeSpec t)
-               {
-                       return (TypeManager.IsPrimitiveType (t) && t != TypeManager.bool_type) ||
-                               TypeManager.IsEnumType (t) ||
-                               t.IsPointer && t != TypeManager.void_ptr_type;
-               }
 
 #if NET_4_0
                public override SLE.Expression MakeExpression (BuilderContext ctx)
@@ -1079,78 +1193,16 @@ namespace Mono.CSharp {
                }
 #endif
 
-               protected override void CloneTo (CloneContext clonectx, Expression t)
+               public static void Reset ()
                {
-                       UnaryMutator target = (UnaryMutator) t;
-
-                       target.expr = expr.Clone (clonectx);
+                       predefined = null;
                }
 
-               Expression ResolveOperator (ResolveContext ec)
+               protected override void CloneTo (CloneContext clonectx, Expression t)
                {
-                       if (expr is RuntimeValueExpression) {
-                               operation = expr;
-                       } else {
-                               // Use itself at the top of the stack
-                               operation = new EmptyExpression (type);
-                       }
-
-                       //
-                       // The operand of the prefix/postfix increment decrement operators
-                       // should be an expression that is classified as a variable,
-                       // a property access or an indexer access
-                       //
-                       if (expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess) {
-                               expr = expr.ResolveLValue (ec, expr);
-                       } else {
-                               ec.Report.Error (1059, loc, "The operand of an increment or decrement operator must be a variable, property or indexer");
-                       }
-
-                       //
-                       // 1. Check predefined types
-                       //
-                       if (IsPredefinedOperator (type)) {
-                               // TODO: Move to IntConstant once I get rid of int32_type
-                               var one = new IntConstant (1, loc);
-
-                               // TODO: Cache this based on type when using EmptyExpression in
-                               // context cache
-                               Binary.Operator op = IsDecrement ? Binary.Operator.Subtraction : Binary.Operator.Addition;
-                               operation = new Binary (op, operation, one, loc);
-                               operation = operation.Resolve (ec);
-                               if (operation != null && operation.Type != type)
-                                       operation = Convert.ExplicitNumericConversion (operation, type);
-
-                               return this;
-                       }
-
-                       //
-                       // Step 2: Perform Operator overload resolution
-                       //
-                       var user_op = IsDecrement ? Operator.OpType.Decrement : Operator.OpType.Increment;
-                       var methods = MemberCache.GetUserOperator (type, user_op, false);
-
-                       if (methods != null) {
-                               Arguments args = new Arguments (1);
-                               args.Add (new Argument (expr));
-
-                               var res = new OverloadResolver (methods, OverloadResolver.Restrictions.BaseMembersIncluded | OverloadResolver.Restrictions.NoBaseMembers, loc);
-                               var op = res.ResolveOperator (ec, ref args);
-                               if (op == null)
-                                       return null;
-
-                               args[0].Expr = operation;
-                               operation = new UserOperatorCall (op, args, null, loc);
-                               operation = Convert.ImplicitConversionRequired (ec, operation, type, loc);
-                               return this;
-                       }
-
-                       string name = IsDecrement ?
-                               Operator.GetName (Operator.OpType.Decrement) :
-                               Operator.GetName (Operator.OpType.Increment);
+                       UnaryMutator target = (UnaryMutator) t;
 
-                       Unary.Error_OperatorCannotBeApplied (ec, loc, name, type);
-                       return null;
+                       target.expr = expr.Clone (clonectx);
                }
        }
 
@@ -4042,7 +4094,7 @@ namespace Mono.CSharp {
                        eclass = ExprClass.Variable;
                        
                        if (left.Type == TypeManager.void_ptr_type) {
-                               ec.Report.Error (242, loc, "The operation in question is undefined on void pointers");
+                               Error_VoidPointerOperation (ec);
                                return null;
                        }
                        
diff --git a/mcs/tests/test-450.cs b/mcs/tests/test-450.cs
new file mode 100644 (file)
index 0000000..3c70f16
--- /dev/null
@@ -0,0 +1,42 @@
+using System;
+
+enum E : byte
+{
+       V
+}
+
+class A
+{
+       int value;
+       
+       private A (int value)
+       {
+               this.value = value;
+       }
+       
+       public static implicit operator byte (A a)
+       {
+               return 6;
+       }
+
+       public static implicit operator A (int a)
+       {
+               return new A (a);
+       }
+       
+       public static int Main ()
+       {
+               var a = new A (0);
+               a++;
+               if (a.value != 7)
+                       return 1;
+               
+               var e = E.V;
+               e++;
+               
+               if ((int) e != 1)
+                       return 2;
+               
+               return 0;
+       }
+}
\ No newline at end of file
index 66641e221604b09986a34f4e709f4a80fea189d3..941c93fb9e145a74a3d66edee553f8c81164adeb 100644 (file)
       </method>
     </type>
   </test>
+  <test name="test-450.cs">
+    <type name="A">
+      <method name="Int32 Main()">
+        <size>53</size>
+      </method>
+      <method name="Byte op_Implicit(A)">
+        <size>2</size>
+      </method>
+      <method name="A op_Implicit(Int32)">
+        <size>7</size>
+      </method>
+      <method name="Void .ctor(Int32)">
+        <size>14</size>
+      </method>
+    </type>
+  </test>
   <test name="test-451.cs">
     <type name="Test">
       <method name="Void .ctor()">