X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fassign.cs;h=19a5e7e6dac9d5137a87f225cf383d69ef9d4768;hb=9d23552efbb02fb4d6410db9fd1ce0a9ca9d08a8;hp=85ccd7ae557fe44194b6b01c5a1aa18cb1eec764;hpb=ff228e1c801bda9666b6edab3ee962e05edcf480;p=mono.git diff --git a/mcs/mcs/assign.cs b/mcs/mcs/assign.cs index 85ccd7ae557..19a5e7e6dac 100644 --- a/mcs/mcs/assign.cs +++ b/mcs/mcs/assign.cs @@ -4,13 +4,17 @@ // Author: // Miguel de Icaza (miguel@ximian.com) // Martin Baulig (martin@ximian.com) +// Marek Safar (marek.safar@gmail.com) // -// (C) 2001, 2002, 2003 Ximian, Inc. -// (C) 2004 Novell, Inc +// Dual licensed under the terms of the MIT X11 or GNU GPL +// +// Copyright 2001, 2002, 2003 Ximian, Inc. +// Copyright 2004-2008 Novell, Inc // using System; using System.Reflection; using System.Reflection.Emit; +using System.Collections; namespace Mono.CSharp { @@ -35,7 +39,7 @@ namespace Mono.CSharp { // end of the code generated for EmitAssign // void Emit (EmitContext ec, bool leave_copy); - + // // This method does the assignment // `source' will be stored into the location specified by `this' @@ -45,17 +49,17 @@ namespace Mono.CSharp { // for expressions like a [f ()] ++, where you can't call `f ()' twice. // 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); -> [...] -> @@ -64,89 +68,89 @@ namespace Mono.CSharp { 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. */ } @@ -164,7 +168,7 @@ namespace Mono.CSharp { /// 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. + /// 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 @@ -175,18 +179,16 @@ namespace Mono.CSharp { /// 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 { + public class LocalTemporary : Expression, IMemoryLocation, IAssignMethod { LocalBuilder builder; bool is_address; - - public LocalTemporary (EmitContext ec, Type t) : this (ec, t, false) {} - - public LocalTemporary (EmitContext ec, Type t, bool is_address) + + public LocalTemporary (Type t) : this (t, false) {} + + public LocalTemporary (Type t, bool is_address) { type = t; eclass = ExprClass.Value; - loc = Location.Null; - builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (t): t); this.is_address = is_address; } @@ -203,43 +205,98 @@ namespace Mono.CSharp { ec.FreeTemporaryLocal (builder, type); builder = null; } - + + public override Expression CreateExpressionTree (EmitContext ec) + { + ArrayList args = new ArrayList (1); + args.Add (new Argument (this)); + return CreateExpressionFactoryCall ("Constant", args); + } + public override Expression DoResolve (EmitContext ec) { return this; } + public override Expression DoResolveLValue (EmitContext ec, Expression right_side) + { + return this; + } + public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; - + + if (builder == null) + throw new InternalErrorException ("Emit without Store, or after Release"); + ig.Emit (OpCodes.Ldloc, builder); // we need to copy from the pointer if (is_address) LoadFromPtr (ig, type); } + #region IAssignMethod Members + + public void Emit (EmitContext ec, bool leave_copy) + { + Emit (ec); + + if (leave_copy) + Emit (ec); + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load) + { + if (prepare_for_load) + throw new NotImplementedException (); + + source.Emit (ec); + + Store (ec); + + if (leave_copy) + Emit (ec); + } + + #endregion + + public LocalBuilder Builder { + get { return builder; } + } + // 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) { ILGenerator ig = ec.ig; + if (builder == null) + builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type); + ig.Emit (OpCodes.Stloc, builder); } public void AddressOf (EmitContext ec, AddressOp mode) { + if (builder == null) + builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type); + // 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 override void MutateHoistedGenericType (AnonymousMethodStorey storey) + { + type = storey.MutateType (type); + } + public bool PointsToAddress { get { return is_address; @@ -249,369 +306,314 @@ namespace Mono.CSharp { /// /// The Assign node takes care of assigning the value of source into - /// the expression represented by target. + /// the expression represented by target. /// public class Assign : ExpressionStatement { - protected Expression target, source, real_source; - protected LocalTemporary temp = null, real_temp = null; - protected Assign embedded = null; - protected bool is_embedded = false; - protected bool must_free_temp = false; + protected Expression target, source; - public Assign (Expression target, Expression source, Location l) + protected Assign (Expression target, Expression source, Location loc) { this.target = target; - this.source = this.real_source = source; - this.loc = l; + this.source = source; + this.loc = loc; } - - protected Assign (Assign embedded, Location l) - : this (embedded.target, embedded.source, l) - { - this.is_embedded = true; - } - - protected virtual Assign GetEmbeddedAssign (Location loc) + + public override Expression CreateExpressionTree (EmitContext ec) { - return new Assign (this, loc); + Report.Error (832, loc, "An expression tree cannot contain an assignment operator"); + return null; } public Expression Target { - get { - return target; - } - - set { - target = value; - } + get { return target; } } public Expression Source { - get { - return source; - } - - set { - source = value; - } + get { return source; } } - public static void error70 (EventInfo ei, Location l) - { - Report.Error (70, l, "The event '" + ei.Name + - "' can only appear on the left-side of a += or -= (except when" + - " used from within the type '" + ei.DeclaringType + "')"); - } - - // - // Will return either `this' or an instance of `New'. - // public override Expression DoResolve (EmitContext ec) { - // Create an embedded assignment if our source is an assignment. - if (source is Assign) - source = embedded = ((Assign) source).GetEmbeddedAssign (loc); - - real_source = source = source.Resolve (ec); + bool ok = true; + source = source.Resolve (ec); + if (source == null) { - // Ensure that we don't propagate the error as spurious "uninitialized variable" errors. - target = target.ResolveLValue (ec, EmptyExpression.Null); - return null; - } - - // - // This is used in an embedded assignment. - // As an example, consider the statement "A = X = Y = Z". - // - if (is_embedded && !(source is Constant)) { - // If this is the innermost assignment (the "Y = Z" in our example), - // create a new temporary local, otherwise inherit that variable - // from our child (the "X = (Y = Z)" inherits the local from the - // "Y = Z" assignment). - - if (embedded == null) { - if (this is CompoundAssign) - real_temp = temp = new LocalTemporary (ec, target.Type); - else - real_temp = temp = new LocalTemporary (ec, source.Type); - } else - temp = embedded.temp; - - // Set the source to the new temporary variable. - // This means that the following target.ResolveLValue () will tell - // the target to read it's source value from that variable. - source = temp; + ok = false; + source = EmptyExpression.Null; } - // If we have an embedded assignment, use the embedded assignment's temporary - // local variable as source. - if (embedded != null) - source = (embedded.temp != null) ? embedded.temp : embedded.source; + target = target.ResolveLValue (ec, source, Location); - target = target.ResolveLValue (ec, source); - - if (target == null) + if (target == null || !ok) 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; + Type source_type = source.Type; - // If we're an embedded assignment, our parent will reuse our source as its - // source, it won't read from our target. - if (is_embedded) - type = source_type; - else - type = target_type; eclass = ExprClass.Value; + type = target_type; - - if (target is EventExpr) { - EventInfo ei = ((EventExpr) target).EventInfo; - - Expression ml = MemberLookup ( - ec, ec.ContainerType, ei.Name, - MemberTypes.Event, AllBindingFlags | BindingFlags.DeclaredOnly, loc); - - if (ml == null) { - // - // If this is the case, then the Event does not belong - // to this Type and so, according to the spec - // is allowed to only appear on the left hand of - // the += and -= operators - // - // Note that target will not appear as an EventExpr - // in the case it is being referenced within the same type container; - // it will appear as a FieldExpr in that case. - // - - if (!(source is BinaryDelegate)) { - error70 (ei, loc); - return null; - } - } - } - - 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)) { - Report.Error (131, loc, - "Left hand of an assignment must be a variable, " + - "a property or an indexer"); + if (!(target is IAssignMethod)) { + Error_ValueAssignment (loc); return null; } if ((source.eclass == ExprClass.Type) && (source is TypeExpr)) { - source.Error_UnexpectedKind ("variable or value", loc); + source.Error_UnexpectedKind (ec.DeclContainer, "variable or value", loc); return null; } else if ((RootContext.Version == LanguageVersion.ISO_1) && (source is MethodGroupExpr)){ ((MethodGroupExpr) source).ReportUsageError (); return null; - } - 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 - // in the spec. - // + if (!TypeManager.IsEqual (target_type, source_type)){ + Expression resolved = ResolveConversions (ec); - if (this is CompoundAssign){ - CompoundAssign a = (CompoundAssign) this; - - Binary b = source as Binary; - if (b != null){ - // - // 1. if the source is explicitly convertible to the - // target_type - // - - source = Convert.ExplicitConversion (ec, source, target_type, loc); - if (source == null){ - Convert.Error_CannotImplicitConversion (loc, source_type, target_type); - return null; - } - - // - // 2. and the original right side is implicitly convertible to - // the type of target - // - if (Convert.ImplicitStandardConversionExists (ec, a.original_source, target_type)) - return this; - - // - // In the spec 2.4 they added: or if type of the target is int - // and the operator is a shift operator... - // - if (source_type == TypeManager.int32_type && - (b.Oper == Binary.Operator.LeftShift || b.Oper == Binary.Operator.RightShift)) - return this; - - Convert.Error_CannotImplicitConversion (loc, a.original_source.Type, target_type); - return null; - } + if (resolved != this) + return resolved; } - source = Convert.ImplicitConversionRequired (ec, source, target_type, loc); + return this; + } + + public override void MutateHoistedGenericType (AnonymousMethodStorey storey) + { + source.MutateHoistedGenericType (storey); + target.MutateHoistedGenericType (storey); + type = storey.MutateType (type); + } + + protected virtual Expression ResolveConversions (EmitContext ec) + { + source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc); if (source == null) return null; - // If we're an embedded assignment, we need to create a new temporary variable - // for the converted value. Our parent will use this new variable as its source. - // The same applies when we have an embedded assignment - in this case, we need - // to convert our embedded assignment's temporary local variable to the correct - // type and store it in a new temporary local. - if (is_embedded || embedded != null) { - type = target_type; - temp = new LocalTemporary (ec, type); - must_free_temp = true; - } - return this; } - Expression EmitEmbedded (EmitContext ec) + void Emit (EmitContext ec, bool is_statement) { - // Emit an embedded assignment. + IAssignMethod t = (IAssignMethod) target; + t.EmitAssign (ec, source, !is_statement, this is CompoundAssign); + } - if (real_temp != null) { - // If we're the innermost assignment, `real_source' is the right-hand - // expression which gets assigned to all the variables left of it. - // Emit this expression and store its result in real_temp. - real_source.Emit (ec); - real_temp.Store (ec); - } + public override void Emit (EmitContext ec) + { + Emit (ec, false); + } - if (embedded != null) - embedded.EmitEmbedded (ec); + public override void EmitStatement (EmitContext ec) + { + Emit (ec, true); + } - // This happens when we've done a type conversion, in this case source will be - // the expression which does the type conversion from real_temp. - // So emit it and store the result in temp; this is the var which will be read - // by our parent. - if (temp != real_temp) { - source.Emit (ec); - temp.Store (ec); - } + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Assign _target = (Assign) t; - Expression temp_source = (temp != null) ? temp : source; - ((IAssignMethod) target).EmitAssign (ec, temp_source, false, false); - return temp_source; + _target.target = target.Clone (clonectx); + _target.source = source.Clone (clonectx); } + } - void ReleaseEmbedded (EmitContext ec) + class SimpleAssign : Assign { + public SimpleAssign (Expression target, Expression source) + : this (target, source, target.Location) { - if (embedded != null) - embedded.ReleaseEmbedded (ec); + } - if (real_temp != null) - real_temp.Release (ec); + public SimpleAssign (Expression target, Expression source, Location loc) + : base (target, source, loc) + { + } - if (must_free_temp) - temp.Release (ec); + bool CheckEqualAssign (Expression t) + { + if (source is Assign) { + Assign a = (Assign) source; + if (t.Equals (a.Target)) + return true; + return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t); + } + return t.Equals (source); } - void Emit (EmitContext ec, bool is_statement) + public override Expression DoResolve (EmitContext ec) { - if (target is EventExpr) { - ((EventExpr) target).EmitAddOrRemove (ec, source); - return; + Expression e = base.DoResolve (ec); + if (e == null || e != this) + return e; + + if (CheckEqualAssign (target)) + Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?"); + + return this; + } + } + + // This class implements fields and events class initializers + public class FieldInitializer : Assign + { + // + // Keep resolved value because field initializers have their own rules + // + ExpressionStatement resolved; + IResolveContext rc; + + public FieldInitializer (FieldBuilder field, Expression expression, IResolveContext rc) + : base (new FieldExpr (field, expression.Location), expression, expression.Location) + { + this.rc = rc; + if (!field.IsStatic) + ((FieldExpr)target).InstanceExpression = CompilerGeneratedThis.Instance; + } + + public override Expression DoResolve (EmitContext ec) + { + // Field initializer can be resolved (fail) many times + if (source == null) + return null; + + if (resolved == null) { + // + // Field initializers are tricky for partial classes. They have to + // share same costructor (block) but they have they own resolve scope. + // + + // TODO: Use ResolveContext only + EmitContext f_ec = new EmitContext (rc, rc.DeclContainer, loc, null, TypeManager.void_type, 0, true); + f_ec.IsStatic = ec.IsStatic; + f_ec.CurrentBlock = ec.CurrentBlock; + + EmitContext.Flags flags = EmitContext.Flags.InFieldInitializer; + if (ec.IsInUnsafeScope) + flags |= EmitContext.Flags.InUnsafe; + + f_ec.Set (flags); + + resolved = base.DoResolve (f_ec) as ExpressionStatement; } - - IAssignMethod am = (IAssignMethod) target; - Expression temp_source; - if (embedded != null) { - temp_source = embedded.EmitEmbedded (ec); + return resolved; + } - if (temp != null) { - source.Emit (ec); - temp.Store (ec); - temp_source = temp; - } - } else - temp_source = source; + public override void EmitStatement (EmitContext ec) + { + if (resolved == null) + return; + + if (resolved != this) + resolved.EmitStatement (ec); + else + base.EmitStatement (ec); + } - am.EmitAssign (ec, temp_source, !is_statement, this is CompoundAssign); + public bool IsComplexInitializer { + get { return !(source is Constant); } + } + + public bool IsDefaultInitializer { + get { + Constant c = source as Constant; + if (c == null) + return false; - if (embedded != null) { - if (temp != null) - temp.Release (ec); - embedded.ReleaseEmbedded (ec); + FieldExpr fe = (FieldExpr)target; + return c.IsDefaultInitializer (fe.Type); } } - + } + + class EventAddOrRemove : ExpressionStatement { + EventExpr target; + Binary.Operator op; + Expression source; + + public EventAddOrRemove (Expression target, Binary.Operator op, Expression source, Location loc) + { + this.target = target as EventExpr; + this.op = op; + this.source = source; + this.loc = loc; + } + + public override Expression CreateExpressionTree (EmitContext ec) + { + return new SimpleAssign (target, source).CreateExpressionTree (ec); + } + + public override Expression DoResolve (EmitContext ec) + { + source = source.Resolve (ec); + if (source == null) + return null; + + source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc); + if (source == null) + return null; + + eclass = ExprClass.Value; + type = TypeManager.void_type; + return this; + } + public override void Emit (EmitContext ec) { - Emit (ec, false); + if (RootContext.EvalMode) + EmitStatement (ec); + else + throw new InternalErrorException ("don't know what to emit"); } public override void EmitStatement (EmitContext ec) { - Emit (ec, true); + target.EmitAddOrRemove (ec, op == Binary.Operator.Addition, source); } } - // - // This class is used for compound assignments. + // This class is used for compound assignments. // class CompoundAssign : Assign { Binary.Operator op; - public Expression original_source; - - public CompoundAssign (Binary.Operator op, Expression target, Expression source, Location l) - : base (target, source, l) + Expression original_source; + + public CompoundAssign (Binary.Operator op, Expression target, Expression source) + : base (target, source, target.Location) { original_source = source; this.op = op; } - protected CompoundAssign (CompoundAssign embedded, Location l) - : this (embedded.op, embedded.target, embedded.source, l) - { - this.is_embedded = true; - } + public sealed class TargetExpression : Expression { + Expression child; + public TargetExpression (Expression child) + { + this.child = child; + this.loc = child.Location; + } - protected override Assign GetEmbeddedAssign (Location loc) - { - return new CompoundAssign (this, loc); - } + public override Expression CreateExpressionTree (EmitContext ec) + { + throw new NotSupportedException ("ET"); + } - public Expression ResolveSource (EmitContext ec) - { - return original_source.Resolve (ec); + public override Expression DoResolve (EmitContext ec) + { + child = child.Resolve (ec); + if (child == null) + return null; + type = child.Type; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + } } public override Expression DoResolve (EmitContext ec) @@ -620,21 +622,70 @@ namespace Mono.CSharp { if (original_source == null) return null; - target = target.Resolve (ec); + using (ec.Set (EmitContext.Flags.InCompoundAssignment)) { + target = target.Resolve (ec); + } + if (target == null) return null; - + + if (target is MethodGroupExpr){ + Error_CannotAssign (((MethodGroupExpr)target).Name, target.ExprClassName); + return null; + } + + if (target is EventExpr) + return new EventAddOrRemove (target, op, original_source, loc).DoResolve (ec); + // // 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, loc); + source = new Binary (op, new TargetExpression (target), original_source, true); return base.DoResolve (ec); } - } -} + protected override Expression ResolveConversions (EmitContext ec) + { + Type target_type = target.Type; + // + // 1. the return type is implicitly convertible to the type of target + // + if (Convert.ImplicitConversionExists (ec, source, target_type)) { + source = Convert.ImplicitConversion (ec, source, target_type, loc); + return this; + } + // + // Otherwise, if the selected operator is a predefined operator + // + Binary b = source as Binary; + if (b != null) { + // + // 2a. the operator is a shift operator + // + // 2b. the return type is explicitly convertible to the type of x, and + // y is implicitly convertible to the type of x + // + if ((b.Oper & Binary.Operator.ShiftMask) != 0 || + Convert.ImplicitConversionExists (ec, original_source, target_type)) { + source = Convert.ExplicitConversion (ec, source, target_type, loc); + return this; + } + } + + original_source.Error_ValueCannotBeConverted (ec, loc, target_type, false); + return null; + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + CompoundAssign ctarget = (CompoundAssign) t; + ctarget.original_source = ctarget.source = source.Clone (clonectx); + ctarget.target = target.Clone (clonectx); + } + } +}