2006-04-27 Jonathan Chambers <jonathan.chambers@ansys.com>
[mcs.git] / gmcs / assign.cs
blobdabe1c75d39e01ae4589ed17ebf5baefcec49d9c
1 //
2 // assign.cs: Assignments.
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 //
8 // (C) 2001, 2002, 2003 Ximian, Inc.
9 // (C) 2004 Novell, Inc
11 using System;
12 using System.Reflection;
13 using System.Reflection.Emit;
15 namespace Mono.CSharp {
17 /// <summary>
18 /// This interface is implemented by expressions that can be assigned to.
19 /// </summary>
20 /// <remarks>
21 /// This interface is implemented by Expressions whose values can not
22 /// store the result on the top of the stack.
23 ///
24 /// Expressions implementing this (Properties, Indexers and Arrays) would
25 /// perform an assignment of the Expression "source" into its final
26 /// location.
27 ///
28 /// No values on the top of the stack are expected to be left by
29 /// invoking this method.
30 /// </remarks>
31 public interface IAssignMethod {
33 // This is an extra version of Emit. If leave_copy is `true'
34 // A copy of the expression will be left on the stack at the
35 // end of the code generated for EmitAssign
37 void Emit (EmitContext ec, bool leave_copy);
40 // This method does the assignment
41 // `source' will be stored into the location specified by `this'
42 // if `leave_copy' is true, a copy of `source' will be left on the stack
43 // if `prepare_for_load' is true, when `source' is emitted, there will
44 // be data on the stack that it can use to compuatate its value. This is
45 // for expressions like a [f ()] ++, where you can't call `f ()' twice.
47 void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load);
50 For simple assignments, this interface is very simple, EmitAssign is called with source
51 as the source expression and leave_copy and prepare_for_load false.
53 For compound assignments it gets complicated.
55 EmitAssign will be called as before, however, prepare_for_load will be
56 true. The @source expression will contain an expression
57 which calls Emit. So, the calls look like:
59 this.EmitAssign (ec, source, false, true) ->
60 source.Emit (ec); ->
61 [...] ->
62 this.Emit (ec, false); ->
63 end this.Emit (ec, false); ->
64 end [...]
65 end source.Emit (ec);
66 end this.EmitAssign (ec, source, false, true)
69 When prepare_for_load is true, EmitAssign emits a `token' on the stack that
70 Emit will use for its state.
72 Let's take FieldExpr as an example. assume we are emitting f ().y += 1;
74 Here is the call tree again. This time, each call is annotated with the IL
75 it produces:
77 this.EmitAssign (ec, source, false, true)
78 call f
79 dup
81 Binary.Emit ()
82 this.Emit (ec, false);
83 ldfld y
84 end this.Emit (ec, false);
86 IntConstant.Emit ()
87 ldc.i4.1
88 end IntConstant.Emit
90 add
91 end Binary.Emit ()
93 stfld
94 end this.EmitAssign (ec, source, false, true)
96 Observe two things:
97 1) EmitAssign left a token on the stack. It was the result of f ().
98 2) This token was used by Emit
100 leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy
101 of the expression at that point in evaluation. This is used for pre/post inc/dec
102 and for a = x += y. Let's do the above example with leave_copy true in EmitAssign
104 this.EmitAssign (ec, source, true, true)
105 call f
108 Binary.Emit ()
109 this.Emit (ec, false);
110 ldfld y
111 end this.Emit (ec, false);
113 IntConstant.Emit ()
114 ldc.i4.1
115 end IntConstant.Emit
118 end Binary.Emit ()
121 stloc temp
122 stfld
123 ldloc temp
124 end this.EmitAssign (ec, source, true, true)
126 And with it true in Emit
128 this.EmitAssign (ec, source, false, true)
129 call f
132 Binary.Emit ()
133 this.Emit (ec, true);
134 ldfld y
136 stloc temp
137 end this.Emit (ec, true);
139 IntConstant.Emit ()
140 ldc.i4.1
141 end IntConstant.Emit
144 end Binary.Emit ()
146 stfld
147 ldloc temp
148 end this.EmitAssign (ec, source, false, true)
150 Note that these two examples are what happens for ++x and x++, respectively.
154 /// <summary>
155 /// An Expression to hold a temporary value.
156 /// </summary>
157 /// <remarks>
158 /// The LocalTemporary class is used to hold temporary values of a given
159 /// type to "simulate" the expression semantics on property and indexer
160 /// access whose return values are void.
162 /// The local temporary is used to alter the normal flow of code generation
163 /// basically it creates a local variable, and its emit instruction generates
164 /// code to access this value, return its address or save its value.
166 /// If `is_address' is true, then the value that we store is the address to the
167 /// real value, and not the value itself.
169 /// This is needed for a value type, because otherwise you just end up making a
170 /// copy of the value on the stack and modifying it. You really need a pointer
171 /// to the origional value so that you can modify it in that location. This
172 /// Does not happen with a class because a class is a pointer -- so you always
173 /// get the indirection.
175 /// The `is_address' stuff is really just a hack. We need to come up with a better
176 /// way to handle it.
177 /// </remarks>
178 public class LocalTemporary : Expression, IMemoryLocation {
179 LocalBuilder builder;
180 bool is_address;
182 public LocalTemporary (Type t) : this (t, false) {}
184 public LocalTemporary (Type t, bool is_address)
186 type = t;
187 eclass = ExprClass.Value;
188 this.is_address = is_address;
191 public LocalTemporary (LocalBuilder b, Type t)
193 type = t;
194 eclass = ExprClass.Value;
195 loc = Location.Null;
196 builder = b;
199 public void Release (EmitContext ec)
201 ec.FreeTemporaryLocal (builder, type);
202 builder = null;
205 public override Expression DoResolve (EmitContext ec)
207 return this;
210 public override void Emit (EmitContext ec)
212 ILGenerator ig = ec.ig;
214 ig.Emit (OpCodes.Ldloc, builder);
215 // we need to copy from the pointer
216 if (is_address)
217 LoadFromPtr (ig, type);
220 // NB: if you have `is_address' on the stack there must
221 // be a managed pointer. Otherwise, it is the type from
222 // the ctor.
223 public void Store (EmitContext ec)
225 ILGenerator ig = ec.ig;
226 if (builder == null)
227 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type);
229 ig.Emit (OpCodes.Stloc, builder);
232 public void AddressOf (EmitContext ec, AddressOp mode)
234 if (builder == null)
235 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type);
237 // if is_address, than this is just the address anyways,
238 // so we just return this.
239 ILGenerator ig = ec.ig;
241 if (is_address)
242 ig.Emit (OpCodes.Ldloc, builder);
243 else
244 ig.Emit (OpCodes.Ldloca, builder);
247 public bool PointsToAddress {
248 get {
249 return is_address;
254 /// <summary>
255 /// The Assign node takes care of assigning the value of source into
256 /// the expression represented by target.
257 /// </summary>
258 public class Assign : ExpressionStatement {
259 protected Expression target, source, real_source;
260 protected LocalTemporary temp = null, real_temp = null;
261 protected Assign embedded = null;
262 protected bool is_embedded = false;
263 protected bool must_free_temp = false;
265 public Assign (Expression target, Expression source)
266 : this (target, source, target.Location)
270 public Assign (Expression target, Expression source, Location l)
272 this.target = target;
273 this.source = this.real_source = source;
274 this.loc = l;
277 protected Assign (Assign embedded, Location l)
278 : this (embedded.target, embedded.source, l)
280 this.is_embedded = true;
283 protected virtual Assign GetEmbeddedAssign (Location loc)
285 return new Assign (this, loc);
288 public Expression Target {
289 get {
290 return target;
293 set {
294 target = value;
298 public Expression Source {
299 get {
300 return source;
303 set {
304 source = value;
308 public static void error70 (EventInfo ei, Location l)
310 Report.Error (70, l, "The event `" + TypeManager.CSharpSignature (ei) +
311 "' can only appear on the left hand side of += or -= (except when" +
312 " used from within the type `" + ei.DeclaringType + "')");
316 // Will return either `this' or an instance of `New'.
318 public override Expression DoResolve (EmitContext ec)
320 // Create an embedded assignment if our source is an assignment.
321 if (source is Assign)
322 source = embedded = ((Assign) source).GetEmbeddedAssign (loc);
324 real_source = source = source.Resolve (ec);
325 if (source == null) {
326 // Ensure that we don't propagate the error as spurious "uninitialized variable" errors.
327 target = target.ResolveLValue (ec, EmptyExpression.Null, Location);
328 return null;
332 // This is used in an embedded assignment.
333 // As an example, consider the statement "A = X = Y = Z".
335 if (is_embedded && !(source is Constant)) {
336 // If this is the innermost assignment (the "Y = Z" in our example),
337 // create a new temporary local, otherwise inherit that variable
338 // from our child (the "X = (Y = Z)" inherits the local from the
339 // "Y = Z" assignment).
341 if (embedded == null) {
342 if (this is CompoundAssign)
343 real_temp = temp = new LocalTemporary (target.Type);
344 else
345 real_temp = temp = new LocalTemporary (source.Type);
346 } else
347 temp = embedded.temp;
349 // Set the source to the new temporary variable.
350 // This means that the following target.ResolveLValue () will tell
351 // the target to read it's source value from that variable.
352 source = temp;
355 // If we have an embedded assignment, use the embedded assignment's temporary
356 // local variable as source.
357 if (embedded != null)
358 source = (embedded.temp != null) ? embedded.temp : embedded.source;
360 target = target.ResolveLValue (ec, source, Location);
362 if (target == null)
363 return null;
365 bool same_assignment = (embedded != null) ? embedded.Target.Equals(target) : source.Equals (target);
366 if (same_assignment) {
367 Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
370 Type target_type = target.Type;
371 Type source_type = real_source.Type;
373 // If we're an embedded assignment, our parent will reuse our source as its
374 // source, it won't read from our target.
375 if (is_embedded)
376 type = source_type;
377 else
378 type = target_type;
379 eclass = ExprClass.Value;
381 if (target is EventExpr) {
382 EventInfo ei = ((EventExpr) target).EventInfo;
384 Expression ml = MemberLookup (
385 ec.ContainerType, ec.ContainerType, ei.Name,
386 MemberTypes.Event, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
388 if (ml == null) {
390 // If this is the case, then the Event does not belong
391 // to this Type and so, according to the spec
392 // is allowed to only appear on the left hand of
393 // the += and -= operators
395 // Note that target will not appear as an EventExpr
396 // in the case it is being referenced within the same type container;
397 // it will appear as a FieldExpr in that case.
400 if (!(source is BinaryDelegate)) {
401 error70 (ei, loc);
402 return null;
407 if (!(target is IAssignMethod) && (target.eclass != ExprClass.EventAccess)) {
408 Report.Error (131, loc,
409 "Left hand of an assignment must be a variable, " +
410 "a property or an indexer");
411 return null;
414 if ((source.eclass == ExprClass.Type) && (source is TypeExpr)) {
415 source.Error_UnexpectedKind (ec.DeclContainer, "variable or value", loc);
416 return null;
417 } else if ((RootContext.Version == LanguageVersion.ISO_1) &&
418 (source is MethodGroupExpr)){
419 ((MethodGroupExpr) source).ReportUsageError ();
420 return null;
424 if (target_type == source_type){
425 if (source is New && target_type.IsValueType &&
426 (target.eclass != ExprClass.IndexerAccess) && (target.eclass != ExprClass.PropertyAccess)){
427 New n = (New) source;
429 if (n.SetValueTypeVariable (target))
430 return n;
431 else
432 return null;
435 return this;
439 // If this assignment/operator was part of a compound binary
440 // operator, then we allow an explicit conversion, as detailed
441 // in the spec.
444 if (this is CompoundAssign){
445 CompoundAssign a = (CompoundAssign) this;
447 Binary b = source as Binary;
448 if (b != null){
450 // 1. if the source is explicitly convertible to the
451 // target_type
454 source = Convert.ExplicitConversion (ec, source, target_type, loc);
455 if (source == null){
456 a.original_source.Error_ValueCannotBeConverted (loc, target_type, true);
457 return null;
461 // 2. and the original right side is implicitly convertible to
462 // the type of target
464 if (Convert.ImplicitStandardConversionExists (a.original_source, target_type))
465 return this;
468 // In the spec 2.4 they added: or if type of the target is int
469 // and the operator is a shift operator...
471 if (source_type == TypeManager.int32_type &&
472 (b.Oper == Binary.Operator.LeftShift || b.Oper == Binary.Operator.RightShift))
473 return this;
475 a.original_source.Error_ValueCannotBeConverted (loc, target_type, false);
476 return null;
480 if (source.eclass == ExprClass.MethodGroup && !TypeManager.IsDelegateType (target_type)) {
481 Report.Error (428, source.Location, "Cannot convert method group `{0}' to non-delegate type `{1}'. Did you intend to invoke the method?",
482 ((MethodGroupExpr)source).Name, target.GetSignatureForError ());
483 return null;
486 source = Convert.ImplicitConversionRequired (ec, source, target_type, loc);
487 if (source == null)
488 return null;
490 // If we're an embedded assignment, we need to create a new temporary variable
491 // for the converted value. Our parent will use this new variable as its source.
492 // The same applies when we have an embedded assignment - in this case, we need
493 // to convert our embedded assignment's temporary local variable to the correct
494 // type and store it in a new temporary local.
495 if (is_embedded || embedded != null) {
496 type = target_type;
497 temp = new LocalTemporary (type);
498 must_free_temp = true;
501 return this;
504 Expression EmitEmbedded (EmitContext ec)
506 // Emit an embedded assignment.
508 if (real_temp != null) {
509 // If we're the innermost assignment, `real_source' is the right-hand
510 // expression which gets assigned to all the variables left of it.
511 // Emit this expression and store its result in real_temp.
512 real_source.Emit (ec);
513 real_temp.Store (ec);
516 if (embedded != null)
517 embedded.EmitEmbedded (ec);
519 // This happens when we've done a type conversion, in this case source will be
520 // the expression which does the type conversion from real_temp.
521 // So emit it and store the result in temp; this is the var which will be read
522 // by our parent.
523 if (temp != real_temp) {
524 source.Emit (ec);
525 temp.Store (ec);
528 Expression temp_source = (temp != null) ? temp : source;
529 ((IAssignMethod) target).EmitAssign (ec, temp_source, false, false);
530 return temp_source;
533 void ReleaseEmbedded (EmitContext ec)
535 if (embedded != null)
536 embedded.ReleaseEmbedded (ec);
538 if (real_temp != null)
539 real_temp.Release (ec);
541 if (must_free_temp)
542 temp.Release (ec);
545 void Emit (EmitContext ec, bool is_statement)
547 if (target is EventExpr) {
548 ((EventExpr) target).EmitAddOrRemove (ec, source);
549 return;
552 IAssignMethod am = (IAssignMethod) target;
554 Expression temp_source;
555 if (embedded != null) {
556 temp_source = embedded.EmitEmbedded (ec);
558 if (temp != null) {
559 source.Emit (ec);
560 temp.Store (ec);
561 temp_source = temp;
563 } else
564 temp_source = source;
566 am.EmitAssign (ec, temp_source, !is_statement, this is CompoundAssign);
568 if (embedded != null) {
569 if (temp != null)
570 temp.Release (ec);
571 embedded.ReleaseEmbedded (ec);
575 public override void Emit (EmitContext ec)
577 Emit (ec, false);
580 public override void EmitStatement (EmitContext ec)
582 Emit (ec, true);
588 // This class is used for compound assignments.
590 class CompoundAssign : Assign {
591 Binary.Operator op;
592 public Expression original_source;
594 public CompoundAssign (Binary.Operator op, Expression target, Expression source)
595 : base (target, source, target.Location)
597 original_source = source;
598 this.op = op;
601 protected CompoundAssign (CompoundAssign embedded, Location l)
602 : this (embedded.op, embedded.target, embedded.source)
604 this.is_embedded = true;
607 protected override Assign GetEmbeddedAssign (Location loc)
609 return new CompoundAssign (this, loc);
612 public override Expression DoResolve (EmitContext ec)
614 original_source = original_source.Resolve (ec);
615 if (original_source == null)
616 return null;
618 target = target.Resolve (ec);
619 if (target == null)
620 return null;
623 // Only now we can decouple the original source/target
624 // into a tree, to guarantee that we do not have side
625 // effects.
627 source = new Binary (op, target, original_source);
628 return base.DoResolve (ec);