2002-06-08 Martin Baulig <martin@gnome.org>
[mono.git] / mcs / mcs / assign.cs
index af30e1dc81b5c0e94bb1e645c34b8286dac6a01e..aff163825fa451c041b44bbea70b0f6c12433698 100755 (executable)
@@ -4,7 +4,7 @@
 // Author:
 //   Miguel de Icaza (miguel@ximian.com)
 //
-// (C) 2001 Ximian, Inc.
+// (C) 2001, 2002 Ximian, Inc.
 //
 using System;
 using System.Reflection;
@@ -27,7 +27,20 @@ namespace Mono.CSharp {
        ///   invoking this method.
        /// </remarks>
        public interface IAssignMethod {
+               //
+               // This method will emit the code for the actual assignment
+               //
                void EmitAssign (EmitContext ec, Expression source);
+
+               //
+               // This method is invoked before any code generation takes
+               // place, and it is a mechanism to inform that the expression
+               // will be invoked more than once, and that the method should
+               // use temporary values to avoid having side effects
+               //
+               // Example: a [ g () ] ++
+               //
+               void CacheTemporaries (EmitContext ec);
        }
 
        /// <summary>
@@ -91,7 +104,7 @@ namespace Mono.CSharp {
        ///   the expression represented by target. 
        /// </summary>
        public class Assign : ExpressionStatement {
-               Expression target, source;
+               protected Expression target, source;
                public Location l;
 
                public Assign (Expression target, Expression source, Location l)
@@ -147,7 +160,7 @@ namespace Mono.CSharp {
 
                        type = target_type;
                        eclass = ExprClass.Value;
-                       
+
                        //
                        // If we are doing a property assignment, then
                        // set the `value' field on the property, and Resolve
@@ -156,6 +169,12 @@ namespace Mono.CSharp {
                        if (target is PropertyExpr){
                                PropertyExpr property_assign = (PropertyExpr) target;
 
+                               if (source_type != target_type){
+                                       source = ConvertImplicitRequired (ec, source, target_type, l);
+                                       if (source == null)
+                                               return null;
+                               }
+
                                //
                                // FIXME: Maybe handle this in the LValueResolve
                                //
@@ -230,10 +249,6 @@ namespace Mono.CSharp {
                        if (this is CompoundAssign){
                                CompoundAssign a = (CompoundAssign) this;
                                
-                               a.original_source = a.original_source.Resolve (ec);
-                               if (a.original_source == null)
-                                       return null;
-                               
                                Binary b = source as Binary;
                                if (b != null && b.IsBuiltinOperator){
                                        //
@@ -251,12 +266,6 @@ namespace Mono.CSharp {
                                        // 2. and the original right side is implicitly convertible to
                                        // the type of target_type.
                                        //
-                                       a.original_source = a.original_source.Resolve (ec);
-                                       if (a.original_source == null){
-                                               Error_CannotConvertImplicit (l, source_type, target_type);
-                                               return null;
-                                       }
-                                       
                                        if (StandardConversionExists (a.original_source, target_type))
                                                return this;
 
@@ -284,6 +293,10 @@ namespace Mono.CSharp {
                        // just use `dup' to propagate the result
                        // 
                        IAssignMethod am = (IAssignMethod) target;
+
+                       if (this is CompoundAssign){
+                               am.CacheTemporaries (ec);
+                       }
                        
                        if (is_statement)
                                am.EmitAssign (ec, source);
@@ -313,22 +326,42 @@ namespace Mono.CSharp {
 
        
        //
-       // This is just a class used to flag that the assignment was part of a
-       // compound assignment, hence allowing a set of extra rules to be used.
+       // This class is used for compound assignments.  
        //
        class CompoundAssign : Assign {
+               Binary.Operator op;
                public Expression original_source;
                
-               public CompoundAssign (Expression target, Expression source, Expression osource, Location l)
+               public CompoundAssign (Binary.Operator op, Expression target, Expression source, Location l)
                        : base (target, source, l)
                {
-                       original_source = osource;
+                       original_source = source;
+                       this.op = op;
                }
 
                public Expression ResolveSource (EmitContext ec)
                {
                        return original_source.Resolve (ec);
                }
+
+               public override Expression DoResolve (EmitContext ec)
+               {
+                       target = target.ResolveLValue (ec, source);
+                       if (target == null)
+                               return null;
+
+                       original_source = original_source.Resolve (ec);
+                       if (original_source == null)
+                               return null;
+
+                       //
+                       // Only now we can decouple the original source/target
+                       // into a tree, to guarantee that we do not have side
+                       // effects.
+                       //
+                       source = new Binary (op, target, original_source, l);
+                       return base.DoResolve (ec);
+               }
        }
 }