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 10 // 11 // Copyright 2001, 2002, 2003 Ximian, Inc. 12 // Copyright 2004-2008 Novell, Inc 13 // Copyright 2011 Xamarin Inc 14 // 15 using System; 16 17 #if STATIC 18 using IKVM.Reflection.Emit; 19 #else 20 using System.Reflection.Emit; 21 #endif 22 23 namespace Mono.CSharp { 24 25 /// <summary> 26 /// This interface is implemented by expressions that can be assigned to. 27 /// </summary> 28 /// <remarks> 29 /// This interface is implemented by Expressions whose values can not 30 /// store the result on the top of the stack. 31 /// 32 /// Expressions implementing this (Properties, Indexers and Arrays) would 33 /// perform an assignment of the Expression "source" into its final 34 /// location. 35 /// 36 /// No values on the top of the stack are expected to be left by 37 /// invoking this method. 38 /// </remarks> 39 public interface IAssignMethod { 40 // 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 44 // Emit(EmitContext ec, bool leave_copy)45 void Emit (EmitContext ec, bool leave_copy); 46 47 // 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. 54 // EmitAssign(EmitContext ec, Expression source, bool leave_copy, bool isCompound)55 void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound); 56 57 /* 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. 60 61 For compound assignments it gets complicated. 62 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: 66 67 this.EmitAssign (ec, source, false, true) -> 68 source.Emit (ec); -> 69 [...] -> 70 this.Emit (ec, false); -> 71 end this.Emit (ec, false); -> 72 end [...] 73 end source.Emit (ec); 74 end this.EmitAssign (ec, source, false, true) 75 76 77 When prepare_for_load is true, EmitAssign emits a `token' on the stack that 78 Emit will use for its state. 79 80 Let's take FieldExpr as an example. assume we are emitting f ().y += 1; 81 82 Here is the call tree again. This time, each call is annotated with the IL 83 it produces: 84 85 this.EmitAssign (ec, source, false, true) 86 call f 87 dup 88 89 Binary.Emit () 90 this.Emit (ec, false); 91 ldfld y 92 end this.Emit (ec, false); 93 94 IntConstant.Emit () 95 ldc.i4.1 96 end IntConstant.Emit 97 98 add 99 end Binary.Emit () 100 101 stfld 102 end this.EmitAssign (ec, source, false, true) 103 104 Observe two things: 105 1) EmitAssign left a token on the stack. It was the result of f (). 106 2) This token was used by Emit 107 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 111 112 this.EmitAssign (ec, source, true, true) 113 call f 114 dup 115 116 Binary.Emit () 117 this.Emit (ec, false); 118 ldfld y 119 end this.Emit (ec, false); 120 121 IntConstant.Emit () 122 ldc.i4.1 123 end IntConstant.Emit 124 125 add 126 end Binary.Emit () 127 128 dup 129 stloc temp 130 stfld 131 ldloc temp 132 end this.EmitAssign (ec, source, true, true) 133 134 And with it true in Emit 135 136 this.EmitAssign (ec, source, false, true) 137 call f 138 dup 139 140 Binary.Emit () 141 this.Emit (ec, true); 142 ldfld y 143 dup 144 stloc temp 145 end this.Emit (ec, true); 146 147 IntConstant.Emit () 148 ldc.i4.1 149 end IntConstant.Emit 150 151 add 152 end Binary.Emit () 153 154 stfld 155 ldloc temp 156 end this.EmitAssign (ec, source, false, true) 157 158 Note that these two examples are what happens for ++x and x++, respectively. 159 */ 160 } 161 162 /// <summary> 163 /// An Expression to hold a temporary value. 164 /// </summary> 165 /// <remarks> 166 /// The LocalTemporary class is used to hold temporary values of a given 167 /// type to "simulate" the expression semantics. The local variable is 168 /// never captured. 169 /// 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. 173 /// 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. 176 /// 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. 182 /// 183 /// </remarks> 184 public class LocalTemporary : Expression, IMemoryLocation, IAssignMethod { 185 LocalBuilder builder; 186 LocalTemporary(TypeSpec t)187 public LocalTemporary (TypeSpec t) 188 { 189 type = t; 190 eclass = ExprClass.Value; 191 } 192 LocalTemporary(LocalBuilder b, TypeSpec t)193 public LocalTemporary (LocalBuilder b, TypeSpec t) 194 : this (t) 195 { 196 builder = b; 197 } 198 Release(EmitContext ec)199 public void Release (EmitContext ec) 200 { 201 ec.FreeTemporaryLocal (builder, type); 202 builder = null; 203 } 204 ContainsEmitWithAwait()205 public override bool ContainsEmitWithAwait () 206 { 207 return false; 208 } 209 CreateExpressionTree(ResolveContext ec)210 public override Expression CreateExpressionTree (ResolveContext ec) 211 { 212 Arguments args = new Arguments (1); 213 args.Add (new Argument (this)); 214 return CreateExpressionFactoryCall (ec, "Constant", args); 215 } 216 DoResolve(ResolveContext ec)217 protected override Expression DoResolve (ResolveContext ec) 218 { 219 return this; 220 } 221 DoResolveLValue(ResolveContext ec, Expression right_side)222 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) 223 { 224 return this; 225 } 226 Emit(EmitContext ec)227 public override void Emit (EmitContext ec) 228 { 229 if (builder == null) 230 throw new InternalErrorException ("Emit without Store, or after Release"); 231 232 ec.Emit (OpCodes.Ldloc, builder); 233 } 234 235 #region IAssignMethod Members 236 Emit(EmitContext ec, bool leave_copy)237 public void Emit (EmitContext ec, bool leave_copy) 238 { 239 Emit (ec); 240 241 if (leave_copy) 242 Emit (ec); 243 } 244 EmitAssign(EmitContext ec, Expression source, bool leave_copy, bool isCompound)245 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) 246 { 247 if (isCompound) 248 throw new NotImplementedException (); 249 250 source.Emit (ec); 251 252 Store (ec); 253 254 if (leave_copy) 255 Emit (ec); 256 } 257 258 #endregion 259 260 public LocalBuilder Builder { 261 get { return builder; } 262 } 263 Store(EmitContext ec)264 public void Store (EmitContext ec) 265 { 266 if (builder == null) 267 builder = ec.GetTemporaryLocal (type); 268 269 ec.Emit (OpCodes.Stloc, builder); 270 } 271 AddressOf(EmitContext ec, AddressOp mode)272 public void AddressOf (EmitContext ec, AddressOp mode) 273 { 274 if (builder == null) 275 builder = ec.GetTemporaryLocal (type); 276 277 if (builder.LocalType.IsByRef) { 278 // 279 // if is_address, than this is just the address anyways, 280 // so we just return this. 281 // 282 ec.Emit (OpCodes.Ldloc, builder); 283 } else { 284 ec.Emit (OpCodes.Ldloca, builder); 285 } 286 } 287 } 288 289 /// <summary> 290 /// The Assign node takes care of assigning the value of source into 291 /// the expression represented by target. 292 /// </summary> 293 public abstract class Assign : ExpressionStatement { 294 protected Expression target, source; 295 Assign(Expression target, Expression source, Location loc)296 protected Assign (Expression target, Expression source, Location loc) 297 { 298 this.target = target; 299 this.source = source; 300 this.loc = loc; 301 } 302 303 public Expression Target { 304 get { return target; } 305 } 306 307 public Expression Source { 308 get { 309 return source; 310 } 311 } 312 313 public override Location StartLocation { 314 get { 315 return target.StartLocation; 316 } 317 } 318 ContainsEmitWithAwait()319 public override bool ContainsEmitWithAwait () 320 { 321 return target.ContainsEmitWithAwait () || source.ContainsEmitWithAwait (); 322 } 323 CreateExpressionTree(ResolveContext ec)324 public override Expression CreateExpressionTree (ResolveContext ec) 325 { 326 ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator"); 327 return null; 328 } 329 DoResolve(ResolveContext ec)330 protected override Expression DoResolve (ResolveContext ec) 331 { 332 bool ok = true; 333 source = source.Resolve (ec); 334 335 if (source == null) { 336 ok = false; 337 source = ErrorExpression.Instance; 338 } 339 340 target = target.ResolveLValue (ec, source); 341 342 if (target == null || !ok) 343 return null; 344 345 TypeSpec target_type = target.Type; 346 TypeSpec source_type = source.Type; 347 348 eclass = ExprClass.Value; 349 type = target_type; 350 351 if (!(target is IAssignMethod)) { 352 target.Error_ValueAssignment (ec, source); 353 return null; 354 } 355 356 if (target_type != source_type) { 357 Expression resolved = ResolveConversions (ec); 358 359 if (resolved != this) 360 return resolved; 361 } 362 363 return this; 364 } 365 MakeExpression(BuilderContext ctx)366 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx) 367 { 368 var tassign = target as IDynamicAssign; 369 if (tassign == null) 370 throw new InternalErrorException (target.GetType () + " does not support dynamic assignment"); 371 372 var target_object = tassign.MakeAssignExpression (ctx, source); 373 374 // 375 // Some hacking is needed as DLR does not support void type and requires 376 // always have object convertible return type to support caching and chaining 377 // 378 // We do this by introducing an explicit block which returns RHS value when 379 // available or null 380 // 381 if (target_object.NodeType == System.Linq.Expressions.ExpressionType.Block) 382 return target_object; 383 384 System.Linq.Expressions.UnaryExpression source_object; 385 if (ctx.HasSet (BuilderContext.Options.CheckedScope)) { 386 source_object = System.Linq.Expressions.Expression.ConvertChecked (source.MakeExpression (ctx), target_object.Type); 387 } else { 388 source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type); 389 } 390 391 return System.Linq.Expressions.Expression.Assign (target_object, source_object); 392 } 393 ResolveConversions(ResolveContext rc)394 protected virtual Expression ResolveConversions (ResolveContext rc) 395 { 396 var ttype = target.Type; 397 var stackAlloc = source as StackAlloc; 398 if (stackAlloc != null && ttype.Arity == 1 && ttype.GetDefinition () == rc.Module.PredefinedTypes.SpanGeneric.TypeSpec && 399 rc.Module.Compiler.Settings.Version >= LanguageVersion.V_7_2) { 400 401 var etype = ttype.TypeArguments [0]; 402 var stype = ((PointerContainer)source.Type).Element; 403 if (etype == stype && stackAlloc.ResolveSpanConversion (rc, ttype)) { 404 return this; 405 } 406 } 407 408 source = Convert.ImplicitConversionRequired (rc, source, ttype, source.Location); 409 if (source == null) 410 return null; 411 412 return this; 413 } 414 Emit(EmitContext ec, bool is_statement)415 void Emit (EmitContext ec, bool is_statement) 416 { 417 IAssignMethod t = (IAssignMethod) target; 418 t.EmitAssign (ec, source, !is_statement, this is CompoundAssign); 419 } 420 Emit(EmitContext ec)421 public override void Emit (EmitContext ec) 422 { 423 Emit (ec, false); 424 } 425 EmitStatement(EmitContext ec)426 public override void EmitStatement (EmitContext ec) 427 { 428 Emit (ec, true); 429 } 430 FlowAnalysis(FlowAnalysisContext fc)431 public override void FlowAnalysis (FlowAnalysisContext fc) 432 { 433 source.FlowAnalysis (fc); 434 435 if (target is ArrayAccess || target is IndexerExpr) { 436 target.FlowAnalysis (fc); 437 return; 438 } 439 440 var pe = target as PropertyExpr; 441 if (pe != null && !pe.IsAutoPropertyAccess) 442 target.FlowAnalysis (fc); 443 } 444 CloneTo(CloneContext clonectx, Expression t)445 protected override void CloneTo (CloneContext clonectx, Expression t) 446 { 447 Assign _target = (Assign) t; 448 449 _target.target = target.Clone (clonectx); 450 _target.source = source.Clone (clonectx); 451 } 452 Accept(StructuralVisitor visitor)453 public override object Accept (StructuralVisitor visitor) 454 { 455 return visitor.Visit (this); 456 } 457 } 458 459 public class SimpleAssign : Assign 460 { SimpleAssign(Expression target, Expression source)461 public SimpleAssign (Expression target, Expression source) 462 : this (target, source, target.Location) 463 { 464 } 465 SimpleAssign(Expression target, Expression source, Location loc)466 public SimpleAssign (Expression target, Expression source, Location loc) 467 : base (target, source, loc) 468 { 469 } 470 CheckEqualAssign(Expression t)471 bool CheckEqualAssign (Expression t) 472 { 473 if (source is Assign) { 474 Assign a = (Assign) source; 475 if (t.Equals (a.Target)) 476 return true; 477 return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t); 478 } 479 return t.Equals (source); 480 } 481 DoResolve(ResolveContext ec)482 protected override Expression DoResolve (ResolveContext ec) 483 { 484 Expression e = base.DoResolve (ec); 485 if (e == null || e != this) 486 return e; 487 488 if (CheckEqualAssign (target)) 489 ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?"); 490 491 return this; 492 } 493 FlowAnalysis(FlowAnalysisContext fc)494 public override void FlowAnalysis (FlowAnalysisContext fc) 495 { 496 base.FlowAnalysis (fc); 497 498 var vr = target as VariableReference; 499 if (vr != null) { 500 if (vr.VariableInfo != null) 501 fc.SetVariableAssigned (vr.VariableInfo); 502 503 return; 504 } 505 506 var fe = target as FieldExpr; 507 if (fe != null) { 508 fe.SetFieldAssigned (fc); 509 return; 510 } 511 512 var pe = target as PropertyExpr; 513 if (pe != null) { 514 pe.SetBackingFieldAssigned (fc); 515 return; 516 } 517 518 var td = target as TupleDeconstruct; 519 if (td != null) { 520 td.SetGeneratedFieldAssigned (fc); 521 return; 522 } 523 } 524 MarkReachable(Reachability rc)525 public override Reachability MarkReachable (Reachability rc) 526 { 527 return source.MarkReachable (rc); 528 } 529 } 530 531 public class RuntimeExplicitAssign : Assign 532 { RuntimeExplicitAssign(Expression target, Expression source)533 public RuntimeExplicitAssign (Expression target, Expression source) 534 : base (target, source, target.Location) 535 { 536 } 537 ResolveConversions(ResolveContext ec)538 protected override Expression ResolveConversions (ResolveContext ec) 539 { 540 source = EmptyCast.Create (source, target.Type); 541 return this; 542 } 543 } 544 545 // 546 // Compiler generated assign 547 // 548 class CompilerAssign : Assign 549 { CompilerAssign(Expression target, Expression source, Location loc)550 public CompilerAssign (Expression target, Expression source, Location loc) 551 : base (target, source, loc) 552 { 553 if (target.Type != null) { 554 type = target.Type; 555 eclass = ExprClass.Value; 556 } 557 } 558 DoResolve(ResolveContext ec)559 protected override Expression DoResolve (ResolveContext ec) 560 { 561 var expr = base.DoResolve (ec); 562 var vr = target as VariableReference; 563 if (vr != null && vr.VariableInfo != null) 564 vr.VariableInfo.IsEverAssigned = false; 565 566 return expr; 567 } 568 UpdateSource(Expression source)569 public void UpdateSource (Expression source) 570 { 571 base.source = source; 572 } 573 } 574 575 // 576 // Implements fields and events class initializers 577 // 578 public class FieldInitializer : Assign 579 { 580 // 581 // Field initializers are tricky for partial classes. They have to 582 // share same constructor (block) for expression trees resolve but 583 // they have they own resolve scope 584 // 585 sealed class FieldInitializerContext : BlockContext 586 { 587 readonly ExplicitBlock ctor_block; 588 FieldInitializerContext(IMemberContext mc, BlockContext constructorContext)589 public FieldInitializerContext (IMemberContext mc, BlockContext constructorContext) 590 : base (mc, null, constructorContext.ReturnType) 591 { 592 flags |= Options.FieldInitializerScope | Options.ConstructorScope; 593 this.ctor_block = constructorContext.CurrentBlock.Explicit; 594 595 if (ctor_block.IsCompilerGenerated) 596 CurrentBlock = ctor_block; 597 } 598 599 public override ExplicitBlock ConstructorBlock { 600 get { 601 return ctor_block; 602 } 603 } 604 } 605 606 // 607 // Keep resolved value because field initializers have their own rules 608 // 609 ExpressionStatement resolved; 610 FieldBase mc; 611 FieldInitializer(FieldBase mc, Expression expression, Location loc)612 public FieldInitializer (FieldBase mc, Expression expression, Location loc) 613 : base (new FieldExpr (mc.Spec, expression.Location), expression, loc) 614 { 615 this.mc = mc; 616 if (!mc.IsStatic) 617 ((FieldExpr)target).InstanceExpression = new CompilerGeneratedThis (mc.CurrentType, expression.Location); 618 } 619 620 public int AssignmentOffset { get; private set; } 621 622 public FieldBase Field { 623 get { 624 return mc; 625 } 626 } 627 628 public override Location StartLocation { 629 get { 630 return loc; 631 } 632 } 633 DoResolve(ResolveContext rc)634 protected override Expression DoResolve (ResolveContext rc) 635 { 636 // Field initializer can be resolved (fail) many times 637 if (source == null) 638 return null; 639 640 if (resolved == null) { 641 var bc = (BlockContext) rc; 642 var ctx = new FieldInitializerContext (mc, bc); 643 resolved = base.DoResolve (ctx) as ExpressionStatement; 644 AssignmentOffset = ctx.AssignmentInfoOffset - bc.AssignmentInfoOffset; 645 } 646 647 return resolved; 648 } 649 EmitStatement(EmitContext ec)650 public override void EmitStatement (EmitContext ec) 651 { 652 if (resolved == null) 653 return; 654 655 // 656 // Emit sequence symbol info even if we are in compiler generated 657 // block to allow debugging field initializers when constructor is 658 // compiler generated 659 // 660 if (ec.HasSet (BuilderContext.Options.OmitDebugInfo) && ec.HasMethodSymbolBuilder) { 661 using (ec.With (BuilderContext.Options.OmitDebugInfo, false)) { 662 ec.Mark (loc); 663 } 664 } 665 666 if (resolved != this) 667 resolved.EmitStatement (ec); 668 else 669 base.EmitStatement (ec); 670 } 671 FlowAnalysis(FlowAnalysisContext fc)672 public override void FlowAnalysis (FlowAnalysisContext fc) 673 { 674 source.FlowAnalysis (fc); 675 ((FieldExpr) target).SetFieldAssigned (fc); 676 } 677 678 public bool IsDefaultInitializer { 679 get { 680 Constant c = source as Constant; 681 if (c == null) 682 return false; 683 684 FieldExpr fe = (FieldExpr)target; 685 return c.IsDefaultInitializer (fe.Type); 686 } 687 } 688 689 public override bool IsSideEffectFree { 690 get { 691 return source.IsSideEffectFree; 692 } 693 } 694 } 695 696 class PrimaryConstructorAssign : SimpleAssign 697 { 698 readonly Field field; 699 readonly Parameter parameter; 700 PrimaryConstructorAssign(Field field, Parameter parameter)701 public PrimaryConstructorAssign (Field field, Parameter parameter) 702 : base (null, null, parameter.Location) 703 { 704 this.field = field; 705 this.parameter = parameter; 706 } 707 DoResolve(ResolveContext rc)708 protected override Expression DoResolve (ResolveContext rc) 709 { 710 target = new FieldExpr (field, loc); 711 source = rc.CurrentBlock.ParametersBlock.GetParameterInfo (parameter).CreateReferenceExpression (rc, loc); 712 return base.DoResolve (rc); 713 } 714 EmitStatement(EmitContext ec)715 public override void EmitStatement (EmitContext ec) 716 { 717 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { 718 base.EmitStatement (ec); 719 } 720 } 721 } 722 723 // 724 // This class is used for compound assignments. 725 // 726 public class CompoundAssign : Assign 727 { 728 // This is just a hack implemented for arrays only 729 public sealed class TargetExpression : Expression 730 { 731 readonly Expression child; 732 TargetExpression(Expression child)733 public TargetExpression (Expression child) 734 { 735 this.child = child; 736 this.loc = child.Location; 737 } 738 739 public bool RequiresEmitWithAwait { get; set; } 740 ContainsEmitWithAwait()741 public override bool ContainsEmitWithAwait () 742 { 743 return RequiresEmitWithAwait || child.ContainsEmitWithAwait (); 744 } 745 CreateExpressionTree(ResolveContext ec)746 public override Expression CreateExpressionTree (ResolveContext ec) 747 { 748 throw new NotSupportedException ("ET"); 749 } 750 DoResolve(ResolveContext ec)751 protected override Expression DoResolve (ResolveContext ec) 752 { 753 type = child.Type; 754 eclass = ExprClass.Value; 755 return this; 756 } 757 Emit(EmitContext ec)758 public override void Emit (EmitContext ec) 759 { 760 child.Emit (ec); 761 } 762 EmitToField(EmitContext ec)763 public override Expression EmitToField (EmitContext ec) 764 { 765 return child.EmitToField (ec); 766 } 767 } 768 769 // Used for underlying binary operator 770 readonly Binary.Operator op; 771 Expression right; 772 Expression left; 773 CompoundAssign(Binary.Operator op, Expression target, Expression source)774 public CompoundAssign (Binary.Operator op, Expression target, Expression source) 775 : base (target, source, target.Location) 776 { 777 right = source; 778 this.op = op; 779 } 780 CompoundAssign(Binary.Operator op, Expression target, Expression source, Expression left)781 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left) 782 : this (op, target, source) 783 { 784 this.left = left; 785 } 786 787 public Binary.Operator Operator { 788 get { 789 return op; 790 } 791 } 792 DoResolve(ResolveContext ec)793 protected override Expression DoResolve (ResolveContext ec) 794 { 795 right = right.Resolve (ec); 796 if (right == null) 797 return null; 798 799 MemberAccess ma = target as MemberAccess; 800 using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) { 801 target = target.Resolve (ec); 802 } 803 804 if (target == null) 805 return null; 806 807 if (target is MethodGroupExpr){ 808 ec.Report.Error (1656, loc, 809 "Cannot assign to `{0}' because it is a `{1}'", 810 ((MethodGroupExpr)target).Name, target.ExprClassName); 811 return null; 812 } 813 814 var event_expr = target as EventExpr; 815 if (event_expr != null) { 816 source = Convert.ImplicitConversionRequired (ec, right, target.Type, loc); 817 if (source == null) 818 return null; 819 820 Expression rside; 821 if (op == Binary.Operator.Addition) 822 rside = EmptyExpression.EventAddition; 823 else if (op == Binary.Operator.Subtraction) 824 rside = EmptyExpression.EventSubtraction; 825 else 826 rside = null; 827 828 target = target.ResolveLValue (ec, rside); 829 if (target == null) 830 return null; 831 832 eclass = ExprClass.Value; 833 type = event_expr.Operator.ReturnType; 834 return this; 835 } 836 837 // 838 // Only now we can decouple the original source/target 839 // into a tree, to guarantee that we do not have side 840 // effects. 841 // 842 if (left == null) 843 left = new TargetExpression (target); 844 845 source = new Binary (op, left, right, true); 846 847 if (target is DynamicMemberAssignable) { 848 Arguments targs = ((DynamicMemberAssignable) target).Arguments; 849 source = source.Resolve (ec); 850 851 Arguments args = new Arguments (targs.Count + 1); 852 args.AddRange (targs); 853 args.Add (new Argument (source)); 854 855 var binder_flags = CSharpBinderFlags.ValueFromCompoundAssignment; 856 857 // 858 // Compound assignment does target conversion using additional method 859 // call, set checked context as the binary operation can overflow 860 // 861 if (ec.HasSet (ResolveContext.Options.CheckedScope)) 862 binder_flags |= CSharpBinderFlags.CheckedContext; 863 864 if (target is DynamicMemberBinder) { 865 source = new DynamicMemberBinder (ma.Name, binder_flags, args, loc).Resolve (ec); 866 867 // Handles possible event addition/subtraction 868 if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) { 869 args = new Arguments (targs.Count + 1); 870 args.AddRange (targs); 871 args.Add (new Argument (right)); 872 string method_prefix = op == Binary.Operator.Addition ? 873 Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix; 874 875 var invoke = DynamicInvocation.CreateSpecialNameInvoke ( 876 new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec); 877 878 args = new Arguments (targs.Count); 879 args.AddRange (targs); 880 source = new DynamicEventCompoundAssign (ma.Name, args, 881 (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec); 882 } 883 } else { 884 source = new DynamicIndexBinder (binder_flags, args, loc).Resolve (ec); 885 } 886 887 return source; 888 } 889 890 return base.DoResolve (ec); 891 } 892 FlowAnalysis(FlowAnalysisContext fc)893 public override void FlowAnalysis (FlowAnalysisContext fc) 894 { 895 target.FlowAnalysis (fc); 896 source.FlowAnalysis (fc); 897 } 898 ResolveConversions(ResolveContext ec)899 protected override Expression ResolveConversions (ResolveContext ec) 900 { 901 // 902 // LAMESPEC: Under dynamic context no target conversion is happening 903 // This allows more natual dynamic behaviour but breaks compatibility 904 // with static binding 905 // 906 if (target is RuntimeValueExpression) 907 return this; 908 909 TypeSpec target_type = target.Type; 910 911 // 912 // 1. the return type is implicitly convertible to the type of target 913 // 914 if (Convert.ImplicitConversionExists (ec, source, target_type)) { 915 source = Convert.ImplicitConversion (ec, source, target_type, loc); 916 return this; 917 } 918 919 // 920 // Otherwise, if the selected operator is a predefined operator 921 // 922 Binary b = source as Binary; 923 if (b == null) { 924 if (source is ReducedExpression) 925 b = ((ReducedExpression) source).OriginalExpression as Binary; 926 else if (source is ReducedExpression.ReducedConstantExpression) { 927 b = ((ReducedExpression.ReducedConstantExpression) source).OriginalExpression as Binary; 928 } else if (source is Nullable.LiftedBinaryOperator) { 929 var po = ((Nullable.LiftedBinaryOperator) source); 930 if (po.UserOperator == null) 931 b = po.Binary; 932 } else if (source is TypeCast) { 933 b = ((TypeCast) source).Child as Binary; 934 } 935 } 936 937 if (b != null) { 938 // 939 // 2a. the operator is a shift operator 940 // 941 // 2b. the return type is explicitly convertible to the type of x, and 942 // y is implicitly convertible to the type of x 943 // 944 if ((b.Oper & Binary.Operator.ShiftMask) != 0 || 945 Convert.ImplicitConversionExists (ec, right, target_type)) { 946 source = Convert.ExplicitConversion (ec, source, target_type, loc); 947 return this; 948 } 949 } 950 951 if (source.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { 952 Arguments arg = new Arguments (1); 953 arg.Add (new Argument (source)); 954 return new SimpleAssign (target, new DynamicConversion (target_type, CSharpBinderFlags.ConvertExplicit, arg, loc), loc).Resolve (ec); 955 } 956 957 right.Error_ValueCannotBeConverted (ec, target_type, false); 958 return null; 959 } 960 CloneTo(CloneContext clonectx, Expression t)961 protected override void CloneTo (CloneContext clonectx, Expression t) 962 { 963 CompoundAssign ctarget = (CompoundAssign) t; 964 965 ctarget.right = ctarget.source = source.Clone (clonectx); 966 ctarget.target = target.Clone (clonectx); 967 } 968 Accept(StructuralVisitor visitor)969 public override object Accept (StructuralVisitor visitor) 970 { 971 return visitor.Visit (this); 972 } 973 } 974 } 975