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 | // |
---|
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 | // |
---|
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 | |
---|
187 | public LocalTemporary (TypeSpec t) |
---|
188 | { |
---|
189 | type = t; |
---|
190 | eclass = ExprClass.Value; |
---|
191 | } |
---|
192 | |
---|
193 | public LocalTemporary (LocalBuilder b, TypeSpec t) |
---|
194 | : this (t) |
---|
195 | { |
---|
196 | builder = b; |
---|
197 | } |
---|
198 | |
---|
199 | public void Release (EmitContext ec) |
---|
200 | { |
---|
201 | ec.FreeTemporaryLocal (builder, type); |
---|
202 | builder = null; |
---|
203 | } |
---|
204 | |
---|
205 | public override bool ContainsEmitWithAwait () |
---|
206 | { |
---|
207 | return false; |
---|
208 | } |
---|
209 | |
---|
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 | |
---|
217 | protected override Expression DoResolve (ResolveContext ec) |
---|
218 | { |
---|
219 | return this; |
---|
220 | } |
---|
221 | |
---|
222 | public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) |
---|
223 | { |
---|
224 | return this; |
---|
225 | } |
---|
226 | |
---|
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 | |
---|
237 | public void Emit (EmitContext ec, bool leave_copy) |
---|
238 | { |
---|
239 | Emit (ec); |
---|
240 | |
---|
241 | if (leave_copy) |
---|
242 | Emit (ec); |
---|
243 | } |
---|
244 | |
---|
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 | |
---|
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 | |
---|
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 | |
---|
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 | |
---|
319 | public override bool ContainsEmitWithAwait () |
---|
320 | { |
---|
321 | return target.ContainsEmitWithAwait () || source.ContainsEmitWithAwait (); |
---|
322 | } |
---|
323 | |
---|
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 | |
---|
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 | |
---|
366 | #if NET_4_0 || MOBILE_DYNAMIC |
---|
367 | public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx) |
---|
368 | { |
---|
369 | var tassign = target as IDynamicAssign; |
---|
370 | if (tassign == null) |
---|
371 | throw new InternalErrorException (target.GetType () + " does not support dynamic assignment"); |
---|
372 | |
---|
373 | var target_object = tassign.MakeAssignExpression (ctx, source); |
---|
374 | |
---|
375 | // |
---|
376 | // Some hacking is needed as DLR does not support void type and requires |
---|
377 | // always have object convertible return type to support caching and chaining |
---|
378 | // |
---|
379 | // We do this by introducing an explicit block which returns RHS value when |
---|
380 | // available or null |
---|
381 | // |
---|
382 | if (target_object.NodeType == System.Linq.Expressions.ExpressionType.Block) |
---|
383 | return target_object; |
---|
384 | |
---|
385 | System.Linq.Expressions.UnaryExpression source_object; |
---|
386 | if (ctx.HasSet (BuilderContext.Options.CheckedScope)) { |
---|
387 | source_object = System.Linq.Expressions.Expression.ConvertChecked (source.MakeExpression (ctx), target_object.Type); |
---|
388 | } else { |
---|
389 | source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type); |
---|
390 | } |
---|
391 | |
---|
392 | return System.Linq.Expressions.Expression.Assign (target_object, source_object); |
---|
393 | } |
---|
394 | #endif |
---|
395 | protected virtual Expression ResolveConversions (ResolveContext ec) |
---|
396 | { |
---|
397 | source = Convert.ImplicitConversionRequired (ec, source, target.Type, source.Location); |
---|
398 | if (source == null) |
---|
399 | return null; |
---|
400 | |
---|
401 | return this; |
---|
402 | } |
---|
403 | |
---|
404 | void Emit (EmitContext ec, bool is_statement) |
---|
405 | { |
---|
406 | IAssignMethod t = (IAssignMethod) target; |
---|
407 | t.EmitAssign (ec, source, !is_statement, this is CompoundAssign); |
---|
408 | } |
---|
409 | |
---|
410 | public override void Emit (EmitContext ec) |
---|
411 | { |
---|
412 | Emit (ec, false); |
---|
413 | } |
---|
414 | |
---|
415 | public override void EmitStatement (EmitContext ec) |
---|
416 | { |
---|
417 | Emit (ec, true); |
---|
418 | } |
---|
419 | |
---|
420 | public override void FlowAnalysis (FlowAnalysisContext fc) |
---|
421 | { |
---|
422 | source.FlowAnalysis (fc); |
---|
423 | |
---|
424 | if (target is ArrayAccess || target is IndexerExpr || target is PropertyExpr) |
---|
425 | target.FlowAnalysis (fc); |
---|
426 | } |
---|
427 | |
---|
428 | protected override void CloneTo (CloneContext clonectx, Expression t) |
---|
429 | { |
---|
430 | Assign _target = (Assign) t; |
---|
431 | |
---|
432 | _target.target = target.Clone (clonectx); |
---|
433 | _target.source = source.Clone (clonectx); |
---|
434 | } |
---|
435 | |
---|
436 | public override object Accept (StructuralVisitor visitor) |
---|
437 | { |
---|
438 | return visitor.Visit (this); |
---|
439 | } |
---|
440 | } |
---|
441 | |
---|
442 | public class SimpleAssign : Assign |
---|
443 | { |
---|
444 | public SimpleAssign (Expression target, Expression source) |
---|
445 | : this (target, source, target.Location) |
---|
446 | { |
---|
447 | } |
---|
448 | |
---|
449 | public SimpleAssign (Expression target, Expression source, Location loc) |
---|
450 | : base (target, source, loc) |
---|
451 | { |
---|
452 | } |
---|
453 | |
---|
454 | bool CheckEqualAssign (Expression t) |
---|
455 | { |
---|
456 | if (source is Assign) { |
---|
457 | Assign a = (Assign) source; |
---|
458 | if (t.Equals (a.Target)) |
---|
459 | return true; |
---|
460 | return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t); |
---|
461 | } |
---|
462 | return t.Equals (source); |
---|
463 | } |
---|
464 | |
---|
465 | protected override Expression DoResolve (ResolveContext ec) |
---|
466 | { |
---|
467 | Expression e = base.DoResolve (ec); |
---|
468 | if (e == null || e != this) |
---|
469 | return e; |
---|
470 | |
---|
471 | if (CheckEqualAssign (target)) |
---|
472 | ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?"); |
---|
473 | |
---|
474 | return this; |
---|
475 | } |
---|
476 | |
---|
477 | public override object Accept (StructuralVisitor visitor) |
---|
478 | { |
---|
479 | return visitor.Visit (this); |
---|
480 | } |
---|
481 | |
---|
482 | public override void FlowAnalysis (FlowAnalysisContext fc) |
---|
483 | { |
---|
484 | base.FlowAnalysis (fc); |
---|
485 | |
---|
486 | var vr = target as VariableReference; |
---|
487 | if (vr != null) { |
---|
488 | if (vr.VariableInfo != null) |
---|
489 | fc.SetVariableAssigned (vr.VariableInfo); |
---|
490 | |
---|
491 | return; |
---|
492 | } |
---|
493 | |
---|
494 | var fe = target as FieldExpr; |
---|
495 | if (fe != null) { |
---|
496 | fe.SetFieldAssigned (fc); |
---|
497 | return; |
---|
498 | } |
---|
499 | } |
---|
500 | |
---|
501 | public override void MarkReachable (Reachability rc) |
---|
502 | { |
---|
503 | var es = source as ExpressionStatement; |
---|
504 | if (es != null) |
---|
505 | es.MarkReachable (rc); |
---|
506 | } |
---|
507 | } |
---|
508 | |
---|
509 | public class RuntimeExplicitAssign : Assign |
---|
510 | { |
---|
511 | public RuntimeExplicitAssign (Expression target, Expression source) |
---|
512 | : base (target, source, target.Location) |
---|
513 | { |
---|
514 | } |
---|
515 | |
---|
516 | protected override Expression ResolveConversions (ResolveContext ec) |
---|
517 | { |
---|
518 | source = EmptyCast.Create (source, target.Type); |
---|
519 | return this; |
---|
520 | } |
---|
521 | } |
---|
522 | |
---|
523 | // |
---|
524 | // Compiler generated assign |
---|
525 | // |
---|
526 | class CompilerAssign : Assign |
---|
527 | { |
---|
528 | public CompilerAssign (Expression target, Expression source, Location loc) |
---|
529 | : base (target, source, loc) |
---|
530 | { |
---|
531 | if (target.Type != null) { |
---|
532 | type = target.Type; |
---|
533 | eclass = ExprClass.Value; |
---|
534 | } |
---|
535 | } |
---|
536 | |
---|
537 | protected override Expression DoResolve (ResolveContext ec) |
---|
538 | { |
---|
539 | var expr = base.DoResolve (ec); |
---|
540 | var vr = target as VariableReference; |
---|
541 | if (vr != null && vr.VariableInfo != null) |
---|
542 | vr.VariableInfo.IsEverAssigned = false; |
---|
543 | |
---|
544 | return expr; |
---|
545 | } |
---|
546 | |
---|
547 | public void UpdateSource (Expression source) |
---|
548 | { |
---|
549 | base.source = source; |
---|
550 | } |
---|
551 | } |
---|
552 | |
---|
553 | // |
---|
554 | // Implements fields and events class initializers |
---|
555 | // |
---|
556 | public class FieldInitializer : Assign |
---|
557 | { |
---|
558 | // |
---|
559 | // Field initializers are tricky for partial classes. They have to |
---|
560 | // share same constructor (block) for expression trees resolve but |
---|
561 | // they have they own resolve scope |
---|
562 | // |
---|
563 | sealed class FieldInitializerContext : BlockContext |
---|
564 | { |
---|
565 | readonly ExplicitBlock ctor_block; |
---|
566 | |
---|
567 | public FieldInitializerContext (IMemberContext mc, BlockContext constructorContext) |
---|
568 | : base (mc, null, constructorContext.ReturnType) |
---|
569 | { |
---|
570 | flags |= Options.FieldInitializerScope | Options.ConstructorScope; |
---|
571 | this.ctor_block = constructorContext.CurrentBlock.Explicit; |
---|
572 | |
---|
573 | if (ctor_block.IsCompilerGenerated) |
---|
574 | CurrentBlock = ctor_block; |
---|
575 | } |
---|
576 | |
---|
577 | public override ExplicitBlock ConstructorBlock { |
---|
578 | get { |
---|
579 | return ctor_block; |
---|
580 | } |
---|
581 | } |
---|
582 | } |
---|
583 | |
---|
584 | // |
---|
585 | // Keep resolved value because field initializers have their own rules |
---|
586 | // |
---|
587 | ExpressionStatement resolved; |
---|
588 | FieldBase mc; |
---|
589 | |
---|
590 | public FieldInitializer (FieldBase mc, Expression expression, Location loc) |
---|
591 | : base (new FieldExpr (mc.Spec, expression.Location), expression, loc) |
---|
592 | { |
---|
593 | this.mc = mc; |
---|
594 | if (!mc.IsStatic) |
---|
595 | ((FieldExpr)target).InstanceExpression = new CompilerGeneratedThis (mc.CurrentType, expression.Location); |
---|
596 | } |
---|
597 | |
---|
598 | public int AssignmentOffset { get; private set; } |
---|
599 | |
---|
600 | public FieldBase Field { |
---|
601 | get { |
---|
602 | return mc; |
---|
603 | } |
---|
604 | } |
---|
605 | |
---|
606 | public override Location StartLocation { |
---|
607 | get { |
---|
608 | return loc; |
---|
609 | } |
---|
610 | } |
---|
611 | |
---|
612 | protected override Expression DoResolve (ResolveContext rc) |
---|
613 | { |
---|
614 | // Field initializer can be resolved (fail) many times |
---|
615 | if (source == null) |
---|
616 | return null; |
---|
617 | |
---|
618 | if (resolved == null) { |
---|
619 | var bc = (BlockContext) rc; |
---|
620 | var ctx = new FieldInitializerContext (mc, bc); |
---|
621 | resolved = base.DoResolve (ctx) as ExpressionStatement; |
---|
622 | AssignmentOffset = ctx.AssignmentInfoOffset - bc.AssignmentInfoOffset; |
---|
623 | } |
---|
624 | |
---|
625 | return resolved; |
---|
626 | } |
---|
627 | |
---|
628 | public override void EmitStatement (EmitContext ec) |
---|
629 | { |
---|
630 | if (resolved == null) |
---|
631 | return; |
---|
632 | |
---|
633 | // |
---|
634 | // Emit sequence symbol info even if we are in compiler generated |
---|
635 | // block to allow debugging field initializers when constructor is |
---|
636 | // compiler generated |
---|
637 | // |
---|
638 | if (ec.HasSet (BuilderContext.Options.OmitDebugInfo) && ec.HasMethodSymbolBuilder) { |
---|
639 | using (ec.With (BuilderContext.Options.OmitDebugInfo, false)) { |
---|
640 | ec.Mark (loc); |
---|
641 | } |
---|
642 | } |
---|
643 | |
---|
644 | if (resolved != this) |
---|
645 | resolved.EmitStatement (ec); |
---|
646 | else |
---|
647 | base.EmitStatement (ec); |
---|
648 | } |
---|
649 | |
---|
650 | public override void FlowAnalysis (FlowAnalysisContext fc) |
---|
651 | { |
---|
652 | source.FlowAnalysis (fc); |
---|
653 | ((FieldExpr) target).SetFieldAssigned (fc); |
---|
654 | } |
---|
655 | |
---|
656 | public bool IsDefaultInitializer { |
---|
657 | get { |
---|
658 | Constant c = source as Constant; |
---|
659 | if (c == null) |
---|
660 | return false; |
---|
661 | |
---|
662 | FieldExpr fe = (FieldExpr)target; |
---|
663 | return c.IsDefaultInitializer (fe.Type); |
---|
664 | } |
---|
665 | } |
---|
666 | |
---|
667 | public override bool IsSideEffectFree { |
---|
668 | get { |
---|
669 | return source.IsSideEffectFree; |
---|
670 | } |
---|
671 | } |
---|
672 | } |
---|
673 | |
---|
674 | class PrimaryConstructorAssign : SimpleAssign |
---|
675 | { |
---|
676 | readonly Field field; |
---|
677 | readonly Parameter parameter; |
---|
678 | |
---|
679 | public PrimaryConstructorAssign (Field field, Parameter parameter) |
---|
680 | : base (null, null, parameter.Location) |
---|
681 | { |
---|
682 | this.field = field; |
---|
683 | this.parameter = parameter; |
---|
684 | } |
---|
685 | |
---|
686 | protected override Expression DoResolve (ResolveContext rc) |
---|
687 | { |
---|
688 | target = new FieldExpr (field, loc); |
---|
689 | source = rc.CurrentBlock.ParametersBlock.GetParameterInfo (parameter).CreateReferenceExpression (rc, loc); |
---|
690 | return base.DoResolve (rc); |
---|
691 | } |
---|
692 | |
---|
693 | public override void EmitStatement (EmitContext ec) |
---|
694 | { |
---|
695 | using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) { |
---|
696 | base.EmitStatement (ec); |
---|
697 | } |
---|
698 | } |
---|
699 | } |
---|
700 | |
---|
701 | // |
---|
702 | // This class is used for compound assignments. |
---|
703 | // |
---|
704 | public class CompoundAssign : Assign |
---|
705 | { |
---|
706 | // This is just a hack implemented for arrays only |
---|
707 | public sealed class TargetExpression : Expression |
---|
708 | { |
---|
709 | readonly Expression child; |
---|
710 | |
---|
711 | public TargetExpression (Expression child) |
---|
712 | { |
---|
713 | this.child = child; |
---|
714 | this.loc = child.Location; |
---|
715 | } |
---|
716 | |
---|
717 | public override bool ContainsEmitWithAwait () |
---|
718 | { |
---|
719 | return child.ContainsEmitWithAwait (); |
---|
720 | } |
---|
721 | |
---|
722 | public override Expression CreateExpressionTree (ResolveContext ec) |
---|
723 | { |
---|
724 | throw new NotSupportedException ("ET"); |
---|
725 | } |
---|
726 | |
---|
727 | protected override Expression DoResolve (ResolveContext ec) |
---|
728 | { |
---|
729 | type = child.Type; |
---|
730 | eclass = ExprClass.Value; |
---|
731 | return this; |
---|
732 | } |
---|
733 | |
---|
734 | public override void Emit (EmitContext ec) |
---|
735 | { |
---|
736 | child.Emit (ec); |
---|
737 | } |
---|
738 | |
---|
739 | public override Expression EmitToField (EmitContext ec) |
---|
740 | { |
---|
741 | return child.EmitToField (ec); |
---|
742 | } |
---|
743 | } |
---|
744 | |
---|
745 | // Used for underlying binary operator |
---|
746 | readonly Binary.Operator op; |
---|
747 | Expression right; |
---|
748 | Expression left; |
---|
749 | |
---|
750 | public Binary.Operator Op { |
---|
751 | get { |
---|
752 | return op; |
---|
753 | } |
---|
754 | } |
---|
755 | |
---|
756 | public CompoundAssign (Binary.Operator op, Expression target, Expression source) |
---|
757 | : base (target, source, target.Location) |
---|
758 | { |
---|
759 | right = source; |
---|
760 | this.op = op; |
---|
761 | } |
---|
762 | |
---|
763 | public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left) |
---|
764 | : this (op, target, source) |
---|
765 | { |
---|
766 | this.left = left; |
---|
767 | } |
---|
768 | |
---|
769 | public Binary.Operator Operator { |
---|
770 | get { |
---|
771 | return op; |
---|
772 | } |
---|
773 | } |
---|
774 | |
---|
775 | protected override Expression DoResolve (ResolveContext ec) |
---|
776 | { |
---|
777 | right = right.Resolve (ec); |
---|
778 | if (right == null) |
---|
779 | return null; |
---|
780 | |
---|
781 | MemberAccess ma = target as MemberAccess; |
---|
782 | using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) { |
---|
783 | target = target.Resolve (ec); |
---|
784 | } |
---|
785 | |
---|
786 | if (target == null) |
---|
787 | return null; |
---|
788 | |
---|
789 | if (target is MethodGroupExpr){ |
---|
790 | ec.Report.Error (1656, loc, |
---|
791 | "Cannot assign to `{0}' because it is a `{1}'", |
---|
792 | ((MethodGroupExpr)target).Name, target.ExprClassName); |
---|
793 | return null; |
---|
794 | } |
---|
795 | |
---|
796 | var event_expr = target as EventExpr; |
---|
797 | if (event_expr != null) { |
---|
798 | source = Convert.ImplicitConversionRequired (ec, right, target.Type, loc); |
---|
799 | if (source == null) |
---|
800 | return null; |
---|
801 | |
---|
802 | Expression rside; |
---|
803 | if (op == Binary.Operator.Addition) |
---|
804 | rside = EmptyExpression.EventAddition; |
---|
805 | else if (op == Binary.Operator.Subtraction) |
---|
806 | rside = EmptyExpression.EventSubtraction; |
---|
807 | else |
---|
808 | rside = null; |
---|
809 | |
---|
810 | target = target.ResolveLValue (ec, rside); |
---|
811 | if (target == null) |
---|
812 | return null; |
---|
813 | |
---|
814 | eclass = ExprClass.Value; |
---|
815 | type = event_expr.Operator.ReturnType; |
---|
816 | return this; |
---|
817 | } |
---|
818 | |
---|
819 | // |
---|
820 | // Only now we can decouple the original source/target |
---|
821 | // into a tree, to guarantee that we do not have side |
---|
822 | // effects. |
---|
823 | // |
---|
824 | if (left == null) |
---|
825 | left = new TargetExpression (target); |
---|
826 | |
---|
827 | source = new Binary (op, left, right, true); |
---|
828 | |
---|
829 | if (target is DynamicMemberAssignable) { |
---|
830 | Arguments targs = ((DynamicMemberAssignable) target).Arguments; |
---|
831 | source = source.Resolve (ec); |
---|
832 | |
---|
833 | Arguments args = new Arguments (targs.Count + 1); |
---|
834 | args.AddRange (targs); |
---|
835 | args.Add (new Argument (source)); |
---|
836 | |
---|
837 | var binder_flags = CSharpBinderFlags.ValueFromCompoundAssignment; |
---|
838 | |
---|
839 | // |
---|
840 | // Compound assignment does target conversion using additional method |
---|
841 | // call, set checked context as the binary operation can overflow |
---|
842 | // |
---|
843 | if (ec.HasSet (ResolveContext.Options.CheckedScope)) |
---|
844 | binder_flags |= CSharpBinderFlags.CheckedContext; |
---|
845 | |
---|
846 | if (target is DynamicMemberBinder) { |
---|
847 | source = new DynamicMemberBinder (ma.Name, binder_flags, args, loc).Resolve (ec); |
---|
848 | |
---|
849 | // Handles possible event addition/subtraction |
---|
850 | if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) { |
---|
851 | args = new Arguments (targs.Count + 1); |
---|
852 | args.AddRange (targs); |
---|
853 | args.Add (new Argument (right)); |
---|
854 | string method_prefix = op == Binary.Operator.Addition ? |
---|
855 | Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix; |
---|
856 | |
---|
857 | var invoke = DynamicInvocation.CreateSpecialNameInvoke ( |
---|
858 | new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec); |
---|
859 | |
---|
860 | args = new Arguments (targs.Count); |
---|
861 | args.AddRange (targs); |
---|
862 | source = new DynamicEventCompoundAssign (ma.Name, args, |
---|
863 | (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec); |
---|
864 | } |
---|
865 | } else { |
---|
866 | source = new DynamicIndexBinder (binder_flags, args, loc).Resolve (ec); |
---|
867 | } |
---|
868 | |
---|
869 | return source; |
---|
870 | } |
---|
871 | |
---|
872 | return base.DoResolve (ec); |
---|
873 | } |
---|
874 | |
---|
875 | public override void FlowAnalysis (FlowAnalysisContext fc) |
---|
876 | { |
---|
877 | target.FlowAnalysis (fc); |
---|
878 | source.FlowAnalysis (fc); |
---|
879 | } |
---|
880 | |
---|
881 | protected override Expression ResolveConversions (ResolveContext ec) |
---|
882 | { |
---|
883 | // |
---|
884 | // LAMESPEC: Under dynamic context no target conversion is happening |
---|
885 | // This allows more natual dynamic behaviour but breaks compatibility |
---|
886 | // with static binding |
---|
887 | // |
---|
888 | if (target is RuntimeValueExpression) |
---|
889 | return this; |
---|
890 | |
---|
891 | TypeSpec target_type = target.Type; |
---|
892 | |
---|
893 | // |
---|
894 | // 1. the return type is implicitly convertible to the type of target |
---|
895 | // |
---|
896 | if (Convert.ImplicitConversionExists (ec, source, target_type)) { |
---|
897 | source = Convert.ImplicitConversion (ec, source, target_type, loc); |
---|
898 | return this; |
---|
899 | } |
---|
900 | |
---|
901 | // |
---|
902 | // Otherwise, if the selected operator is a predefined operator |
---|
903 | // |
---|
904 | Binary b = source as Binary; |
---|
905 | if (b == null) { |
---|
906 | if (source is ReducedExpression) |
---|
907 | b = ((ReducedExpression) source).OriginalExpression as Binary; |
---|
908 | else if (source is ReducedExpression.ReducedConstantExpression) { |
---|
909 | b = ((ReducedExpression.ReducedConstantExpression) source).OriginalExpression as Binary; |
---|
910 | } else if (source is Nullable.LiftedBinaryOperator) { |
---|
911 | var po = ((Nullable.LiftedBinaryOperator) source); |
---|
912 | if (po.UserOperator == null) |
---|
913 | b = po.Binary; |
---|
914 | } else if (source is TypeCast) { |
---|
915 | b = ((TypeCast) source).Child as Binary; |
---|
916 | } |
---|
917 | } |
---|
918 | |
---|
919 | if (b != null) { |
---|
920 | // |
---|
921 | // 2a. the operator is a shift operator |
---|
922 | // |
---|
923 | // 2b. the return type is explicitly convertible to the type of x, and |
---|
924 | // y is implicitly convertible to the type of x |
---|
925 | // |
---|
926 | if ((b.Oper & Binary.Operator.ShiftMask) != 0 || |
---|
927 | Convert.ImplicitConversionExists (ec, right, target_type)) { |
---|
928 | source = Convert.ExplicitConversion (ec, source, target_type, loc); |
---|
929 | return this; |
---|
930 | } |
---|
931 | } |
---|
932 | |
---|
933 | if (source.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { |
---|
934 | Arguments arg = new Arguments (1); |
---|
935 | arg.Add (new Argument (source)); |
---|
936 | return new SimpleAssign (target, new DynamicConversion (target_type, CSharpBinderFlags.ConvertExplicit, arg, loc), loc).Resolve (ec); |
---|
937 | } |
---|
938 | |
---|
939 | right.Error_ValueCannotBeConverted (ec, target_type, false); |
---|
940 | return null; |
---|
941 | } |
---|
942 | |
---|
943 | protected override void CloneTo (CloneContext clonectx, Expression t) |
---|
944 | { |
---|
945 | CompoundAssign ctarget = (CompoundAssign) t; |
---|
946 | |
---|
947 | ctarget.right = ctarget.source = source.Clone (clonectx); |
---|
948 | ctarget.target = target.Clone (clonectx); |
---|
949 | } |
---|
950 | |
---|
951 | public override object Accept (StructuralVisitor visitor) |
---|
952 | { |
---|
953 | return visitor.Visit (this); |
---|
954 | } |
---|
955 | } |
---|
956 | } |
---|