* TypeDef.cs (TypeDef.IComparable): Implement IComparable interface.
[mcs.git] / gmcs / assign.cs
blob6b81d51f4b630a55aaaf82dd4476fc04e1f463b4
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 (EmitContext ec, Type t) : this (ec, t, false) {}
184 public LocalTemporary (EmitContext ec, Type t, bool is_address)
186 type = t;
187 eclass = ExprClass.Value;
188 loc = Location.Null;
189 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (t): t);
190 this.is_address = is_address;
193 public LocalTemporary (LocalBuilder b, Type t)
195 type = t;
196 eclass = ExprClass.Value;
197 loc = Location.Null;
198 builder = b;
201 public void Release (EmitContext ec)
203 ec.FreeTemporaryLocal (builder, type);
204 builder = null;
207 public override Expression DoResolve (EmitContext ec)
209 return this;
212 public override void Emit (EmitContext ec)
214 ILGenerator ig = ec.ig;
216 ig.Emit (OpCodes.Ldloc, builder);
217 // we need to copy from the pointer
218 if (is_address)
219 LoadFromPtr (ig, type);
222 // NB: if you have `is_address' on the stack there must
223 // be a managed pointer. Otherwise, it is the type from
224 // the ctor.
225 public void Store (EmitContext ec)
227 ILGenerator ig = ec.ig;
228 ig.Emit (OpCodes.Stloc, builder);
231 public void AddressOf (EmitContext ec, AddressOp mode)
233 // if is_address, than this is just the address anyways,
234 // so we just return this.
235 ILGenerator ig = ec.ig;
237 if (is_address)
238 ig.Emit (OpCodes.Ldloc, builder);
239 else
240 ig.Emit (OpCodes.Ldloca, builder);
243 public bool PointsToAddress {
244 get {
245 return is_address;
250 /// <summary>
251 /// The Assign node takes care of assigning the value of source into
252 /// the expression represented by target.
253 /// </summary>
254 public class Assign : ExpressionStatement {
255 protected Expression target, source, real_source;
256 protected LocalTemporary temp = null, real_temp = null;
257 protected Assign embedded = null;
258 protected bool is_embedded = false;
259 protected bool must_free_temp = false;
261 public Assign (Expression target, Expression source)
262 : this (target, source, target.Location)
266 public Assign (Expression target, Expression source, Location l)
268 this.target = target;
269 this.source = this.real_source = source;
270 this.loc = l;
273 protected Assign (Assign embedded, Location l)
274 : this (embedded.target, embedded.source, l)
276 this.is_embedded = true;
279 protected virtual Assign GetEmbeddedAssign (Location loc)
281 return new Assign (this, loc);
284 public Expression Target {
285 get {
286 return target;
289 set {
290 target = value;
294 public Expression Source {
295 get {
296 return source;
299 set {
300 source = value;
304 public static void error70 (EventInfo ei, Location l)
306 Report.Error (70, l, "The event `" + TypeManager.CSharpSignature (ei) +
307 "' can only appear on the left hand side of += or -= (except when" +
308 " used from within the type `" + ei.DeclaringType + "')");
312 // Will return either `this' or an instance of `New'.
314 public override Expression DoResolve (EmitContext ec)
316 // Create an embedded assignment if our source is an assignment.
317 if (source is Assign)
318 source = embedded = ((Assign) source).GetEmbeddedAssign (loc);
320 real_source = source = source.Resolve (ec);
322 if (source == null) {
323 // Ensure that we don't propagate the error as spurious "uninitialized variable" errors.
324 target = target.ResolveLValue (ec, EmptyExpression.Null, Location);
325 return null;
329 // This is used in an embedded assignment.
330 // As an example, consider the statement "A = X = Y = Z".
332 if (is_embedded && !(source is Constant)) {
333 // If this is the innermost assignment (the "Y = Z" in our example),
334 // create a new temporary local, otherwise inherit that variable
335 // from our child (the "X = (Y = Z)" inherits the local from the
336 // "Y = Z" assignment).
338 if (embedded == null) {
339 if (this is CompoundAssign)
340 real_temp = temp = new LocalTemporary (ec, target.Type);
341 else
342 real_temp = temp = new LocalTemporary (ec, source.Type);
343 } else
344 temp = embedded.temp;
346 // Set the source to the new temporary variable.
347 // This means that the following target.ResolveLValue () will tell
348 // the target to read it's source value from that variable.
349 source = temp;
352 // If we have an embedded assignment, use the embedded assignment's temporary
353 // local variable as source.
354 if (embedded != null)
355 source = (embedded.temp != null) ? embedded.temp : embedded.source;
357 target = target.ResolveLValue (ec, source, Location);
359 if (target == null)
360 return null;
362 if (source.Equals (target)) {
363 Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
366 Type target_type = target.Type;
367 Type source_type = real_source.Type;
369 // If we're an embedded assignment, our parent will reuse our source as its
370 // source, it won't read from our target.
371 if (is_embedded)
372 type = source_type;
373 else
374 type = target_type;
375 eclass = ExprClass.Value;
377 if (target is EventExpr) {
378 EventInfo ei = ((EventExpr) target).EventInfo;
380 Expression ml = MemberLookup (
381 ec, ec.ContainerType, ei.Name,
382 MemberTypes.Event, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
384 if (ml == null) {
386 // If this is the case, then the Event does not belong
387 // to this Type and so, according to the spec
388 // is allowed to only appear on the left hand of
389 // the += and -= operators
391 // Note that target will not appear as an EventExpr
392 // in the case it is being referenced within the same type container;
393 // it will appear as a FieldExpr in that case.
396 if (!(source is BinaryDelegate)) {
397 error70 (ei, loc);
398 return null;
403 FieldExpr field_exp = target as FieldExpr;
404 if (field_exp != null && field_exp.DeclaringType.IsValueType && !ec.IsConstructor && !ec.IsFieldInitializer) {
405 field_exp = field_exp.InstanceExpression as FieldExpr;
406 if (field_exp != null && field_exp.FieldInfo.IsInitOnly) {
407 if (field_exp.IsStatic) {
408 Report.Error (1650, loc, "Fields of static readonly field `{0}' cannot be assigned to (except in a static constructor or a variable initializer)",
409 field_exp.GetSignatureForError ());
410 } else {
411 Report.Error (1648, loc, "Members of readonly field `{0}' cannot be modified (except in a constructor or a variable initializer)",
412 field_exp.GetSignatureForError ());
414 return null;
418 if (!(target is IAssignMethod) && (target.eclass != ExprClass.EventAccess)) {
419 Report.Error (131, loc,
420 "Left hand of an assignment must be a variable, " +
421 "a property or an indexer");
422 return null;
425 if ((source.eclass == ExprClass.Type) && (source is TypeExpr)) {
426 source.Error_UnexpectedKind (ec, "variable or value", loc);
427 return null;
428 } else if ((RootContext.Version == LanguageVersion.ISO_1) &&
429 (source is MethodGroupExpr)){
430 ((MethodGroupExpr) source).ReportUsageError ();
431 return null;
435 if (target_type == source_type){
436 if (source is New && target_type.IsValueType &&
437 (target.eclass != ExprClass.IndexerAccess) && (target.eclass != ExprClass.PropertyAccess)){
438 New n = (New) source;
440 if (n.SetValueTypeVariable (target))
441 return n;
442 else
443 return null;
446 return this;
450 // If this assignemnt/operator was part of a compound binary
451 // operator, then we allow an explicit conversion, as detailed
452 // in the spec.
455 if (this is CompoundAssign){
456 CompoundAssign a = (CompoundAssign) this;
458 Binary b = source as Binary;
459 if (b != null){
461 // 1. if the source is explicitly convertible to the
462 // target_type
465 source = Convert.ExplicitConversion (ec, source, target_type, loc);
466 if (source == null){
467 a.original_source.Error_ValueCannotBeConverted (loc, target_type, true);
468 return null;
472 // 2. and the original right side is implicitly convertible to
473 // the type of target
475 if (Convert.ImplicitStandardConversionExists (ec, a.original_source, target_type))
476 return this;
479 // In the spec 2.4 they added: or if type of the target is int
480 // and the operator is a shift operator...
482 if (source_type == TypeManager.int32_type &&
483 (b.Oper == Binary.Operator.LeftShift || b.Oper == Binary.Operator.RightShift))
484 return this;
486 a.original_source.Error_ValueCannotBeConverted (loc, target_type, false);
487 return null;
491 if (source.eclass == ExprClass.MethodGroup && !TypeManager.IsDelegateType (target_type)) {
492 Report.Error (428, source.Location, "Cannot convert method group `{0}' to non-delegate type `{1}'. Did you intend to invoke the method?",
493 ((MethodGroupExpr)source).Name, target.GetSignatureForError ());
494 return null;
497 source = Convert.ImplicitConversionRequired (ec, source, target_type, loc);
498 if (source == null)
499 return null;
501 // If we're an embedded assignment, we need to create a new temporary variable
502 // for the converted value. Our parent will use this new variable as its source.
503 // The same applies when we have an embedded assignment - in this case, we need
504 // to convert our embedded assignment's temporary local variable to the correct
505 // type and store it in a new temporary local.
506 if (is_embedded || embedded != null) {
507 type = target_type;
508 temp = new LocalTemporary (ec, type);
509 must_free_temp = true;
512 return this;
515 Expression EmitEmbedded (EmitContext ec)
517 // Emit an embedded assignment.
519 if (real_temp != null) {
520 // If we're the innermost assignment, `real_source' is the right-hand
521 // expression which gets assigned to all the variables left of it.
522 // Emit this expression and store its result in real_temp.
523 real_source.Emit (ec);
524 real_temp.Store (ec);
527 if (embedded != null)
528 embedded.EmitEmbedded (ec);
530 // This happens when we've done a type conversion, in this case source will be
531 // the expression which does the type conversion from real_temp.
532 // So emit it and store the result in temp; this is the var which will be read
533 // by our parent.
534 if (temp != real_temp) {
535 source.Emit (ec);
536 temp.Store (ec);
539 Expression temp_source = (temp != null) ? temp : source;
540 ((IAssignMethod) target).EmitAssign (ec, temp_source, false, false);
541 return temp_source;
544 void ReleaseEmbedded (EmitContext ec)
546 if (embedded != null)
547 embedded.ReleaseEmbedded (ec);
549 if (real_temp != null)
550 real_temp.Release (ec);
552 if (must_free_temp)
553 temp.Release (ec);
556 void Emit (EmitContext ec, bool is_statement)
558 if (target is EventExpr) {
559 ((EventExpr) target).EmitAddOrRemove (ec, source);
560 return;
563 IAssignMethod am = (IAssignMethod) target;
565 Expression temp_source;
566 if (embedded != null) {
567 temp_source = embedded.EmitEmbedded (ec);
569 if (temp != null) {
570 source.Emit (ec);
571 temp.Store (ec);
572 temp_source = temp;
574 } else
575 temp_source = source;
577 am.EmitAssign (ec, temp_source, !is_statement, this is CompoundAssign);
579 if (embedded != null) {
580 if (temp != null)
581 temp.Release (ec);
582 embedded.ReleaseEmbedded (ec);
586 public override void Emit (EmitContext ec)
588 Emit (ec, false);
591 public override void EmitStatement (EmitContext ec)
593 Emit (ec, true);
599 // This class is used for compound assignments.
601 class CompoundAssign : Assign {
602 Binary.Operator op;
603 public Expression original_source;
605 public CompoundAssign (Binary.Operator op, Expression target, Expression source)
606 : base (target, source, target.Location)
608 original_source = source;
609 this.op = op;
612 protected CompoundAssign (CompoundAssign embedded, Location l)
613 : this (embedded.op, embedded.target, embedded.source)
615 this.is_embedded = true;
618 protected override Assign GetEmbeddedAssign (Location loc)
620 return new CompoundAssign (this, loc);
623 public Expression ResolveSource (EmitContext ec)
625 return original_source.Resolve (ec);
628 public override Expression DoResolve (EmitContext ec)
630 original_source = original_source.Resolve (ec);
631 if (original_source == null)
632 return null;
634 target = target.Resolve (ec);
635 if (target == null)
636 return null;
639 // Only now we can decouple the original source/target
640 // into a tree, to guarantee that we do not have side
641 // effects.
643 source = new Binary (op, target, original_source);
644 return base.DoResolve (ec);