[Mono-list] Thinko fixed.

Miguel de Icaza miguel@ximian.com
Sun, 14 Oct 2001 23:57:54 -0400


Hey guys,

   There is a special situation that my previous property patch did
not take care of:  Properties might appear in:

    variable1 = PropertyName = variable2 = VALUE;

   Which requires a temporary value to be provided so the value can be
bridged between variable2 and variable1.  This patch introduces the
feature along with a test.

Index: mcs/ChangeLog
===================================================================
RCS file: /cvs/public/mcs/mcs/ChangeLog,v
retrieving revision 1.133
diff -u -r1.133 ChangeLog
--- mcs/ChangeLog	2001/10/14 22:30:14	1.133
+++ mcs/ChangeLog	2001/10/15 00:00:57
@@ -1,5 +1,8 @@
 2001-10-14  Miguel de Icaza  <miguel@ximian.com>
 
+	* assign.cs: Handle PropertyAccess back here, so that we can
+	provide the proper semantic access to properties.
+
 	* expression.cs (Expression::ConvertReferenceExplicit): Implement
 	a few more explicit conversions. 
 
Index: mcs/assign.cs
===================================================================
RCS file: /cvs/public/mcs/mcs/assign.cs,v
retrieving revision 1.17
diff -u -r1.17 assign.cs
--- mcs/assign.cs	2001/10/14 22:30:14	1.17
+++ mcs/assign.cs	2001/10/15 00:00:57
@@ -50,6 +50,12 @@
 			if (target == null || source == null)
 				return null;
 
+			Type target_type = target.Type;
+			Type source_type = source.Type;
+
+			type = target_type;
+			eclass = ExprClass.Value;
+			
 			//
 			// If we are doing a property assignment, then
 			// set the `value' field on the property, and Resolve
@@ -58,23 +64,20 @@
 			if (target is PropertyExpr){
 				PropertyExpr property_assign = (PropertyExpr) target;
 				
-				property_assign.Value = source;
+				if (!property_assign.VerifyAssignable ())
+					return null;
 				
-				return property_assign.Resolve (ec);
+				return this;
 			}
-
-			if (source is New && target.Type.IsSubclassOf (TypeManager.value_type)){
+			
+			if (source is New && target_type.IsSubclassOf (TypeManager.value_type)){
 				New n = (New) source;
 
 				n.ValueTypeVariable = target;
 
-				return source;
+				return n;
 			}
-
-			Type target_type = target.Type;
 			
-			Type source_type = source.Type;
-
 			if (target_type != source_type){
 				source = ConvertImplicitRequired (ec, source, target_type, l);
 				if (source == null)
@@ -85,16 +88,16 @@
 				Report.Error (131, l, "Left hand of an assignment must be a variable, a property or an indexer");
 				return null;
 			}
-			type = target_type;
-			eclass = ExprClass.Value;
+
 			return this;
 		}
 
 		void Emit (EmitContext ec, bool is_statement)
 		{
 			ILGenerator ig = ec.ig;
+			ExprClass eclass = target.ExprClass;
 			
-			if (target.ExprClass == ExprClass.Variable){
+			if (eclass == ExprClass.Variable){
 
 				//
 				// If it is an instance field, load the this pointer
@@ -112,11 +115,26 @@
 					ig.Emit (OpCodes.Dup);
 
 				((LValue) target).Store (ec);
-			} else if (target.ExprClass == ExprClass.PropertyAccess){
-				// FIXME
-				throw new Exception ("Can not assign to properties yet");
-			} else if (target.ExprClass == ExprClass.IndexerAccess){
-				// FIXME
+			} else if (eclass == ExprClass.PropertyAccess){
+				PropertyExpr pe = (PropertyExpr) target;
+
+				if (is_statement){
+					pe.Value = source;
+					pe.Emit (ec);
+				} else {
+					LocalTemporary tempo;
+					
+					tempo = new LocalTemporary (ec, source.Type);
+
+					pe.Value = tempo;
+					source.Emit (ec);
+					tempo.Store (ec);
+					target.Emit (ec);
+
+					tempo.Emit (ec);
+				}
+			} else if (eclass == ExprClass.IndexerAccess){
+				
 				throw new Exception ("Can not assign to indexers yet");
 			}
 		}
Index: mcs/class.cs
===================================================================
RCS file: /cvs/public/mcs/mcs/class.cs,v
retrieving revision 1.75
diff -u -r1.75 class.cs
--- mcs/class.cs	2001/10/14 22:30:14	1.75
+++ mcs/class.cs	2001/10/15 00:01:03
@@ -697,7 +697,7 @@
 			ArrayList bases = Bases;
 			int count;
 			int start, j, i;
-			
+
 			error = false;
 
 			if (is_class)
Index: mcs/expression.cs
===================================================================
RCS file: /cvs/public/mcs/mcs/expression.cs,v
retrieving revision 1.83
diff -u -r1.83 expression.cs
--- mcs/expression.cs	2001/10/14 22:30:14	1.83
+++ mcs/expression.cs	2001/10/15 00:01:16
@@ -3168,6 +3168,42 @@
 		// </summary>
 		void AddressOf (EmitContext ec);
 	}
+
+	public class LocalTemporary : Expression, LValue, MemoryLocation {
+		LocalBuilder builder;
+		
+		public LocalTemporary (EmitContext ec, Type t)
+		{
+			type = t;
+			eclass = ExprClass.Value;
+			builder = ec.GetTemporaryStorage (t);
+		}
+
+		public override Expression DoResolve (EmitContext ec)
+		{
+			return this;
+		}
+
+		public Expression LValueResolve (EmitContext ec)
+		{
+			return this;
+		}
+		
+		public override void Emit (EmitContext ec)
+		{
+			ec.ig.Emit (OpCodes.Ldloc, builder); 
+		}
+
+		public void Store (EmitContext ec)
+		{
+			ec.ig.Emit (OpCodes.Stloc, builder);
+		}
+
+		public void AddressOf (EmitContext ec)
+		{
+			ec.ig.Emit (OpCodes.Ldloca, builder);
+		}
+	}
 	
 	public class LocalVariableReference : Expression, LValue, MemoryLocation {
 		public readonly string Name;
@@ -4695,24 +4731,31 @@
 				return instance_expr;
 			}
 		}
+
+		public bool VerifyAssignable ()
+		{
+			if (!PropertyInfo.CanWrite){
+				Report.Error (200, loc, 
+					      "The property `" + PropertyInfo.Name +
+					      "' can not be assigned to, as it has not set accessor");
+				return false;
+			}
+
+			return true;
+		}
 		
 		override public Expression DoResolve (EmitContext ec)
 		{
-			if (value == null){
-				if (!PropertyInfo.CanRead){
-					Report.Error (154, loc, 
-						      "The property `" + PropertyInfo.Name +
-						      "' can not be used in " +
-						      "this context because it lacks a get accessor");
-					return null;
-				}
-			} else {
-				if (!PropertyInfo.CanWrite){
-					Report.Error (200, loc, 
-						      "The property `" + PropertyInfo.Name +
-						      "' can not be assigned to, as it has not set accessor");
-					return null;
-				}
+			//
+			// Not really sure who should call perform the test below
+			// given that `assignable' has special code for this.
+			//
+			if (!PropertyInfo.CanRead){
+				Report.Error (154, loc, 
+					      "The property `" + PropertyInfo.Name +
+					      "' can not be used in " +
+					      "this context because it lacks a get accessor");
+				return null;
 			}
 			
 			return this;
Index: tests/makefile
===================================================================
RCS file: /cvs/public/mcs/tests/makefile,v
retrieving revision 1.20
diff -u -r1.20 makefile
--- tests/makefile	2001/10/14 22:30:14	1.20
+++ tests/makefile	2001/10/15 00:01:19
@@ -4,7 +4,7 @@
 TEST_SOURCES = \
 	test-1 test-2 test-3 test-4 test-6 test-7 test-8 test-9 test-10 \
 	test-11 test-12 test-13 test-16 test-17 test-18 test-20 test-21 \
-	test-23
+	test-23 test-24
 
 TEST_NOPASS = \
 	test-5
Index: tests/test-23.cs
===================================================================
RCS file: /cvs/public/mcs/tests/test-23.cs,v
retrieving revision 1.1
diff -u -r1.1 test-23.cs
--- tests/test-23.cs	2001/10/14 22:30:14	1.1
+++ tests/test-23.cs	2001/10/15 00:01:19
@@ -51,6 +51,7 @@
 		z.XVal = 23;
 		if (z.XVal != 23)
 			return 7;
+
 		return 0;
 	}
 }