X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fassign.cs;h=19a5e7e6dac9d5137a87f225cf383d69ef9d4768;hb=f8be5461467b13a0423adc77b4f0ad897537d532;hp=37db641c1f98a9f41a72a55db9bd862648f65d04;hpb=ecd61be7b3ef7aa871d3bd7be9669d69dd6812b6;p=mono.git diff --git a/mcs/mcs/assign.cs b/mcs/mcs/assign.cs index 37db641c1f9..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 { @@ -175,7 +179,7 @@ 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; @@ -202,11 +206,23 @@ namespace Mono.CSharp { 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; @@ -220,6 +236,35 @@ namespace Mono.CSharp { 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. @@ -247,6 +292,11 @@ namespace Mono.CSharp { ig.Emit (OpCodes.Ldloca, builder); } + public override void MutateHoistedGenericType (AnonymousMethodStorey storey) + { + type = storey.MutateType (type); + } + public bool PointsToAddress { get { return is_address; @@ -259,155 +309,51 @@ namespace Mono.CSharp { /// 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; - - public Assign (Expression target, Expression source) - : this (target, source, target.Location) - { - } + 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 `" + TypeManager.CSharpSignature (ei) + - "' can only appear on the left hand side of += 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, Location); - 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 (target.Type); - else - real_temp = temp = new LocalTemporary (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); - if (target == null) + if (target == null || !ok) return null; - bool same_assignment = (embedded != null) ? embedded.Target.Equals(target) : source.Equals (target); - if (same_assignment) { - 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.ContainerType, 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; - } - } - } - - if (!(target is IAssignMethod) && (target.eclass != ExprClass.EventAccess)) { + if (!(target is IAssignMethod)) { Error_ValueAssignment (loc); return null; } @@ -419,192 +365,153 @@ namespace Mono.CSharp { (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 (!TypeManager.IsEqual (target_type, source_type)){ + Expression resolved = ResolveConversions (ec); - if (n.SetValueTypeVariable (target)) - return n; - else - return null; - } - - return this; + if (resolved != this) + return resolved; } - // - // If this assignment/operator was part of a compound binary - // operator, then we allow an explicit conversion, as detailed - // in the spec. - // - - 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){ - a.original_source.Error_ValueCannotBeConverted (ec, loc, target_type, true); - return null; - } - - // - // 2. and the original right side is implicitly convertible to - // the type of target - // - if (Convert.ImplicitConversionExists (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; - - a.original_source.Error_ValueCannotBeConverted (ec, loc, target_type, false); - return null; - } - } + return this; + } - if (source.eclass == ExprClass.MethodGroup && !TypeManager.IsDelegateType (target_type)) { - Report.Error (428, source.Location, "Cannot convert method group `{0}' to non-delegate type `{1}'. Did you intend to invoke the method?", - ((MethodGroupExpr)source).Name, target.GetSignatureForError ()); - return null; - } + public override void MutateHoistedGenericType (AnonymousMethodStorey storey) + { + source.MutateHoistedGenericType (storey); + target.MutateHoistedGenericType (storey); + type = storey.MutateType (type); + } - source = Convert.ImplicitConversionRequired (ec, source, target_type, loc); + 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 (type); - must_free_temp = true; - } - return this; } - Expression EmitEmbedded (EmitContext ec) + void Emit (EmitContext ec, bool is_statement) { - // Emit an embedded assignment. - - 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); - } - - if (embedded != null) - embedded.EmitEmbedded (ec); - - // 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); - } - - Expression temp_source = (temp != null) ? temp : source; - ((IAssignMethod) target).EmitAssign (ec, temp_source, false, false); - return temp_source; + IAssignMethod t = (IAssignMethod) target; + t.EmitAssign (ec, source, !is_statement, this is CompoundAssign); } - void ReleaseEmbedded (EmitContext ec) + public override void Emit (EmitContext ec) { - if (embedded != null) - embedded.ReleaseEmbedded (ec); - - if (real_temp != null) - real_temp.Release (ec); - - if (must_free_temp) - temp.Release (ec); + Emit (ec, false); } - void Emit (EmitContext ec, bool is_statement) + public override void EmitStatement (EmitContext ec) { - if (target is EventExpr) { - ((EventExpr) target).EmitAddOrRemove (ec, source); - return; - } - - IAssignMethod am = (IAssignMethod) target; + Emit (ec, true); + } - Expression temp_source; - if (embedded != null) { - temp_source = embedded.EmitEmbedded (ec); + protected override void CloneTo (CloneContext clonectx, Expression t) + { + Assign _target = (Assign) t; - if (temp != null) { - source.Emit (ec); - temp.Store (ec); - temp_source = temp; - } - } else - temp_source = source; + _target.target = target.Clone (clonectx); + _target.source = source.Clone (clonectx); + } + } - am.EmitAssign (ec, temp_source, !is_statement, this is CompoundAssign); + class SimpleAssign : Assign { + public SimpleAssign (Expression target, Expression source) + : this (target, source, target.Location) + { + } - if (embedded != null) { - if (temp != null) - temp.Release (ec); - embedded.ReleaseEmbedded (ec); - } + public SimpleAssign (Expression target, Expression source, Location loc) + : base (target, source, loc) + { } - public override void Emit (EmitContext ec) + bool CheckEqualAssign (Expression t) { - Emit (ec, false); + 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); } - public override void EmitStatement (EmitContext ec) + public override Expression DoResolve (EmitContext ec) { - Emit (ec, true); + 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 { - public readonly DeclSpace TypeContainer; + // + // Keep resolved value because field initializers have their own rules + // + ExpressionStatement resolved; + IResolveContext rc; - public FieldInitializer (FieldBuilder field, Expression expression, DeclSpace container) - : base (new FieldExpr (field, expression.Location, true), expression) + public FieldInitializer (FieldBuilder field, Expression expression, IResolveContext rc) + : base (new FieldExpr (field, expression.Location), expression, expression.Location) { - this.TypeContainer = container; + this.rc = rc; if (!field.IsStatic) ((FieldExpr)target).InstanceExpression = CompilerGeneratedThis.Instance; } - public bool IsComplexInitializer { - get { - if (embedded != null) - return true; + 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; - return !(source is Constant); + f_ec.Set (flags); + + resolved = base.DoResolve (f_ec) as ExpressionStatement; } + + return resolved; + } + + public override void EmitStatement (EmitContext ec) + { + if (resolved == null) + return; + + if (resolved != this) + resolved.EmitStatement (ec); + else + base.EmitStatement (ec); + } + + public bool IsComplexInitializer { + get { return !(source is Constant); } } public bool IsDefaultInitializer { @@ -619,13 +526,59 @@ namespace Mono.CSharp { } } + 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) + { + if (RootContext.EvalMode) + EmitStatement (ec); + else + throw new InternalErrorException ("don't know what to emit"); + } + + public override void EmitStatement (EmitContext ec) + { + target.EmitAddOrRemove (ec, op == Binary.Operator.Addition, source); + } + } // // This class is used for compound assignments. // class CompoundAssign : Assign { Binary.Operator op; - public Expression original_source; + Expression original_source; public CompoundAssign (Binary.Operator op, Expression target, Expression source) : base (target, source, target.Location) @@ -634,15 +587,33 @@ namespace Mono.CSharp { this.op = op; } - protected CompoundAssign (CompoundAssign embedded, Location l) - : this (embedded.op, embedded.target, embedded.source) - { - 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 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) @@ -651,7 +622,10 @@ 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; @@ -659,13 +633,59 @@ namespace Mono.CSharp { 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); + 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); + } } }