X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fgmcs%2Fassign.cs;h=b4b75fbfdfe91dad17e8acb2be42da73fefccfb4;hb=e7e6591153da6bdfefdd7f5e0a3ab2f4b06cfeac;hp=3f1827e5cfa224567e6d603db54f77fcd5552e22;hpb=b7c17c47e6b3c02192e64175cb5ee0ce7f7dda1b;p=mono.git diff --git a/mcs/gmcs/assign.cs b/mcs/gmcs/assign.cs old mode 100755 new mode 100644 index 3f1827e5cfa..b4b75fbfdfe --- a/mcs/gmcs/assign.cs +++ b/mcs/gmcs/assign.cs @@ -3,9 +3,10 @@ // // Author: // Miguel de Icaza (miguel@ximian.com) -// Martin Baulig (martin@gnome.org) +// Martin Baulig (martin@ximian.com) // // (C) 2001, 2002, 2003 Ximian, Inc. +// (C) 2004 Novell, Inc // using System; using System.Reflection; @@ -29,19 +30,125 @@ namespace Mono.CSharp { /// public interface IAssignMethod { // - // This method will emit the code for the actual assignment + // This is an extra version of Emit. If leave_copy is `true' + // A copy of the expression will be left on the stack at the + // end of the code generated for EmitAssign // - 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 + void Emit (EmitContext ec, bool leave_copy); + // - // Example: a [ g () ] ++ + // This method does the assignment + // `source' will be stored into the location specified by `this' + // if `leave_copy' is true, a copy of `source' will be left on the stack + // if `prepare_for_load' is true, when `source' is emitted, there will + // be data on the stack that it can use to compuatate its value. This is + // for expressions like a [f ()] ++, where you can't call `f ()' twice. // - void CacheTemporaries (EmitContext ec); + void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load); + + /* + For simple assignments, this interface is very simple, EmitAssign is called with source + as the source expression and leave_copy and prepare_for_load false. + + For compound assignments it gets complicated. + + EmitAssign will be called as before, however, prepare_for_load will be + true. The @source expression will contain an expression + which calls Emit. So, the calls look like: + + this.EmitAssign (ec, source, false, true) -> + source.Emit (ec); -> + [...] -> + this.Emit (ec, false); -> + end this.Emit (ec, false); -> + end [...] + end source.Emit (ec); + end this.EmitAssign (ec, source, false, true) + + + When prepare_for_load is true, EmitAssign emits a `token' on the stack that + Emit will use for its state. + + Let's take FieldExpr as an example. assume we are emitting f ().y += 1; + + Here is the call tree again. This time, each call is annotated with the IL + it produces: + + this.EmitAssign (ec, source, false, true) + call f + dup + + Binary.Emit () + this.Emit (ec, false); + ldfld y + end this.Emit (ec, false); + + IntConstant.Emit () + ldc.i4.1 + end IntConstant.Emit + + add + end Binary.Emit () + + stfld + end this.EmitAssign (ec, source, false, true) + + Observe two things: + 1) EmitAssign left a token on the stack. It was the result of f (). + 2) This token was used by Emit + + leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy + of the expression at that point in evaluation. This is used for pre/post inc/dec + and for a = x += y. Let's do the above example with leave_copy true in EmitAssign + + this.EmitAssign (ec, source, true, true) + call f + dup + + Binary.Emit () + this.Emit (ec, false); + ldfld y + end this.Emit (ec, false); + + IntConstant.Emit () + ldc.i4.1 + end IntConstant.Emit + + add + end Binary.Emit () + + dup + stloc temp + stfld + ldloc temp + end this.EmitAssign (ec, source, true, true) + + And with it true in Emit + + this.EmitAssign (ec, source, false, true) + call f + dup + + Binary.Emit () + this.Emit (ec, true); + ldfld y + dup + stloc temp + end this.Emit (ec, true); + + IntConstant.Emit () + ldc.i4.1 + end IntConstant.Emit + + add + end Binary.Emit () + + stfld + ldloc temp + end this.EmitAssign (ec, source, false, true) + + Note that these two examples are what happens for ++x and x++, respectively. + */ } /// @@ -55,24 +162,34 @@ namespace Mono.CSharp { /// The local temporary is used to alter the normal flow of code generation /// basically it creates a local variable, and its emit instruction generates /// code to access this value, return its address or save its value. + /// + /// If `is_address' is true, then the value that we store is the address to the + /// real value, and not the value itself. + /// + /// This is needed for a value type, because otherwise you just end up making a + /// copy of the value on the stack and modifying it. You really need a pointer + /// to the origional value so that you can modify it in that location. This + /// Does not happen with a class because a class is a pointer -- so you always + /// get the indirection. + /// + /// The `is_address' stuff is really just a hack. We need to come up with a better + /// way to handle it. /// public class LocalTemporary : Expression, IMemoryLocation { LocalBuilder builder; + bool is_address; - public LocalTemporary (EmitContext ec, Type t) + public LocalTemporary (EmitContext ec, Type t) : this (ec, t, false) {} + + public LocalTemporary (EmitContext ec, Type t, bool is_address) { type = t; eclass = ExprClass.Value; loc = Location.Null; - builder = ec.GetTemporaryLocal (t); + builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (t): t); + this.is_address = is_address; } - public void Release (EmitContext ec) - { - ec.FreeTemporaryLocal (builder, type); - builder = null; - } - public LocalTemporary (LocalBuilder b, Type t) { type = t; @@ -80,6 +197,12 @@ namespace Mono.CSharp { loc = Location.Null; builder = b; } + + public void Release (EmitContext ec) + { + ec.FreeTemporaryLocal (builder, type); + builder = null; + } public override Expression DoResolve (EmitContext ec) { @@ -88,17 +211,39 @@ namespace Mono.CSharp { public override void Emit (EmitContext ec) { - ec.ig.Emit (OpCodes.Ldloc, builder); + ILGenerator ig = ec.ig; + + ig.Emit (OpCodes.Ldloc, builder); + // we need to copy from the pointer + if (is_address) + LoadFromPtr (ig, type); } + // NB: if you have `is_address' on the stack there must + // be a managed pointer. Otherwise, it is the type from + // the ctor. public void Store (EmitContext ec) { - ec.ig.Emit (OpCodes.Stloc, builder); + ILGenerator ig = ec.ig; + ig.Emit (OpCodes.Stloc, builder); } public void AddressOf (EmitContext ec, AddressOp mode) { - ec.ig.Emit (OpCodes.Ldloca, builder); + // if is_address, than this is just the address anyways, + // so we just return this. + ILGenerator ig = ec.ig; + + if (is_address) + ig.Emit (OpCodes.Ldloc, builder); + else + ig.Emit (OpCodes.Ldloca, builder); + } + + public bool PointsToAddress { + get { + return is_address; + } } } @@ -168,8 +313,11 @@ namespace Mono.CSharp { source = embedded = ((Assign) source).GetEmbeddedAssign (loc); real_source = source = source.Resolve (ec); - if (source == null) + if (source == null) { + // Ensure that we don't propagate the error as spurious "uninitialized variable" errors. + target = target.ResolveLValue (ec, EmptyExpression.Null, Location); return null; + } // // This is used in an embedded assignment. @@ -200,11 +348,15 @@ namespace Mono.CSharp { if (embedded != null) source = (embedded.temp != null) ? embedded.temp : embedded.source; - target = target.ResolveLValue (ec, source); + target = target.ResolveLValue (ec, source, Location); if (target == null) return null; + if (source.Equals (target)) { + Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?"); + } + Type target_type = target.Type; Type source_type = real_source.Type; @@ -241,15 +393,20 @@ namespace Mono.CSharp { } } } - - if (source is New && target_type.IsValueType && - (target.eclass != ExprClass.IndexerAccess) && (target.eclass != ExprClass.PropertyAccess)){ - New n = (New) source; - if (n.SetValueTypeVariable (target)) - return n; - else + FieldExpr field_exp = target as FieldExpr; + if (field_exp != null && field_exp.DeclaringType.IsValueType && !ec.IsConstructor && !ec.IsFieldInitializer) { + field_exp = field_exp.InstanceExpression as FieldExpr; + if (field_exp != null && field_exp.FieldInfo.IsInitOnly) { + if (field_exp.IsStatic) { + Report.Error (1650, loc, "Members of static readonly field '{0}' cannot be assigned to " + + "(except in a static constructor or a variable initializer)", TypeManager.GetFullNameSignature (field_exp.FieldInfo)); + } else { + Report.Error (1648, loc, "Members of readonly field '{0}' cannot be assigned to " + + "(except in a constructor or a variable initializer)", TypeManager.GetFullNameSignature (field_exp.FieldInfo)); + } return null; + } } if (!(target is IAssignMethod) && (target.eclass != ExprClass.EventAccess)) { @@ -260,17 +417,29 @@ namespace Mono.CSharp { } if ((source.eclass == ExprClass.Type) && (source is TypeExpr)) { - source.Error_UnexpectedKind ("variable or value"); + source.Error_UnexpectedKind ("variable or value", loc); return null; - } else if (!RootContext.V2 && (source is MethodGroupExpr)){ + } else if ((RootContext.Version == LanguageVersion.ISO_1) && + (source is MethodGroupExpr)){ ((MethodGroupExpr) source).ReportUsageError (); return null; } - if (target_type == source_type) + if (target_type == source_type){ + if (source is New && target_type.IsValueType && + (target.eclass != ExprClass.IndexerAccess) && (target.eclass != ExprClass.PropertyAccess)){ + New n = (New) source; + + if (n.SetValueTypeVariable (target)) + return n; + else + return null; + } + return this; - + } + // // If this assignemnt/operator was part of a compound binary // operator, then we allow an explicit conversion, as detailed @@ -297,7 +466,7 @@ namespace Mono.CSharp { // 2. and the original right side is implicitly convertible to // the type of target // - if (Convert.ImplicitStandardConversionExists (a.original_source, target_type)) + if (Convert.ImplicitStandardConversionExists (ec, a.original_source, target_type)) return this; // @@ -356,7 +525,7 @@ namespace Mono.CSharp { } Expression temp_source = (temp != null) ? temp : source; - ((IAssignMethod) target).EmitAssign (ec, temp_source); + ((IAssignMethod) target).EmitAssign (ec, temp_source, false, false); return temp_source; } @@ -378,20 +547,8 @@ namespace Mono.CSharp { ((EventExpr) target).EmitAddOrRemove (ec, source); return; } - - bool use_temporaries = false; - // - // FIXME! We need a way to "probe" if the process can - // just use `dup' to propagate the result - // IAssignMethod am = (IAssignMethod) target; - - if (this is CompoundAssign) - am.CacheTemporaries (ec); - - if (!is_statement) - use_temporaries = true; Expression temp_source; if (embedded != null) { @@ -404,27 +561,8 @@ namespace Mono.CSharp { } } else temp_source = source; - - if (use_temporaries){ - // - // Doing this for every path is too expensive - // I wonder if we can work around this and have a less - // expensive path - // - LocalTemporary tempo; - - tempo = new LocalTemporary (ec, source.Type); - - temp_source.Emit (ec); - tempo.Store (ec); - am.EmitAssign (ec, tempo); - if (!is_statement) - tempo.Emit (ec); - - tempo.Release (ec); - } else { - am.EmitAssign (ec, temp_source); - } + + am.EmitAssign (ec, temp_source, !is_statement, this is CompoundAssign); if (embedded != null) { if (temp != null)