Implement proper ctor parameter checking
[mono-project.git] / mcs / mcs / assign.cs
blob95ca88d4a8ca62c2c5ac129a73ce5f91bc7be7d0
1 //
2 // assign.cs: Assignments.
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
8 //
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
14 using System;
16 #if STATIC
17 using IKVM.Reflection.Emit;
18 #else
19 using System.Reflection.Emit;
20 #endif
22 namespace Mono.CSharp {
24 /// <summary>
25 /// This interface is implemented by expressions that can be assigned to.
26 /// </summary>
27 /// <remarks>
28 /// This interface is implemented by Expressions whose values can not
29 /// store the result on the top of the stack.
30 ///
31 /// Expressions implementing this (Properties, Indexers and Arrays) would
32 /// perform an assignment of the Expression "source" into its final
33 /// location.
34 ///
35 /// No values on the top of the stack are expected to be left by
36 /// invoking this method.
37 /// </remarks>
38 public interface IAssignMethod {
40 // This is an extra version of Emit. If leave_copy is `true'
41 // A copy of the expression will be left on the stack at the
42 // end of the code generated for EmitAssign
44 void Emit (EmitContext ec, bool leave_copy);
47 // This method does the assignment
48 // `source' will be stored into the location specified by `this'
49 // if `leave_copy' is true, a copy of `source' will be left on the stack
50 // if `prepare_for_load' is true, when `source' is emitted, there will
51 // be data on the stack that it can use to compuatate its value. This is
52 // for expressions like a [f ()] ++, where you can't call `f ()' twice.
54 void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound);
57 For simple assignments, this interface is very simple, EmitAssign is called with source
58 as the source expression and leave_copy and prepare_for_load false.
60 For compound assignments it gets complicated.
62 EmitAssign will be called as before, however, prepare_for_load will be
63 true. The @source expression will contain an expression
64 which calls Emit. So, the calls look like:
66 this.EmitAssign (ec, source, false, true) ->
67 source.Emit (ec); ->
68 [...] ->
69 this.Emit (ec, false); ->
70 end this.Emit (ec, false); ->
71 end [...]
72 end source.Emit (ec);
73 end this.EmitAssign (ec, source, false, true)
76 When prepare_for_load is true, EmitAssign emits a `token' on the stack that
77 Emit will use for its state.
79 Let's take FieldExpr as an example. assume we are emitting f ().y += 1;
81 Here is the call tree again. This time, each call is annotated with the IL
82 it produces:
84 this.EmitAssign (ec, source, false, true)
85 call f
86 dup
88 Binary.Emit ()
89 this.Emit (ec, false);
90 ldfld y
91 end this.Emit (ec, false);
93 IntConstant.Emit ()
94 ldc.i4.1
95 end IntConstant.Emit
97 add
98 end Binary.Emit ()
100 stfld
101 end this.EmitAssign (ec, source, false, true)
103 Observe two things:
104 1) EmitAssign left a token on the stack. It was the result of f ().
105 2) This token was used by Emit
107 leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy
108 of the expression at that point in evaluation. This is used for pre/post inc/dec
109 and for a = x += y. Let's do the above example with leave_copy true in EmitAssign
111 this.EmitAssign (ec, source, true, true)
112 call f
115 Binary.Emit ()
116 this.Emit (ec, false);
117 ldfld y
118 end this.Emit (ec, false);
120 IntConstant.Emit ()
121 ldc.i4.1
122 end IntConstant.Emit
125 end Binary.Emit ()
128 stloc temp
129 stfld
130 ldloc temp
131 end this.EmitAssign (ec, source, true, true)
133 And with it true in Emit
135 this.EmitAssign (ec, source, false, true)
136 call f
139 Binary.Emit ()
140 this.Emit (ec, true);
141 ldfld y
143 stloc temp
144 end this.Emit (ec, true);
146 IntConstant.Emit ()
147 ldc.i4.1
148 end IntConstant.Emit
151 end Binary.Emit ()
153 stfld
154 ldloc temp
155 end this.EmitAssign (ec, source, false, true)
157 Note that these two examples are what happens for ++x and x++, respectively.
161 /// <summary>
162 /// An Expression to hold a temporary value.
163 /// </summary>
164 /// <remarks>
165 /// The LocalTemporary class is used to hold temporary values of a given
166 /// type to "simulate" the expression semantics. The local variable is
167 /// never captured.
169 /// The local temporary is used to alter the normal flow of code generation
170 /// basically it creates a local variable, and its emit instruction generates
171 /// code to access this value, return its address or save its value.
173 /// If `is_address' is true, then the value that we store is the address to the
174 /// real value, and not the value itself.
176 /// This is needed for a value type, because otherwise you just end up making a
177 /// copy of the value on the stack and modifying it. You really need a pointer
178 /// to the origional value so that you can modify it in that location. This
179 /// Does not happen with a class because a class is a pointer -- so you always
180 /// get the indirection.
182 /// </remarks>
183 public class LocalTemporary : Expression, IMemoryLocation, IAssignMethod {
184 LocalBuilder builder;
186 public LocalTemporary (TypeSpec t)
188 type = t;
189 eclass = ExprClass.Value;
192 public LocalTemporary (LocalBuilder b, TypeSpec t)
193 : this (t)
195 builder = b;
198 public void Release (EmitContext ec)
200 ec.FreeTemporaryLocal (builder, type);
201 builder = null;
204 public override bool ContainsEmitWithAwait ()
206 return false;
209 public override Expression CreateExpressionTree (ResolveContext ec)
211 Arguments args = new Arguments (1);
212 args.Add (new Argument (this));
213 return CreateExpressionFactoryCall (ec, "Constant", args);
216 protected override Expression DoResolve (ResolveContext ec)
218 return this;
221 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
223 return this;
226 public override void Emit (EmitContext ec)
228 if (builder == null)
229 throw new InternalErrorException ("Emit without Store, or after Release");
231 ec.Emit (OpCodes.Ldloc, builder);
234 #region IAssignMethod Members
236 public void Emit (EmitContext ec, bool leave_copy)
238 Emit (ec);
240 if (leave_copy)
241 Emit (ec);
244 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
246 if (isCompound)
247 throw new NotImplementedException ();
249 source.Emit (ec);
251 Store (ec);
253 if (leave_copy)
254 Emit (ec);
257 #endregion
259 public LocalBuilder Builder {
260 get { return builder; }
263 public void Store (EmitContext ec)
265 if (builder == null)
266 builder = ec.GetTemporaryLocal (type);
268 ec.Emit (OpCodes.Stloc, builder);
271 public void AddressOf (EmitContext ec, AddressOp mode)
273 if (builder == null)
274 builder = ec.GetTemporaryLocal (type);
276 if (builder.LocalType.IsByRef) {
278 // if is_address, than this is just the address anyways,
279 // so we just return this.
281 ec.Emit (OpCodes.Ldloc, builder);
282 } else {
283 ec.Emit (OpCodes.Ldloca, builder);
288 /// <summary>
289 /// The Assign node takes care of assigning the value of source into
290 /// the expression represented by target.
291 /// </summary>
292 public abstract class Assign : ExpressionStatement {
293 protected Expression target, source;
295 protected Assign (Expression target, Expression source, Location loc)
297 this.target = target;
298 this.source = source;
299 this.loc = loc;
302 public Expression Target {
303 get { return target; }
306 public Expression Source {
307 get {
308 return source;
312 public override bool ContainsEmitWithAwait ()
314 return target.ContainsEmitWithAwait () || source.ContainsEmitWithAwait ();
317 public override Expression CreateExpressionTree (ResolveContext ec)
319 ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
320 return null;
323 protected override Expression DoResolve (ResolveContext ec)
325 bool ok = true;
326 source = source.Resolve (ec);
328 if (source == null) {
329 ok = false;
330 source = EmptyExpression.Null;
333 target = target.ResolveLValue (ec, source);
335 if (target == null || !ok)
336 return null;
338 TypeSpec target_type = target.Type;
339 TypeSpec source_type = source.Type;
341 eclass = ExprClass.Value;
342 type = target_type;
344 if (!(target is IAssignMethod)) {
345 Error_ValueAssignment (ec, loc);
346 return null;
349 if (target_type != source_type) {
350 Expression resolved = ResolveConversions (ec);
352 if (resolved != this)
353 return resolved;
356 return this;
359 #if NET_4_0
360 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
362 var tassign = target as IDynamicAssign;
363 if (tassign == null)
364 throw new InternalErrorException (target.GetType () + " does not support dynamic assignment");
366 var target_object = tassign.MakeAssignExpression (ctx, source);
369 // Some hacking is needed as DLR does not support void type and requires
370 // always have object convertible return type to support caching and chaining
372 // We do this by introducing an explicit block which returns RHS value when
373 // available or null
375 if (target_object.NodeType == System.Linq.Expressions.ExpressionType.Block)
376 return target_object;
378 System.Linq.Expressions.UnaryExpression source_object;
379 if (ctx.HasSet (BuilderContext.Options.CheckedScope)) {
380 source_object = System.Linq.Expressions.Expression.ConvertChecked (source.MakeExpression (ctx), target_object.Type);
381 } else {
382 source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type);
385 return System.Linq.Expressions.Expression.Assign (target_object, source_object);
387 #endif
388 protected virtual Expression ResolveConversions (ResolveContext ec)
390 source = Convert.ImplicitConversionRequired (ec, source, target.Type, source.Location);
391 if (source == null)
392 return null;
394 return this;
397 void Emit (EmitContext ec, bool is_statement)
399 IAssignMethod t = (IAssignMethod) target;
400 t.EmitAssign (ec, source, !is_statement, this is CompoundAssign);
403 public override void Emit (EmitContext ec)
405 Emit (ec, false);
408 public override void EmitStatement (EmitContext ec)
410 Emit (ec, true);
413 protected override void CloneTo (CloneContext clonectx, Expression t)
415 Assign _target = (Assign) t;
417 _target.target = target.Clone (clonectx);
418 _target.source = source.Clone (clonectx);
422 public class SimpleAssign : Assign
424 public SimpleAssign (Expression target, Expression source)
425 : this (target, source, target.Location)
429 public SimpleAssign (Expression target, Expression source, Location loc)
430 : base (target, source, loc)
434 bool CheckEqualAssign (Expression t)
436 if (source is Assign) {
437 Assign a = (Assign) source;
438 if (t.Equals (a.Target))
439 return true;
440 return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
442 return t.Equals (source);
445 protected override Expression DoResolve (ResolveContext ec)
447 Expression e = base.DoResolve (ec);
448 if (e == null || e != this)
449 return e;
451 if (CheckEqualAssign (target))
452 ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
454 return this;
458 public class RuntimeExplicitAssign : Assign
460 public RuntimeExplicitAssign (Expression target, Expression source)
461 : base (target, source, target.Location)
465 protected override Expression ResolveConversions (ResolveContext ec)
467 source = EmptyCast.Create (source, target.Type);
468 return this;
473 // Compiler generated assign
475 class CompilerAssign : Assign
477 public CompilerAssign (Expression target, Expression source, Location loc)
478 : base (target, source, loc)
482 public void UpdateSource (Expression source)
484 base.source = source;
489 // Implements fields and events class initializers
491 public class FieldInitializer : Assign
494 // Field initializers are tricky for partial classes. They have to
495 // share same constructor (block) for expression trees resolve but
496 // they have they own resolve scope
498 sealed class FieldInitializerContext : ResolveContext
500 ExplicitBlock ctor_block;
502 public FieldInitializerContext (IMemberContext mc, ResolveContext constructorContext)
503 : base (mc, Options.FieldInitializerScope | Options.ConstructorScope)
505 this.ctor_block = constructorContext.CurrentBlock.Explicit;
508 public override ExplicitBlock ConstructorBlock {
509 get {
510 return ctor_block;
516 // Keep resolved value because field initializers have their own rules
518 ExpressionStatement resolved;
519 IMemberContext mc;
521 public FieldInitializer (FieldSpec spec, Expression expression, IMemberContext mc)
522 : base (new FieldExpr (spec, expression.Location), expression, expression.Location)
524 this.mc = mc;
525 if (!spec.IsStatic)
526 ((FieldExpr)target).InstanceExpression = new CompilerGeneratedThis (mc.CurrentType, expression.Location);
529 protected override Expression DoResolve (ResolveContext ec)
531 // Field initializer can be resolved (fail) many times
532 if (source == null)
533 return null;
535 if (resolved == null) {
536 var ctx = new FieldInitializerContext (mc, ec);
537 resolved = base.DoResolve (ctx) as ExpressionStatement;
540 return resolved;
543 public override void EmitStatement (EmitContext ec)
545 if (resolved == null)
546 return;
548 if (resolved != this)
549 resolved.EmitStatement (ec);
550 else
551 base.EmitStatement (ec);
554 public bool IsDefaultInitializer {
555 get {
556 Constant c = source as Constant;
557 if (c == null)
558 return false;
560 FieldExpr fe = (FieldExpr)target;
561 return c.IsDefaultInitializer (fe.Type);
565 public override bool IsSideEffectFree {
566 get {
567 return source.IsSideEffectFree;
573 // This class is used for compound assignments.
575 public class CompoundAssign : Assign
577 // This is just a hack implemented for arrays only
578 public sealed class TargetExpression : Expression
580 readonly Expression child;
582 public TargetExpression (Expression child)
584 this.child = child;
585 this.loc = child.Location;
588 public override bool ContainsEmitWithAwait ()
590 return child.ContainsEmitWithAwait ();
593 public override Expression CreateExpressionTree (ResolveContext ec)
595 throw new NotSupportedException ("ET");
598 protected override Expression DoResolve (ResolveContext ec)
600 type = child.Type;
601 eclass = ExprClass.Value;
602 return this;
605 public override void Emit (EmitContext ec)
607 child.Emit (ec);
610 public override Expression EmitToField (EmitContext ec)
612 return child.EmitToField (ec);
616 // Used for underlying binary operator
617 readonly Binary.Operator op;
618 Expression right;
619 Expression left;
621 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Location loc)
622 : base (target, source, loc)
624 right = source;
625 this.op = op;
628 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left, Location loc)
629 : this (op, target, source, loc)
631 this.left = left;
634 protected override Expression DoResolve (ResolveContext ec)
636 right = right.Resolve (ec);
637 if (right == null)
638 return null;
640 MemberAccess ma = target as MemberAccess;
641 using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) {
642 target = target.Resolve (ec);
645 if (target == null)
646 return null;
648 if (target is MethodGroupExpr){
649 ec.Report.Error (1656, loc,
650 "Cannot assign to `{0}' because it is a `{1}'",
651 ((MethodGroupExpr)target).Name, target.ExprClassName);
652 return null;
655 var event_expr = target as EventExpr;
656 if (event_expr != null) {
657 source = Convert.ImplicitConversionRequired (ec, right, target.Type, loc);
658 if (source == null)
659 return null;
661 Expression rside;
662 if (op == Binary.Operator.Addition)
663 rside = EmptyExpression.EventAddition;
664 else if (op == Binary.Operator.Subtraction)
665 rside = EmptyExpression.EventSubtraction;
666 else
667 rside = null;
669 target = target.ResolveLValue (ec, rside);
670 if (target == null)
671 return null;
673 eclass = ExprClass.Value;
674 type = event_expr.Operator.ReturnType;
675 return this;
679 // Only now we can decouple the original source/target
680 // into a tree, to guarantee that we do not have side
681 // effects.
683 if (left == null)
684 left = new TargetExpression (target);
686 source = new Binary (op, left, right, true, loc);
688 if (target is DynamicMemberAssignable) {
689 Arguments targs = ((DynamicMemberAssignable) target).Arguments;
690 source = source.Resolve (ec);
692 Arguments args = new Arguments (targs.Count + 1);
693 args.AddRange (targs);
694 args.Add (new Argument (source));
696 var binder_flags = CSharpBinderFlags.ValueFromCompoundAssignment;
699 // Compound assignment does target conversion using additional method
700 // call, set checked context as the binary operation can overflow
702 if (ec.HasSet (ResolveContext.Options.CheckedScope))
703 binder_flags |= CSharpBinderFlags.CheckedContext;
705 if (target is DynamicMemberBinder) {
706 source = new DynamicMemberBinder (ma.Name, binder_flags, args, loc).Resolve (ec);
708 // Handles possible event addition/subtraction
709 if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) {
710 args = new Arguments (targs.Count + 1);
711 args.AddRange (targs);
712 args.Add (new Argument (right));
713 string method_prefix = op == Binary.Operator.Addition ?
714 Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix;
716 var invoke = DynamicInvocation.CreateSpecialNameInvoke (
717 new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec);
719 args = new Arguments (targs.Count);
720 args.AddRange (targs);
721 source = new DynamicEventCompoundAssign (ma.Name, args,
722 (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec);
724 } else {
725 source = new DynamicIndexBinder (binder_flags, args, loc).Resolve (ec);
728 return source;
731 return base.DoResolve (ec);
734 protected override Expression ResolveConversions (ResolveContext ec)
737 // LAMESPEC: Under dynamic context no target conversion is happening
738 // This allows more natual dynamic behaviour but breaks compatibility
739 // with static binding
741 if (target is RuntimeValueExpression)
742 return this;
744 TypeSpec target_type = target.Type;
747 // 1. the return type is implicitly convertible to the type of target
749 if (Convert.ImplicitConversionExists (ec, source, target_type)) {
750 source = Convert.ImplicitConversion (ec, source, target_type, loc);
751 return this;
755 // Otherwise, if the selected operator is a predefined operator
757 Binary b = source as Binary;
758 if (b == null && source is ReducedExpression)
759 b = ((ReducedExpression) source).OriginalExpression as Binary;
761 if (b != null) {
763 // 2a. the operator is a shift operator
765 // 2b. the return type is explicitly convertible to the type of x, and
766 // y is implicitly convertible to the type of x
768 if ((b.Oper & Binary.Operator.ShiftMask) != 0 ||
769 Convert.ImplicitConversionExists (ec, right, target_type)) {
770 source = Convert.ExplicitConversion (ec, source, target_type, loc);
771 return this;
775 if (source.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
776 Arguments arg = new Arguments (1);
777 arg.Add (new Argument (source));
778 return new SimpleAssign (target, new DynamicConversion (target_type, CSharpBinderFlags.ConvertExplicit, arg, loc), loc).Resolve (ec);
781 right.Error_ValueCannotBeConverted (ec, loc, target_type, false);
782 return null;
785 protected override void CloneTo (CloneContext clonectx, Expression t)
787 CompoundAssign ctarget = (CompoundAssign) t;
789 ctarget.right = ctarget.source = source.Clone (clonectx);
790 ctarget.target = target.Clone (clonectx);