(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / mcs / assign.cs
blobe4f657bad02362aeb12e244817ce2930e69d4923
1 //
2 // assign.cs: Assignments.
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@gnome.org)
7 //
8 // (C) 2001, 2002, 2003 Ximian, Inc.
9 //
10 using System;
11 using System.Reflection;
12 using System.Reflection.Emit;
14 namespace Mono.CSharp {
16 /// <summary>
17 /// This interface is implemented by expressions that can be assigned to.
18 /// </summary>
19 /// <remarks>
20 /// This interface is implemented by Expressions whose values can not
21 /// store the result on the top of the stack.
22 ///
23 /// Expressions implementing this (Properties, Indexers and Arrays) would
24 /// perform an assignment of the Expression "source" into its final
25 /// location.
26 ///
27 /// No values on the top of the stack are expected to be left by
28 /// invoking this method.
29 /// </remarks>
30 public interface IAssignMethod {
32 // This is an extra version of Emit. If leave_copy is `true'
33 // A copy of the expression will be left on the stack at the
34 // end of the code generated for EmitAssign
36 void Emit (EmitContext ec, bool leave_copy);
39 // This method does the assignment
40 // `source' will be stored into the location specified by `this'
41 // if `leave_copy' is true, a copy of `source' will be left on the stack
42 // if `prepare_for_load' is true, when `source' is emitted, there will
43 // be data on the stack that it can use to compuatate its value. This is
44 // for expressions like a [f ()] ++, where you can't call `f ()' twice.
46 void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load);
49 For simple assignments, this interface is very simple, EmitAssign is called with source
50 as the source expression and leave_copy and prepare_for_load false.
52 For compound assignments it gets complicated.
54 EmitAssign will be called as before, however, prepare_for_load will be
55 true. The @source expression will contain an expression
56 which calls Emit. So, the calls look like:
58 this.EmitAssign (ec, source, false, true) ->
59 source.Emit (ec); ->
60 [...] ->
61 this.Emit (ec, false); ->
62 end this.Emit (ec, false); ->
63 end [...]
64 end source.Emit (ec);
65 end this.EmitAssign (ec, source, false, true)
68 When prepare_for_load is true, EmitAssign emits a `token' on the stack that
69 Emit will use for its state.
71 Let's take FieldExpr as an example. assume we are emitting f ().y += 1;
73 Here is the call tree again. This time, each call is annotated with the IL
74 it produces:
76 this.EmitAssign (ec, source, false, true)
77 call f
78 dup
80 Binary.Emit ()
81 this.Emit (ec, false);
82 ldfld y
83 end this.Emit (ec, false);
85 IntConstant.Emit ()
86 ldc.i4.1
87 end IntConstant.Emit
89 add
90 end Binary.Emit ()
92 stfld
93 end this.EmitAssign (ec, source, false, true)
95 Observe two things:
96 1) EmitAssign left a token on the stack. It was the result of f ().
97 2) This token was used by Emit
99 leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy
100 of the expression at that point in evaluation. This is used for pre/post inc/dec
101 and for a = x += y. Let's do the above example with leave_copy true in EmitAssign
103 this.EmitAssign (ec, source, true, true)
104 call f
107 Binary.Emit ()
108 this.Emit (ec, false);
109 ldfld y
110 end this.Emit (ec, false);
112 IntConstant.Emit ()
113 ldc.i4.1
114 end IntConstant.Emit
117 end Binary.Emit ()
120 stloc temp
121 stfld
122 ldloc temp
123 end this.EmitAssign (ec, source, true, true)
125 And with it true in Emit
127 this.EmitAssign (ec, source, false, true)
128 call f
131 Binary.Emit ()
132 this.Emit (ec, true);
133 ldfld y
135 stloc temp
136 end this.Emit (ec, true);
138 IntConstant.Emit ()
139 ldc.i4.1
140 end IntConstant.Emit
143 end Binary.Emit ()
145 stfld
146 ldloc temp
147 end this.EmitAssign (ec, source, false, true)
149 Note that these two examples are what happens for ++x and x++, respectively.
153 /// <summary>
154 /// An Expression to hold a temporary value.
155 /// </summary>
156 /// <remarks>
157 /// The LocalTemporary class is used to hold temporary values of a given
158 /// type to "simulate" the expression semantics on property and indexer
159 /// access whose return values are void.
161 /// The local temporary is used to alter the normal flow of code generation
162 /// basically it creates a local variable, and its emit instruction generates
163 /// code to access this value, return its address or save its value.
165 /// If `is_address' is true, then the value that we store is the address to the
166 /// real value, and not the value itself.
168 /// This is needed for a value type, because otherwise you just end up making a
169 /// copy of the value on the stack and modifying it. You really need a pointer
170 /// to the origional value so that you can modify it in that location. This
171 /// Does not happen with a class because a class is a pointer -- so you always
172 /// get the indirection.
174 /// The `is_address' stuff is really just a hack. We need to come up with a better
175 /// way to handle it.
176 /// </remarks>
177 public class LocalTemporary : Expression, IMemoryLocation {
178 LocalBuilder builder;
179 bool is_address;
181 public LocalTemporary (EmitContext ec, Type t) : this (ec, t, false) {}
183 public LocalTemporary (EmitContext ec, Type t, bool is_address)
185 type = t;
186 eclass = ExprClass.Value;
187 loc = Location.Null;
188 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (t): t);
189 this.is_address = is_address;
192 public LocalTemporary (LocalBuilder b, Type t)
194 type = t;
195 eclass = ExprClass.Value;
196 loc = Location.Null;
197 builder = b;
200 public void Release (EmitContext ec)
202 ec.FreeTemporaryLocal (builder, type);
203 builder = null;
206 public override Expression DoResolve (EmitContext ec)
208 return this;
211 public override void Emit (EmitContext ec)
213 ILGenerator ig = ec.ig;
215 ig.Emit (OpCodes.Ldloc, builder);
216 // we need to copy from the pointer
217 if (is_address)
218 LoadFromPtr (ig, type);
221 // NB: if you have `is_address' on the stack there must
222 // be a managed pointer. Otherwise, it is the type from
223 // the ctor.
224 public void Store (EmitContext ec)
226 ILGenerator ig = ec.ig;
227 ig.Emit (OpCodes.Stloc, builder);
230 public void AddressOf (EmitContext ec, AddressOp mode)
232 // if is_address, than this is just the address anyways,
233 // so we just return this.
234 ILGenerator ig = ec.ig;
236 if (is_address)
237 ig.Emit (OpCodes.Ldloc, builder);
238 else
239 ig.Emit (OpCodes.Ldloca, builder);
242 public bool PointsToAddress {
243 get {
244 return is_address;
249 /// <summary>
250 /// The Assign node takes care of assigning the value of source into
251 /// the expression represented by target.
252 /// </summary>
253 public class Assign : ExpressionStatement {
254 protected Expression target, source, real_source;
255 protected LocalTemporary temp = null, real_temp = null;
256 protected Assign embedded = null;
257 protected bool is_embedded = false;
258 protected bool must_free_temp = false;
260 public Assign (Expression target, Expression source, Location l)
262 this.target = target;
263 this.source = this.real_source = source;
264 this.loc = l;
267 protected Assign (Assign embedded, Location l)
268 : this (embedded.target, embedded.source, l)
270 this.is_embedded = true;
273 protected virtual Assign GetEmbeddedAssign (Location loc)
275 return new Assign (this, loc);
278 public Expression Target {
279 get {
280 return target;
283 set {
284 target = value;
288 public Expression Source {
289 get {
290 return source;
293 set {
294 source = value;
298 public static void error70 (EventInfo ei, Location l)
300 Report.Error (70, l, "The event '" + ei.Name +
301 "' can only appear on the left-side of a += or -= (except when" +
302 " used from within the type '" + ei.DeclaringType + "')");
306 // Will return either `this' or an instance of `New'.
308 public override Expression DoResolve (EmitContext ec)
310 // Create an embedded assignment if our source is an assignment.
311 if (source is Assign)
312 source = embedded = ((Assign) source).GetEmbeddedAssign (loc);
314 real_source = source = source.Resolve (ec);
315 if (source == null)
316 return null;
319 // This is used in an embedded assignment.
320 // As an example, consider the statement "A = X = Y = Z".
322 if (is_embedded && !(source is Constant)) {
323 // If this is the innermost assignment (the "Y = Z" in our example),
324 // create a new temporary local, otherwise inherit that variable
325 // from our child (the "X = (Y = Z)" inherits the local from the
326 // "Y = Z" assignment).
328 if (embedded == null) {
329 if (this is CompoundAssign)
330 real_temp = temp = new LocalTemporary (ec, target.Type);
331 else
332 real_temp = temp = new LocalTemporary (ec, source.Type);
333 } else
334 temp = embedded.temp;
336 // Set the source to the new temporary variable.
337 // This means that the following target.ResolveLValue () will tell
338 // the target to read it's source value from that variable.
339 source = temp;
342 // If we have an embedded assignment, use the embedded assignment's temporary
343 // local variable as source.
344 if (embedded != null)
345 source = (embedded.temp != null) ? embedded.temp : embedded.source;
347 target = target.ResolveLValue (ec, source);
349 if (target == null)
350 return null;
352 Type target_type = target.Type;
353 Type source_type = real_source.Type;
355 // If we're an embedded assignment, our parent will reuse our source as its
356 // source, it won't read from our target.
357 if (is_embedded)
358 type = source_type;
359 else
360 type = target_type;
361 eclass = ExprClass.Value;
364 if (target is EventExpr) {
365 EventInfo ei = ((EventExpr) target).EventInfo;
367 Expression ml = MemberLookup (
368 ec, ec.ContainerType, ei.Name,
369 MemberTypes.Event, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
371 if (ml == null) {
373 // If this is the case, then the Event does not belong
374 // to this Type and so, according to the spec
375 // is allowed to only appear on the left hand of
376 // the += and -= operators
378 // Note that target will not appear as an EventExpr
379 // in the case it is being referenced within the same type container;
380 // it will appear as a FieldExpr in that case.
383 if (!(source is BinaryDelegate)) {
384 error70 (ei, loc);
385 return null;
390 if (!(target is IAssignMethod) && (target.eclass != ExprClass.EventAccess)) {
391 Report.Error (131, loc,
392 "Left hand of an assignment must be a variable, " +
393 "a property or an indexer");
394 return null;
397 if ((source.eclass == ExprClass.Type) && (source is TypeExpr)) {
398 source.Error_UnexpectedKind ("variable or value", loc);
399 return null;
400 } else if ((RootContext.Version == LanguageVersion.ISO_1) &&
401 (source is MethodGroupExpr)){
402 ((MethodGroupExpr) source).ReportUsageError ();
403 return null;
407 if (target_type == source_type){
408 if (source is New && target_type.IsValueType &&
409 (target.eclass != ExprClass.IndexerAccess) && (target.eclass != ExprClass.PropertyAccess)){
410 New n = (New) source;
412 if (n.SetValueTypeVariable (target))
413 return n;
414 else
415 return null;
418 return this;
422 // If this assignemnt/operator was part of a compound binary
423 // operator, then we allow an explicit conversion, as detailed
424 // in the spec.
427 if (this is CompoundAssign){
428 CompoundAssign a = (CompoundAssign) this;
430 Binary b = source as Binary;
431 if (b != null){
433 // 1. if the source is explicitly convertible to the
434 // target_type
437 source = Convert.ExplicitConversion (ec, source, target_type, loc);
438 if (source == null){
439 Convert.Error_CannotImplicitConversion (loc, source_type, target_type);
440 return null;
444 // 2. and the original right side is implicitly convertible to
445 // the type of target
447 if (Convert.ImplicitStandardConversionExists (ec, a.original_source, target_type))
448 return this;
451 // In the spec 2.4 they added: or if type of the target is int
452 // and the operator is a shift operator...
454 if (source_type == TypeManager.int32_type &&
455 (b.Oper == Binary.Operator.LeftShift || b.Oper == Binary.Operator.RightShift))
456 return this;
458 Convert.Error_CannotImplicitConversion (loc, a.original_source.Type, target_type);
459 return null;
463 source = Convert.ImplicitConversionRequired (ec, source, target_type, loc);
464 if (source == null)
465 return null;
467 // If we're an embedded assignment, we need to create a new temporary variable
468 // for the converted value. Our parent will use this new variable as its source.
469 // The same applies when we have an embedded assignment - in this case, we need
470 // to convert our embedded assignment's temporary local variable to the correct
471 // type and store it in a new temporary local.
472 if (is_embedded || embedded != null) {
473 type = target_type;
474 temp = new LocalTemporary (ec, type);
475 must_free_temp = true;
478 return this;
481 Expression EmitEmbedded (EmitContext ec)
483 // Emit an embedded assignment.
485 if (real_temp != null) {
486 // If we're the innermost assignment, `real_source' is the right-hand
487 // expression which gets assigned to all the variables left of it.
488 // Emit this expression and store its result in real_temp.
489 real_source.Emit (ec);
490 real_temp.Store (ec);
493 if (embedded != null)
494 embedded.EmitEmbedded (ec);
496 // This happens when we've done a type conversion, in this case source will be
497 // the expression which does the type conversion from real_temp.
498 // So emit it and store the result in temp; this is the var which will be read
499 // by our parent.
500 if (temp != real_temp) {
501 source.Emit (ec);
502 temp.Store (ec);
505 Expression temp_source = (temp != null) ? temp : source;
506 ((IAssignMethod) target).EmitAssign (ec, temp_source, false, false);
507 return temp_source;
510 void ReleaseEmbedded (EmitContext ec)
512 if (embedded != null)
513 embedded.ReleaseEmbedded (ec);
515 if (real_temp != null)
516 real_temp.Release (ec);
518 if (must_free_temp)
519 temp.Release (ec);
522 void Emit (EmitContext ec, bool is_statement)
524 if (target is EventExpr) {
525 ((EventExpr) target).EmitAddOrRemove (ec, source);
526 return;
529 IAssignMethod am = (IAssignMethod) target;
531 Expression temp_source;
532 if (embedded != null) {
533 temp_source = embedded.EmitEmbedded (ec);
535 if (temp != null) {
536 source.Emit (ec);
537 temp.Store (ec);
538 temp_source = temp;
540 } else
541 temp_source = source;
543 am.EmitAssign (ec, temp_source, !is_statement, this is CompoundAssign);
545 if (embedded != null) {
546 if (temp != null)
547 temp.Release (ec);
548 embedded.ReleaseEmbedded (ec);
552 public override void Emit (EmitContext ec)
554 Emit (ec, false);
557 public override void EmitStatement (EmitContext ec)
559 Emit (ec, true);
565 // This class is used for compound assignments.
567 class CompoundAssign : Assign {
568 Binary.Operator op;
569 public Expression original_source;
571 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Location l)
572 : base (target, source, l)
574 original_source = source;
575 this.op = op;
578 protected CompoundAssign (CompoundAssign embedded, Location l)
579 : this (embedded.op, embedded.target, embedded.source, l)
581 this.is_embedded = true;
584 protected override Assign GetEmbeddedAssign (Location loc)
586 return new CompoundAssign (this, loc);
589 public Expression ResolveSource (EmitContext ec)
591 return original_source.Resolve (ec);
594 public override Expression DoResolve (EmitContext ec)
596 original_source = original_source.Resolve (ec);
597 if (original_source == null)
598 return null;
600 target = target.Resolve (ec);
601 if (target == null)
602 return null;
605 // Only now we can decouple the original source/target
606 // into a tree, to guarantee that we do not have side
607 // effects.
609 source = new Binary (op, target, original_source, loc);
610 return base.DoResolve (ec);