2
// assign.cs: Assignments.
5
// Miguel de Icaza (miguel@ximian.com)
6
// Martin Baulig (martin@ximian.com)
7
// Marek Safar (marek.safar@gmail.com)
9
// Dual licensed under the terms of the MIT X11 or GNU GPL
11
// Copyright 2001, 2002, 2003 Ximian, Inc.
12
// Copyright 2004-2008 Novell, Inc
13
// Copyright 2011 Xamarin Inc
18
using IKVM.Reflection.Emit;
20
using System.Reflection.Emit;
23
namespace Mono.CSharp {
26
/// This interface is implemented by expressions that can be assigned to.
29
/// This interface is implemented by Expressions whose values can not
30
/// store the result on the top of the stack.
32
/// Expressions implementing this (Properties, Indexers and Arrays) would
33
/// perform an assignment of the Expression "source" into its final
36
/// No values on the top of the stack are expected to be left by
37
/// invoking this method.
39
public interface IAssignMethod {
41
// This is an extra version of Emit. If leave_copy is `true'
42
// A copy of the expression will be left on the stack at the
43
// end of the code generated for EmitAssign
45
void Emit (EmitContext ec, bool leave_copy);
48
// This method does the assignment
49
// `source' will be stored into the location specified by `this'
50
// if `leave_copy' is true, a copy of `source' will be left on the stack
51
// if `prepare_for_load' is true, when `source' is emitted, there will
52
// be data on the stack that it can use to compuatate its value. This is
53
// for expressions like a [f ()] ++, where you can't call `f ()' twice.
55
void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound);
58
For simple assignments, this interface is very simple, EmitAssign is called with source
59
as the source expression and leave_copy and prepare_for_load false.
61
For compound assignments it gets complicated.
63
EmitAssign will be called as before, however, prepare_for_load will be
64
true. The @source expression will contain an expression
65
which calls Emit. So, the calls look like:
67
this.EmitAssign (ec, source, false, true) ->
70
this.Emit (ec, false); ->
71
end this.Emit (ec, false); ->
74
end this.EmitAssign (ec, source, false, true)
77
When prepare_for_load is true, EmitAssign emits a `token' on the stack that
78
Emit will use for its state.
80
Let's take FieldExpr as an example. assume we are emitting f ().y += 1;
82
Here is the call tree again. This time, each call is annotated with the IL
85
this.EmitAssign (ec, source, false, true)
90
this.Emit (ec, false);
92
end this.Emit (ec, false);
102
end this.EmitAssign (ec, source, false, true)
105
1) EmitAssign left a token on the stack. It was the result of f ().
106
2) This token was used by Emit
108
leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy
109
of the expression at that point in evaluation. This is used for pre/post inc/dec
110
and for a = x += y. Let's do the above example with leave_copy true in EmitAssign
112
this.EmitAssign (ec, source, true, true)
117
this.Emit (ec, false);
119
end this.Emit (ec, false);
132
end this.EmitAssign (ec, source, true, true)
134
And with it true in Emit
136
this.EmitAssign (ec, source, false, true)
141
this.Emit (ec, true);
145
end this.Emit (ec, true);
156
end this.EmitAssign (ec, source, false, true)
158
Note that these two examples are what happens for ++x and x++, respectively.
163
/// An Expression to hold a temporary value.
166
/// The LocalTemporary class is used to hold temporary values of a given
167
/// type to "simulate" the expression semantics. The local variable is
170
/// The local temporary is used to alter the normal flow of code generation
171
/// basically it creates a local variable, and its emit instruction generates
172
/// code to access this value, return its address or save its value.
174
/// If `is_address' is true, then the value that we store is the address to the
175
/// real value, and not the value itself.
177
/// This is needed for a value type, because otherwise you just end up making a
178
/// copy of the value on the stack and modifying it. You really need a pointer
179
/// to the origional value so that you can modify it in that location. This
180
/// Does not happen with a class because a class is a pointer -- so you always
181
/// get the indirection.
184
public class LocalTemporary : Expression, IMemoryLocation, IAssignMethod {
185
LocalBuilder builder;
187
public LocalTemporary (TypeSpec t)
190
eclass = ExprClass.Value;
193
public LocalTemporary (LocalBuilder b, TypeSpec t)
199
public void Release (EmitContext ec)
201
ec.FreeTemporaryLocal (builder, type);
205
public override bool ContainsEmitWithAwait ()
210
public override Expression CreateExpressionTree (ResolveContext ec)
212
Arguments args = new Arguments (1);
213
args.Add (new Argument (this));
214
return CreateExpressionFactoryCall (ec, "Constant", args);
217
protected override Expression DoResolve (ResolveContext ec)
222
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
227
public override void Emit (EmitContext ec)
230
throw new InternalErrorException ("Emit without Store, or after Release");
232
ec.Emit (OpCodes.Ldloc, builder);
235
#region IAssignMethod Members
237
public void Emit (EmitContext ec, bool leave_copy)
245
public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
248
throw new NotImplementedException ();
260
public LocalBuilder Builder {
261
get { return builder; }
264
public void Store (EmitContext ec)
267
builder = ec.GetTemporaryLocal (type);
269
ec.Emit (OpCodes.Stloc, builder);
272
public void AddressOf (EmitContext ec, AddressOp mode)
275
builder = ec.GetTemporaryLocal (type);
277
if (builder.LocalType.IsByRef) {
279
// if is_address, than this is just the address anyways,
280
// so we just return this.
282
ec.Emit (OpCodes.Ldloc, builder);
284
ec.Emit (OpCodes.Ldloca, builder);
290
/// The Assign node takes care of assigning the value of source into
291
/// the expression represented by target.
293
public abstract class Assign : ExpressionStatement {
294
protected Expression target, source;
296
protected Assign (Expression target, Expression source, Location loc)
298
this.target = target;
299
this.source = source;
303
public Expression Target {
304
get { return target; }
307
public Expression Source {
313
public override bool ContainsEmitWithAwait ()
315
return target.ContainsEmitWithAwait () || source.ContainsEmitWithAwait ();
318
public override Expression CreateExpressionTree (ResolveContext ec)
320
ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
324
protected override Expression DoResolve (ResolveContext ec)
327
source = source.Resolve (ec);
329
if (source == null) {
331
source = EmptyExpression.Null;
334
target = target.ResolveLValue (ec, source);
336
if (target == null || !ok)
339
TypeSpec target_type = target.Type;
340
TypeSpec source_type = source.Type;
342
eclass = ExprClass.Value;
345
if (!(target is IAssignMethod)) {
346
target.Error_ValueAssignment (ec, source);
350
if (target_type != source_type) {
351
Expression resolved = ResolveConversions (ec);
353
if (resolved != this)
360
#if NET_4_0 || MONODROID
361
public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
363
var tassign = target as IDynamicAssign;
365
throw new InternalErrorException (target.GetType () + " does not support dynamic assignment");
367
var target_object = tassign.MakeAssignExpression (ctx, source);
370
// Some hacking is needed as DLR does not support void type and requires
371
// always have object convertible return type to support caching and chaining
373
// We do this by introducing an explicit block which returns RHS value when
376
if (target_object.NodeType == System.Linq.Expressions.ExpressionType.Block)
377
return target_object;
379
System.Linq.Expressions.UnaryExpression source_object;
380
if (ctx.HasSet (BuilderContext.Options.CheckedScope)) {
381
source_object = System.Linq.Expressions.Expression.ConvertChecked (source.MakeExpression (ctx), target_object.Type);
383
source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type);
386
return System.Linq.Expressions.Expression.Assign (target_object, source_object);
389
protected virtual Expression ResolveConversions (ResolveContext ec)
391
source = Convert.ImplicitConversionRequired (ec, source, target.Type, source.Location);
398
void Emit (EmitContext ec, bool is_statement)
400
IAssignMethod t = (IAssignMethod) target;
401
t.EmitAssign (ec, source, !is_statement, this is CompoundAssign);
404
public override void Emit (EmitContext ec)
409
public override void EmitStatement (EmitContext ec)
414
protected override void CloneTo (CloneContext clonectx, Expression t)
416
Assign _target = (Assign) t;
418
_target.target = target.Clone (clonectx);
419
_target.source = source.Clone (clonectx);
422
public override object Accept (StructuralVisitor visitor)
424
return visitor.Visit (this);
428
public class SimpleAssign : Assign
430
public SimpleAssign (Expression target, Expression source)
431
: this (target, source, target.Location)
435
public SimpleAssign (Expression target, Expression source, Location loc)
436
: base (target, source, loc)
440
bool CheckEqualAssign (Expression t)
442
if (source is Assign) {
443
Assign a = (Assign) source;
444
if (t.Equals (a.Target))
446
return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
448
return t.Equals (source);
451
protected override Expression DoResolve (ResolveContext ec)
453
Expression e = base.DoResolve (ec);
454
if (e == null || e != this)
457
if (CheckEqualAssign (target))
458
ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
463
public override object Accept (StructuralVisitor visitor)
465
return visitor.Visit (this);
469
public class RuntimeExplicitAssign : Assign
471
public RuntimeExplicitAssign (Expression target, Expression source)
472
: base (target, source, target.Location)
476
protected override Expression ResolveConversions (ResolveContext ec)
478
source = EmptyCast.Create (source, target.Type);
484
// Compiler generated assign
486
class CompilerAssign : Assign
488
public CompilerAssign (Expression target, Expression source, Location loc)
489
: base (target, source, loc)
491
if (target.Type != null) {
493
eclass = ExprClass.Value;
497
protected override Expression DoResolve (ResolveContext ec)
499
var expr = base.DoResolve (ec);
500
var vr = target as VariableReference;
501
if (vr != null && vr.VariableInfo != null)
502
vr.VariableInfo.IsEverAssigned = false;
507
public void UpdateSource (Expression source)
509
base.source = source;
514
// Implements fields and events class initializers
516
public class FieldInitializer : Assign
519
// Field initializers are tricky for partial classes. They have to
520
// share same constructor (block) for expression trees resolve but
521
// they have they own resolve scope
523
sealed class FieldInitializerContext : ResolveContext
525
ExplicitBlock ctor_block;
527
public FieldInitializerContext (IMemberContext mc, ResolveContext constructorContext)
528
: base (mc, Options.FieldInitializerScope | Options.ConstructorScope)
530
this.ctor_block = constructorContext.CurrentBlock.Explicit;
533
public override ExplicitBlock ConstructorBlock {
541
// Keep resolved value because field initializers have their own rules
543
ExpressionStatement resolved;
546
public FieldInitializer (FieldSpec spec, Expression expression, IMemberContext mc)
547
: base (new FieldExpr (spec, expression.Location), expression, expression.Location)
551
((FieldExpr)target).InstanceExpression = new CompilerGeneratedThis (mc.CurrentType, expression.Location);
554
protected override Expression DoResolve (ResolveContext ec)
556
// Field initializer can be resolved (fail) many times
560
if (resolved == null) {
561
var ctx = new FieldInitializerContext (mc, ec);
562
resolved = base.DoResolve (ctx) as ExpressionStatement;
568
public override void EmitStatement (EmitContext ec)
570
if (resolved == null)
574
// Emit sequence symbol info even if we are in compiler generated
575
// block to allow debugging field initializers when constructor is
576
// compiler generated
578
if (ec.HasSet (BuilderContext.Options.OmitDebugInfo) && ec.HasMethodSymbolBuilder) {
579
using (ec.With (BuilderContext.Options.OmitDebugInfo, false)) {
584
if (resolved != this)
585
resolved.EmitStatement (ec);
587
base.EmitStatement (ec);
590
public bool IsDefaultInitializer {
592
Constant c = source as Constant;
596
FieldExpr fe = (FieldExpr)target;
597
return c.IsDefaultInitializer (fe.Type);
601
public override bool IsSideEffectFree {
603
return source.IsSideEffectFree;
609
// This class is used for compound assignments.
611
public class CompoundAssign : Assign
613
// This is just a hack implemented for arrays only
614
public sealed class TargetExpression : Expression
616
readonly Expression child;
618
public TargetExpression (Expression child)
621
this.loc = child.Location;
624
public override bool ContainsEmitWithAwait ()
626
return child.ContainsEmitWithAwait ();
629
public override Expression CreateExpressionTree (ResolveContext ec)
631
throw new NotSupportedException ("ET");
634
protected override Expression DoResolve (ResolveContext ec)
637
eclass = ExprClass.Value;
641
public override void Emit (EmitContext ec)
646
public override Expression EmitToField (EmitContext ec)
648
return child.EmitToField (ec);
652
// Used for underlying binary operator
653
readonly Binary.Operator op;
657
public Binary.Operator Op {
663
public CompoundAssign (Binary.Operator op, Expression target, Expression source, Location loc)
664
: base (target, source, loc)
670
public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left, Location loc)
671
: this (op, target, source, loc)
676
public Binary.Operator Operator {
682
protected override Expression DoResolve (ResolveContext ec)
684
right = right.Resolve (ec);
688
MemberAccess ma = target as MemberAccess;
689
using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) {
690
target = target.Resolve (ec);
696
if (target is MethodGroupExpr){
697
ec.Report.Error (1656, loc,
698
"Cannot assign to `{0}' because it is a `{1}'",
699
((MethodGroupExpr)target).Name, target.ExprClassName);
703
var event_expr = target as EventExpr;
704
if (event_expr != null) {
705
source = Convert.ImplicitConversionRequired (ec, right, target.Type, loc);
710
if (op == Binary.Operator.Addition)
711
rside = EmptyExpression.EventAddition;
712
else if (op == Binary.Operator.Subtraction)
713
rside = EmptyExpression.EventSubtraction;
717
target = target.ResolveLValue (ec, rside);
721
eclass = ExprClass.Value;
722
type = event_expr.Operator.ReturnType;
727
// Only now we can decouple the original source/target
728
// into a tree, to guarantee that we do not have side
732
left = new TargetExpression (target);
734
source = new Binary (op, left, right, true, loc);
736
if (target is DynamicMemberAssignable) {
737
Arguments targs = ((DynamicMemberAssignable) target).Arguments;
738
source = source.Resolve (ec);
740
Arguments args = new Arguments (targs.Count + 1);
741
args.AddRange (targs);
742
args.Add (new Argument (source));
744
var binder_flags = CSharpBinderFlags.ValueFromCompoundAssignment;
747
// Compound assignment does target conversion using additional method
748
// call, set checked context as the binary operation can overflow
750
if (ec.HasSet (ResolveContext.Options.CheckedScope))
751
binder_flags |= CSharpBinderFlags.CheckedContext;
753
if (target is DynamicMemberBinder) {
754
source = new DynamicMemberBinder (ma.Name, binder_flags, args, loc).Resolve (ec);
756
// Handles possible event addition/subtraction
757
if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) {
758
args = new Arguments (targs.Count + 1);
759
args.AddRange (targs);
760
args.Add (new Argument (right));
761
string method_prefix = op == Binary.Operator.Addition ?
762
Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix;
764
var invoke = DynamicInvocation.CreateSpecialNameInvoke (
765
new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec);
767
args = new Arguments (targs.Count);
768
args.AddRange (targs);
769
source = new DynamicEventCompoundAssign (ma.Name, args,
770
(ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec);
773
source = new DynamicIndexBinder (binder_flags, args, loc).Resolve (ec);
779
return base.DoResolve (ec);
782
protected override Expression ResolveConversions (ResolveContext ec)
785
// LAMESPEC: Under dynamic context no target conversion is happening
786
// This allows more natual dynamic behaviour but breaks compatibility
787
// with static binding
789
if (target is RuntimeValueExpression)
792
TypeSpec target_type = target.Type;
795
// 1. the return type is implicitly convertible to the type of target
797
if (Convert.ImplicitConversionExists (ec, source, target_type)) {
798
source = Convert.ImplicitConversion (ec, source, target_type, loc);
803
// Otherwise, if the selected operator is a predefined operator
805
Binary b = source as Binary;
806
if (b == null && source is ReducedExpression)
807
b = ((ReducedExpression) source).OriginalExpression as Binary;
811
// 2a. the operator is a shift operator
813
// 2b. the return type is explicitly convertible to the type of x, and
814
// y is implicitly convertible to the type of x
816
if ((b.Oper & Binary.Operator.ShiftMask) != 0 ||
817
Convert.ImplicitConversionExists (ec, right, target_type)) {
818
source = Convert.ExplicitConversion (ec, source, target_type, loc);
823
if (source.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
824
Arguments arg = new Arguments (1);
825
arg.Add (new Argument (source));
826
return new SimpleAssign (target, new DynamicConversion (target_type, CSharpBinderFlags.ConvertExplicit, arg, loc), loc).Resolve (ec);
829
right.Error_ValueCannotBeConverted (ec, target_type, false);
833
protected override void CloneTo (CloneContext clonectx, Expression t)
835
CompoundAssign ctarget = (CompoundAssign) t;
837
ctarget.right = ctarget.source = source.Clone (clonectx);
838
ctarget.target = target.Clone (clonectx);
841
public override object Accept (StructuralVisitor visitor)
843
return visitor.Visit (this);