~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/nrefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/statement.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// statement.cs: Statement representation for the IL tree.
 
3
//
 
4
// Authors:
 
5
//   Miguel de Icaza (miguel@ximian.com)
 
6
//   Martin Baulig (martin@ximian.com)
 
7
//   Marek Safar (marek.safar@gmail.com)
 
8
//
 
9
// Copyright 2001, 2002, 2003 Ximian, Inc.
 
10
// Copyright 2003, 2004 Novell, Inc.
 
11
// Copyright 2011 Xamarin Inc.
 
12
//
 
13
 
 
14
using System;
 
15
using System.Collections.Generic;
 
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
        public abstract class Statement {
 
26
                public Location loc;
 
27
                
 
28
                /// <summary>
 
29
                ///   Resolves the statement, true means that all sub-statements
 
30
                ///   did resolve ok.
 
31
                //  </summary>
 
32
                public virtual bool Resolve (BlockContext bc)
 
33
                {
 
34
                        return true;
 
35
                }
 
36
 
 
37
                /// <summary>
 
38
                ///   We already know that the statement is unreachable, but we still
 
39
                ///   need to resolve it to catch errors.
 
40
                /// </summary>
 
41
                public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
 
42
                {
 
43
                        //
 
44
                        // This conflicts with csc's way of doing this, but IMHO it's
 
45
                        // the right thing to do.
 
46
                        //
 
47
                        // If something is unreachable, we still check whether it's
 
48
                        // correct.  This means that you cannot use unassigned variables
 
49
                        // in unreachable code, for instance.
 
50
                        //
 
51
 
 
52
                        bool unreachable = false;
 
53
                        if (warn && !ec.UnreachableReported) {
 
54
                                ec.UnreachableReported = true;
 
55
                                unreachable = true;
 
56
                                ec.Report.Warning (162, 2, loc, "Unreachable code detected");
 
57
                        }
 
58
 
 
59
                        ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
 
60
                        bool ok = Resolve (ec);
 
61
                        ec.KillFlowBranching ();
 
62
 
 
63
                        if (unreachable) {
 
64
                                ec.UnreachableReported = false;
 
65
                        }
 
66
 
 
67
                        return ok;
 
68
                }
 
69
                                
 
70
                /// <summary>
 
71
                ///   Return value indicates whether all code paths emitted return.
 
72
                /// </summary>
 
73
                protected abstract void DoEmit (EmitContext ec);
 
74
 
 
75
                public virtual void Emit (EmitContext ec)
 
76
                {
 
77
                        ec.Mark (loc);
 
78
                        DoEmit (ec);
 
79
 
 
80
                        if (ec.StatementEpilogue != null) {
 
81
                                ec.EmitEpilogue ();
 
82
                        }
 
83
                }
 
84
 
 
85
                //
 
86
                // This routine must be overrided in derived classes and make copies
 
87
                // of all the data that might be modified if resolved
 
88
                // 
 
89
                protected abstract void CloneTo (CloneContext clonectx, Statement target);
 
90
 
 
91
                public Statement Clone (CloneContext clonectx)
 
92
                {
 
93
                        Statement s = (Statement) this.MemberwiseClone ();
 
94
                        CloneTo (clonectx, s);
 
95
                        return s;
 
96
                }
 
97
 
 
98
                public virtual Expression CreateExpressionTree (ResolveContext ec)
 
99
                {
 
100
                        ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
 
101
                        return null;
 
102
                }
 
103
                
 
104
                public virtual object Accept (StructuralVisitor visitor)
 
105
                {
 
106
                        return visitor.Visit (this);
 
107
                }
 
108
        }
 
109
 
 
110
        public sealed class EmptyStatement : Statement
 
111
        {
 
112
                public EmptyStatement (Location loc)
 
113
                {
 
114
                        this.loc = loc;
 
115
                }
 
116
                
 
117
                public override bool Resolve (BlockContext ec)
 
118
                {
 
119
                        return true;
 
120
                }
 
121
 
 
122
                public override bool ResolveUnreachable (BlockContext ec, bool warn)
 
123
                {
 
124
                        return true;
 
125
                }
 
126
 
 
127
                public override void Emit (EmitContext ec)
 
128
                {
 
129
                }
 
130
 
 
131
                protected override void DoEmit (EmitContext ec)
 
132
                {
 
133
                        throw new NotSupportedException ();
 
134
                }
 
135
 
 
136
                protected override void CloneTo (CloneContext clonectx, Statement target)
 
137
                {
 
138
                        // nothing needed.
 
139
                }
 
140
                
 
141
                public override object Accept (StructuralVisitor visitor)
 
142
                {
 
143
                        return visitor.Visit (this);
 
144
                }
 
145
        }
 
146
        
 
147
        public class If : Statement {
 
148
                Expression expr;
 
149
                public Statement TrueStatement;
 
150
                public Statement FalseStatement;
 
151
 
 
152
                bool is_true_ret;
 
153
 
 
154
                public If (Expression bool_expr, Statement true_statement, Location l)
 
155
                        : this (bool_expr, true_statement, null, l)
 
156
                {
 
157
                }
 
158
 
 
159
                public If (Expression bool_expr,
 
160
                           Statement true_statement,
 
161
                           Statement false_statement,
 
162
                           Location l)
 
163
                {
 
164
                        this.expr = bool_expr;
 
165
                        TrueStatement = true_statement;
 
166
                        FalseStatement = false_statement;
 
167
                        loc = l;
 
168
                }
 
169
 
 
170
                public Expression Expr {
 
171
                        get {
 
172
                                return this.expr;
 
173
                        }
 
174
                }
 
175
                
 
176
                public override bool Resolve (BlockContext ec)
 
177
                {
 
178
                        bool ok = true;
 
179
 
 
180
                        expr = expr.Resolve (ec);
 
181
                        if (expr == null) {
 
182
                                ok = false;
 
183
                        } else {
 
184
                                //
 
185
                                // Dead code elimination
 
186
                                //
 
187
                                if (expr is Constant) {
 
188
                                        bool take = !((Constant) expr).IsDefaultValue;
 
189
 
 
190
                                        if (take) {
 
191
                                                if (!TrueStatement.Resolve (ec))
 
192
                                                        return false;
 
193
 
 
194
                                                if ((FalseStatement != null) &&
 
195
                                                        !FalseStatement.ResolveUnreachable (ec, true))
 
196
                                                        return false;
 
197
                                                FalseStatement = null;
 
198
                                        } else {
 
199
                                                if (!TrueStatement.ResolveUnreachable (ec, true))
 
200
                                                        return false;
 
201
                                                TrueStatement = null;
 
202
 
 
203
                                                if ((FalseStatement != null) &&
 
204
                                                        !FalseStatement.Resolve (ec))
 
205
                                                        return false;
 
206
                                        }
 
207
 
 
208
                                        return true;
 
209
                                }
 
210
                        }
 
211
 
 
212
                        ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
 
213
                        
 
214
                        ok &= TrueStatement.Resolve (ec);
 
215
 
 
216
                        is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
 
217
 
 
218
                        ec.CurrentBranching.CreateSibling ();
 
219
 
 
220
                        if (FalseStatement != null)
 
221
                                ok &= FalseStatement.Resolve (ec);
 
222
                                        
 
223
                        ec.EndFlowBranching ();
 
224
 
 
225
                        return ok;
 
226
                }
 
227
                
 
228
                protected override void DoEmit (EmitContext ec)
 
229
                {
 
230
                        Label false_target = ec.DefineLabel ();
 
231
                        Label end;
 
232
 
 
233
                        //
 
234
                        // If we're a boolean constant, Resolve() already
 
235
                        // eliminated dead code for us.
 
236
                        //
 
237
                        Constant c = expr as Constant;
 
238
                        if (c != null){
 
239
                                c.EmitSideEffect (ec);
 
240
 
 
241
                                if (!c.IsDefaultValue)
 
242
                                        TrueStatement.Emit (ec);
 
243
                                else if (FalseStatement != null)
 
244
                                        FalseStatement.Emit (ec);
 
245
 
 
246
                                return;
 
247
                        }                       
 
248
                        
 
249
                        expr.EmitBranchable (ec, false_target, false);
 
250
                        
 
251
                        TrueStatement.Emit (ec);
 
252
 
 
253
                        if (FalseStatement != null){
 
254
                                bool branch_emitted = false;
 
255
                                
 
256
                                end = ec.DefineLabel ();
 
257
                                if (!is_true_ret){
 
258
                                        ec.Emit (OpCodes.Br, end);
 
259
                                        branch_emitted = true;
 
260
                                }
 
261
 
 
262
                                ec.MarkLabel (false_target);
 
263
                                FalseStatement.Emit (ec);
 
264
 
 
265
                                if (branch_emitted)
 
266
                                        ec.MarkLabel (end);
 
267
                        } else {
 
268
                                ec.MarkLabel (false_target);
 
269
                        }
 
270
                }
 
271
 
 
272
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
273
                {
 
274
                        If target = (If) t;
 
275
 
 
276
                        target.expr = expr.Clone (clonectx);
 
277
                        target.TrueStatement = TrueStatement.Clone (clonectx);
 
278
                        if (FalseStatement != null)
 
279
                                target.FalseStatement = FalseStatement.Clone (clonectx);
 
280
                }
 
281
                
 
282
                public override object Accept (StructuralVisitor visitor)
 
283
                {
 
284
                        return visitor.Visit (this);
 
285
                }
 
286
        }
 
287
 
 
288
        public class Do : Statement {
 
289
                public Expression expr;
 
290
                public Statement  EmbeddedStatement;
 
291
 
 
292
                public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
 
293
                {
 
294
                        expr = bool_expr;
 
295
                        EmbeddedStatement = statement;
 
296
                        loc = doLocation;
 
297
                        WhileLocation = whileLocation;
 
298
                }
 
299
 
 
300
                public Location WhileLocation {
 
301
                        get; private set;
 
302
                }
 
303
 
 
304
                public override bool Resolve (BlockContext ec)
 
305
                {
 
306
                        bool ok = true;
 
307
 
 
308
                        ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
 
309
 
 
310
                        bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
 
311
 
 
312
                        ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
 
313
                        if (!EmbeddedStatement.Resolve (ec))
 
314
                                ok = false;
 
315
                        ec.EndFlowBranching ();
 
316
 
 
317
                        if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
 
318
                                ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
 
319
 
 
320
                        expr = expr.Resolve (ec);
 
321
                        if (expr == null)
 
322
                                ok = false;
 
323
                        else if (expr is Constant){
 
324
                                bool infinite = !((Constant) expr).IsDefaultValue;
 
325
                                if (infinite)
 
326
                                        ec.CurrentBranching.CurrentUsageVector.Goto ();
 
327
                        }
 
328
 
 
329
                        ec.EndFlowBranching ();
 
330
 
 
331
                        return ok;
 
332
                }
 
333
                
 
334
                protected override void DoEmit (EmitContext ec)
 
335
                {
 
336
                        Label loop = ec.DefineLabel ();
 
337
                        Label old_begin = ec.LoopBegin;
 
338
                        Label old_end = ec.LoopEnd;
 
339
                        
 
340
                        ec.LoopBegin = ec.DefineLabel ();
 
341
                        ec.LoopEnd = ec.DefineLabel ();
 
342
                                
 
343
                        ec.MarkLabel (loop);
 
344
                        EmbeddedStatement.Emit (ec);
 
345
                        ec.MarkLabel (ec.LoopBegin);
 
346
 
 
347
                        // Mark start of while condition
 
348
                        ec.Mark (WhileLocation);
 
349
 
 
350
                        //
 
351
                        // Dead code elimination
 
352
                        //
 
353
                        if (expr is Constant) {
 
354
                                bool res = !((Constant) expr).IsDefaultValue;
 
355
 
 
356
                                expr.EmitSideEffect (ec);
 
357
                                if (res)
 
358
                                        ec.Emit (OpCodes.Br, loop);
 
359
                        } else {
 
360
                                expr.EmitBranchable (ec, loop, true);
 
361
                        }
 
362
                        
 
363
                        ec.MarkLabel (ec.LoopEnd);
 
364
 
 
365
                        ec.LoopBegin = old_begin;
 
366
                        ec.LoopEnd = old_end;
 
367
                }
 
368
 
 
369
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
370
                {
 
371
                        Do target = (Do) t;
 
372
 
 
373
                        target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
 
374
                        target.expr = expr.Clone (clonectx);
 
375
                }
 
376
                
 
377
                public override object Accept (StructuralVisitor visitor)
 
378
                {
 
379
                        return visitor.Visit (this);
 
380
                }
 
381
        }
 
382
 
 
383
        public class While : Statement {
 
384
                public Expression expr;
 
385
                public Statement Statement;
 
386
                bool infinite, empty;
 
387
 
 
388
                public While (BooleanExpression bool_expr, Statement statement, Location l)
 
389
                {
 
390
                        this.expr = bool_expr;
 
391
                        Statement = statement;
 
392
                        loc = l;
 
393
                }
 
394
 
 
395
                public override bool Resolve (BlockContext ec)
 
396
                {
 
397
                        bool ok = true;
 
398
 
 
399
                        expr = expr.Resolve (ec);
 
400
                        if (expr == null)
 
401
                                ok = false;
 
402
 
 
403
                        //
 
404
                        // Inform whether we are infinite or not
 
405
                        //
 
406
                        if (expr is Constant){
 
407
                                bool value = !((Constant) expr).IsDefaultValue;
 
408
 
 
409
                                if (value == false){
 
410
                                        if (!Statement.ResolveUnreachable (ec, true))
 
411
                                                return false;
 
412
                                        empty = true;
 
413
                                        return true;
 
414
                                } else
 
415
                                        infinite = true;
 
416
                        }
 
417
 
 
418
                        ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
 
419
                        if (!infinite)
 
420
                                ec.CurrentBranching.CreateSibling ();
 
421
 
 
422
                        ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
 
423
                        if (!Statement.Resolve (ec))
 
424
                                ok = false;
 
425
                        ec.EndFlowBranching ();
 
426
 
 
427
                        // There's no direct control flow from the end of the embedded statement to the end of the loop
 
428
                        ec.CurrentBranching.CurrentUsageVector.Goto ();
 
429
 
 
430
                        ec.EndFlowBranching ();
 
431
 
 
432
                        return ok;
 
433
                }
 
434
                
 
435
                protected override void DoEmit (EmitContext ec)
 
436
                {
 
437
                        if (empty) {
 
438
                                expr.EmitSideEffect (ec);
 
439
                                return;
 
440
                        }
 
441
 
 
442
                        Label old_begin = ec.LoopBegin;
 
443
                        Label old_end = ec.LoopEnd;
 
444
                        
 
445
                        ec.LoopBegin = ec.DefineLabel ();
 
446
                        ec.LoopEnd = ec.DefineLabel ();
 
447
 
 
448
                        //
 
449
                        // Inform whether we are infinite or not
 
450
                        //
 
451
                        if (expr is Constant) {
 
452
                                // expr is 'true', since the 'empty' case above handles the 'false' case
 
453
                                ec.MarkLabel (ec.LoopBegin);
 
454
 
 
455
                                if (ec.EmitAccurateDebugInfo)
 
456
                                        ec.Emit (OpCodes.Nop);
 
457
 
 
458
                                expr.EmitSideEffect (ec);
 
459
                                Statement.Emit (ec);
 
460
                                ec.Emit (OpCodes.Br, ec.LoopBegin);
 
461
                                        
 
462
                                //
 
463
                                // Inform that we are infinite (ie, `we return'), only
 
464
                                // if we do not `break' inside the code.
 
465
                                //
 
466
                                ec.MarkLabel (ec.LoopEnd);
 
467
                        } else {
 
468
                                Label while_loop = ec.DefineLabel ();
 
469
 
 
470
                                ec.Emit (OpCodes.Br, ec.LoopBegin);
 
471
                                ec.MarkLabel (while_loop);
 
472
 
 
473
                                Statement.Emit (ec);
 
474
                        
 
475
                                ec.MarkLabel (ec.LoopBegin);
 
476
 
 
477
                                ec.Mark (loc);
 
478
                                expr.EmitBranchable (ec, while_loop, true);
 
479
                                
 
480
                                ec.MarkLabel (ec.LoopEnd);
 
481
                        }       
 
482
 
 
483
                        ec.LoopBegin = old_begin;
 
484
                        ec.LoopEnd = old_end;
 
485
                }
 
486
 
 
487
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
488
                {
 
489
                        While target = (While) t;
 
490
 
 
491
                        target.expr = expr.Clone (clonectx);
 
492
                        target.Statement = Statement.Clone (clonectx);
 
493
                }
 
494
                
 
495
                public override object Accept (StructuralVisitor visitor)
 
496
                {
 
497
                        return visitor.Visit (this);
 
498
                }
 
499
        }
 
500
 
 
501
        public class For : Statement
 
502
        {
 
503
                bool infinite, empty;
 
504
                
 
505
                public For (Location l)
 
506
                {
 
507
                        loc = l;
 
508
                }
 
509
 
 
510
                public Statement Initializer {
 
511
                        get; set;
 
512
                }
 
513
 
 
514
                public Expression Condition {
 
515
                        get; set;
 
516
                }
 
517
 
 
518
                public Statement Iterator {
 
519
                        get; set;
 
520
                }
 
521
 
 
522
                public Statement Statement {
 
523
                        get; set;
 
524
                }
 
525
 
 
526
                public override bool Resolve (BlockContext ec)
 
527
                {
 
528
                        bool ok = true;
 
529
 
 
530
                        if (Initializer != null) {
 
531
                                if (!Initializer.Resolve (ec))
 
532
                                        ok = false;
 
533
                        }
 
534
 
 
535
                        if (Condition != null) {
 
536
                                Condition = Condition.Resolve (ec);
 
537
                                if (Condition == null)
 
538
                                        ok = false;
 
539
                                else if (Condition is Constant) {
 
540
                                        bool value = !((Constant) Condition).IsDefaultValue;
 
541
 
 
542
                                        if (value == false){
 
543
                                                if (!Statement.ResolveUnreachable (ec, true))
 
544
                                                        return false;
 
545
                                                if ((Iterator != null) &&
 
546
                                                        !Iterator.ResolveUnreachable (ec, false))
 
547
                                                        return false;
 
548
                                                empty = true;
 
549
                                                return true;
 
550
                                        } else
 
551
                                                infinite = true;
 
552
                                }
 
553
                        } else
 
554
                                infinite = true;
 
555
 
 
556
                        ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
 
557
                        if (!infinite)
 
558
                                ec.CurrentBranching.CreateSibling ();
 
559
 
 
560
                        bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
 
561
 
 
562
                        ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
 
563
                        if (!Statement.Resolve (ec))
 
564
                                ok = false;
 
565
                        ec.EndFlowBranching ();
 
566
 
 
567
                        if (Iterator != null){
 
568
                                if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
 
569
                                        if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
 
570
                                                ok = false;
 
571
                                } else {
 
572
                                        if (!Iterator.Resolve (ec))
 
573
                                                ok = false;
 
574
                                }
 
575
                        }
 
576
 
 
577
                        // There's no direct control flow from the end of the embedded statement to the end of the loop
 
578
                        ec.CurrentBranching.CurrentUsageVector.Goto ();
 
579
 
 
580
                        ec.EndFlowBranching ();
 
581
 
 
582
                        return ok;
 
583
                }
 
584
 
 
585
                protected override void DoEmit (EmitContext ec)
 
586
                {
 
587
                        if (Initializer != null)
 
588
                                Initializer.Emit (ec);
 
589
 
 
590
                        if (empty) {
 
591
                                Condition.EmitSideEffect (ec);
 
592
                                return;
 
593
                        }
 
594
 
 
595
                        Label old_begin = ec.LoopBegin;
 
596
                        Label old_end = ec.LoopEnd;
 
597
                        Label loop = ec.DefineLabel ();
 
598
                        Label test = ec.DefineLabel ();
 
599
 
 
600
                        ec.LoopBegin = ec.DefineLabel ();
 
601
                        ec.LoopEnd = ec.DefineLabel ();
 
602
 
 
603
                        ec.Emit (OpCodes.Br, test);
 
604
                        ec.MarkLabel (loop);
 
605
                        Statement.Emit (ec);
 
606
 
 
607
                        ec.MarkLabel (ec.LoopBegin);
 
608
                        Iterator.Emit (ec);
 
609
 
 
610
                        ec.MarkLabel (test);
 
611
                        //
 
612
                        // If test is null, there is no test, and we are just
 
613
                        // an infinite loop
 
614
                        //
 
615
                        if (Condition != null) {
 
616
                                ec.Mark (Condition.Location);
 
617
 
 
618
                                //
 
619
                                // The Resolve code already catches the case for
 
620
                                // Test == Constant (false) so we know that
 
621
                                // this is true
 
622
                                //
 
623
                                if (Condition is Constant) {
 
624
                                        Condition.EmitSideEffect (ec);
 
625
                                        ec.Emit (OpCodes.Br, loop);
 
626
                                } else {
 
627
                                        Condition.EmitBranchable (ec, loop, true);
 
628
                                }
 
629
                                
 
630
                        } else
 
631
                                ec.Emit (OpCodes.Br, loop);
 
632
                        ec.MarkLabel (ec.LoopEnd);
 
633
 
 
634
                        ec.LoopBegin = old_begin;
 
635
                        ec.LoopEnd = old_end;
 
636
                }
 
637
 
 
638
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
639
                {
 
640
                        For target = (For) t;
 
641
 
 
642
                        if (Initializer != null)
 
643
                                target.Initializer = Initializer.Clone (clonectx);
 
644
                        if (Condition != null)
 
645
                                target.Condition = Condition.Clone (clonectx);
 
646
                        if (Iterator != null)
 
647
                                target.Iterator = Iterator.Clone (clonectx);
 
648
                        target.Statement = Statement.Clone (clonectx);
 
649
                }
 
650
 
 
651
                public override object Accept (StructuralVisitor visitor)
 
652
                {
 
653
                        return visitor.Visit (this);
 
654
                }
 
655
        }
 
656
        
 
657
        public class StatementExpression : Statement
 
658
        {
 
659
                ExpressionStatement expr;
 
660
                
 
661
                public StatementExpression (ExpressionStatement expr)
 
662
                {
 
663
                        this.expr = expr;
 
664
                        loc = expr.StartLocation;
 
665
                }
 
666
 
 
667
                public StatementExpression (ExpressionStatement expr, Location loc)
 
668
                {
 
669
                        this.expr = expr;
 
670
                        this.loc = loc;
 
671
                }
 
672
 
 
673
                public ExpressionStatement Expr {
 
674
                        get {
 
675
                                return this.expr;
 
676
                        }
 
677
                }
 
678
                
 
679
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
680
                {
 
681
                        StatementExpression target = (StatementExpression) t;
 
682
                        target.expr = (ExpressionStatement) expr.Clone (clonectx);
 
683
                }
 
684
                
 
685
                protected override void DoEmit (EmitContext ec)
 
686
                {
 
687
                        expr.EmitStatement (ec);
 
688
                }
 
689
 
 
690
                public override bool Resolve (BlockContext ec)
 
691
                {
 
692
                        expr = expr.ResolveStatement (ec);
 
693
                        return expr != null;
 
694
                }
 
695
                
 
696
                public override object Accept (StructuralVisitor visitor)
 
697
                {
 
698
                        return visitor.Visit (this);
 
699
                }
 
700
        }
 
701
 
 
702
        public class StatementErrorExpression : Statement
 
703
        {
 
704
                readonly Expression expr;
 
705
 
 
706
                public StatementErrorExpression (Expression expr)
 
707
                {
 
708
                        this.expr = expr;
 
709
                }
 
710
 
 
711
                public Expression Expr {
 
712
                        get {
 
713
                                return expr;
 
714
                        }
 
715
                }
 
716
 
 
717
                protected override void DoEmit (EmitContext ec)
 
718
                {
 
719
                        throw new NotSupportedException ();
 
720
                }
 
721
 
 
722
                protected override void CloneTo (CloneContext clonectx, Statement target)
 
723
                {
 
724
                        throw new NotImplementedException ();
 
725
                }
 
726
                
 
727
                public override object Accept (StructuralVisitor visitor)
 
728
                {
 
729
                        return visitor.Visit (this);
 
730
                }
 
731
        }
 
732
 
 
733
        //
 
734
        // Simple version of statement list not requiring a block
 
735
        //
 
736
        public class StatementList : Statement
 
737
        {
 
738
                List<Statement> statements;
 
739
 
 
740
                public StatementList (Statement first, Statement second)
 
741
                {
 
742
                        statements = new List<Statement> () { first, second };
 
743
                }
 
744
 
 
745
                #region Properties
 
746
                public IList<Statement> Statements {
 
747
                        get {
 
748
                                return statements;
 
749
                        }
 
750
                }
 
751
                #endregion
 
752
 
 
753
                public void Add (Statement statement)
 
754
                {
 
755
                        statements.Add (statement);
 
756
                }
 
757
 
 
758
                public override bool Resolve (BlockContext ec)
 
759
                {
 
760
                        foreach (var s in statements)
 
761
                                s.Resolve (ec);
 
762
 
 
763
                        return true;
 
764
                }
 
765
 
 
766
                protected override void DoEmit (EmitContext ec)
 
767
                {
 
768
                        foreach (var s in statements)
 
769
                                s.Emit (ec);
 
770
                }
 
771
 
 
772
                protected override void CloneTo (CloneContext clonectx, Statement target)
 
773
                {
 
774
                        StatementList t = (StatementList) target;
 
775
 
 
776
                        t.statements = new List<Statement> (statements.Count);
 
777
                        foreach (Statement s in statements)
 
778
                                t.statements.Add (s.Clone (clonectx));
 
779
                }
 
780
                
 
781
                public override object Accept (StructuralVisitor visitor)
 
782
                {
 
783
                        return visitor.Visit (this);
 
784
                }
 
785
        }
 
786
 
 
787
        // A 'return' or a 'yield break'
 
788
        public abstract class ExitStatement : Statement
 
789
        {
 
790
                protected bool unwind_protect;
 
791
                protected abstract bool DoResolve (BlockContext ec);
 
792
 
 
793
                public virtual void Error_FinallyClause (Report Report)
 
794
                {
 
795
                        Report.Error (157, loc, "Control cannot leave the body of a finally clause");
 
796
                }
 
797
 
 
798
                public sealed override bool Resolve (BlockContext ec)
 
799
                {
 
800
                        var res = DoResolve (ec);
 
801
                        unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
 
802
                        ec.CurrentBranching.CurrentUsageVector.Goto ();
 
803
                        return res;
 
804
                }
 
805
        }
 
806
 
 
807
        /// <summary>
 
808
        ///   Implements the return statement
 
809
        /// </summary>
 
810
        public class Return : ExitStatement
 
811
        {
 
812
                Expression expr;
 
813
 
 
814
                public Return (Expression expr, Location l)
 
815
                {
 
816
                        this.expr = expr;
 
817
                        loc = l;
 
818
                }
 
819
 
 
820
                #region Properties
 
821
 
 
822
                public Expression Expr {
 
823
                        get {
 
824
                                return expr;
 
825
                        }
 
826
                        protected set {
 
827
                                expr = value;
 
828
                        }
 
829
                }
 
830
 
 
831
                #endregion
 
832
 
 
833
                protected override bool DoResolve (BlockContext ec)
 
834
                {
 
835
                        if (expr == null) {
 
836
                                if (ec.ReturnType.Kind == MemberKind.Void)
 
837
                                        return true;
 
838
 
 
839
                                //
 
840
                                // Return must not be followed by an expression when
 
841
                                // the method return type is Task
 
842
                                //
 
843
                                if (ec.CurrentAnonymousMethod is AsyncInitializer) {
 
844
                                        var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
 
845
                                        if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
 
846
                                                //
 
847
                                                // Extra trick not to emit ret/leave inside awaiter body
 
848
                                                //
 
849
                                                expr = EmptyExpression.Null;
 
850
                                                return true;
 
851
                                        }
 
852
                                }
 
853
 
 
854
                                if (ec.CurrentIterator != null) {
 
855
                                        Error_ReturnFromIterator (ec);
 
856
                                } else if (ec.ReturnType != InternalType.ErrorType) {
 
857
                                        ec.Report.Error (126, loc,
 
858
                                                "An object of a type convertible to `{0}' is required for the return statement",
 
859
                                                ec.ReturnType.GetSignatureForError ());
 
860
                                }
 
861
 
 
862
                                return false;
 
863
                        }
 
864
 
 
865
                        expr = expr.Resolve (ec);
 
866
                        TypeSpec block_return_type = ec.ReturnType;
 
867
 
 
868
                        AnonymousExpression am = ec.CurrentAnonymousMethod;
 
869
                        if (am == null) {
 
870
                                if (block_return_type.Kind == MemberKind.Void) {
 
871
                                        ec.Report.Error (127, loc,
 
872
                                                "`{0}': A return keyword must not be followed by any expression when method returns void",
 
873
                                                ec.GetSignatureForError ());
 
874
 
 
875
                                        return false;
 
876
                                }
 
877
                        } else {
 
878
                                if (am.IsIterator) {
 
879
                                        Error_ReturnFromIterator (ec);
 
880
                                        return false;
 
881
                                }
 
882
 
 
883
                                var async_block = am as AsyncInitializer;
 
884
                                if (async_block != null) {
 
885
                                        if (expr != null) {
 
886
                                                var storey = (AsyncTaskStorey) am.Storey;
 
887
                                                var async_type = storey.ReturnType;
 
888
 
 
889
                                                if (async_type == null && async_block.ReturnTypeInference != null) {
 
890
                                                        async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
 
891
                                                        return true;
 
892
                                                }
 
893
 
 
894
                                                if (async_type.Kind == MemberKind.Void) {
 
895
                                                        ec.Report.Error (127, loc,
 
896
                                                                "`{0}': A return keyword must not be followed by any expression when method returns void",
 
897
                                                                ec.GetSignatureForError ());
 
898
 
 
899
                                                        return false;
 
900
                                                }
 
901
 
 
902
                                                if (!async_type.IsGenericTask) {
 
903
                                                        if (this is ContextualReturn)
 
904
                                                                return true;
 
905
 
 
906
                                                        ec.Report.Error (1997, loc,
 
907
                                                                "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
 
908
                                                                ec.GetSignatureForError ());
 
909
                                                        return false;
 
910
                                                }
 
911
 
 
912
                                                //
 
913
                                                // The return type is actually Task<T> type argument
 
914
                                                //
 
915
                                                if (expr.Type == async_type) {
 
916
                                                        ec.Report.Error (4016, loc,
 
917
                                                                "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
 
918
                                                                ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
 
919
                                                } else {
 
920
                                                        block_return_type = async_type.TypeArguments[0];
 
921
                                                }
 
922
                                        }
 
923
                                } else {
 
924
                                        // Same error code as .NET but better error message
 
925
                                        if (block_return_type.Kind == MemberKind.Void) {
 
926
                                                ec.Report.Error (127, loc,
 
927
                                                        "`{0}': A return keyword must not be followed by any expression when delegate returns void",
 
928
                                                        am.GetSignatureForError ());
 
929
 
 
930
                                                return false;
 
931
                                        }
 
932
 
 
933
                                        var l = am as AnonymousMethodBody;
 
934
                                        if (l != null && expr != null) {
 
935
                                                if (l.ReturnTypeInference != null) {
 
936
                                                        l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
 
937
                                                        return true;
 
938
                                                }
 
939
 
 
940
                                                //
 
941
                                                // Try to optimize simple lambda. Only when optimizations are enabled not to cause
 
942
                                                // unexpected debugging experience
 
943
                                                //
 
944
                                                if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
 
945
                                                        l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
 
946
                                                }
 
947
                                        }
 
948
                                }
 
949
                        }
 
950
 
 
951
                        if (expr == null)
 
952
                                return false;
 
953
 
 
954
                        if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
 
955
                                expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
 
956
 
 
957
                                if (expr == null) {
 
958
                                        if (am != null && block_return_type == ec.ReturnType) {
 
959
                                                ec.Report.Error (1662, loc,
 
960
                                                        "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
 
961
                                                        am.ContainerType, am.GetSignatureForError ());
 
962
                                        }
 
963
                                        return false;
 
964
                                }
 
965
                        }
 
966
 
 
967
                        return true;                    
 
968
                }
 
969
                
 
970
                protected override void DoEmit (EmitContext ec)
 
971
                {
 
972
                        if (expr != null) {
 
973
                                expr.Emit (ec);
 
974
 
 
975
                                var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
 
976
                                if (async_body != null) {
 
977
                                        var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
 
978
 
 
979
                                        // It's null for await without async
 
980
                                        if (async_return != null) {
 
981
                                                async_return.EmitAssign (ec);
 
982
 
 
983
                                                ec.EmitEpilogue ();
 
984
                                        }
 
985
 
 
986
                                        ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
 
987
                                        return;
 
988
                                }
 
989
 
 
990
                                ec.EmitEpilogue ();
 
991
 
 
992
                                if (unwind_protect || ec.EmitAccurateDebugInfo)
 
993
                                        ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
 
994
                        }
 
995
 
 
996
                        if (unwind_protect) {
 
997
                                ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
 
998
                        } else if (ec.EmitAccurateDebugInfo) {
 
999
                                ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
 
1000
                        } else {
 
1001
                                ec.Emit (OpCodes.Ret);
 
1002
                        }
 
1003
                }
 
1004
 
 
1005
                void Error_ReturnFromIterator (ResolveContext rc)
 
1006
                {
 
1007
                        rc.Report.Error (1622, loc,
 
1008
                                "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
 
1009
                }
 
1010
 
 
1011
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
1012
                {
 
1013
                        Return target = (Return) t;
 
1014
                        // It's null for simple return;
 
1015
                        if (expr != null)
 
1016
                                target.expr = expr.Clone (clonectx);
 
1017
                }
 
1018
 
 
1019
                public override object Accept (StructuralVisitor visitor)
 
1020
                {
 
1021
                        return visitor.Visit (this);
 
1022
                }
 
1023
        }
 
1024
 
 
1025
        public class Goto : Statement {
 
1026
                string target;
 
1027
                LabeledStatement label;
 
1028
                bool unwind_protect;
 
1029
 
 
1030
                public override bool Resolve (BlockContext ec)
 
1031
                {
 
1032
                        unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
 
1033
                        ec.CurrentBranching.CurrentUsageVector.Goto ();
 
1034
                        return true;
 
1035
                }
 
1036
                
 
1037
                public Goto (string label, Location l)
 
1038
                {
 
1039
                        loc = l;
 
1040
                        target = label;
 
1041
                }
 
1042
 
 
1043
                public string Target {
 
1044
                        get { return target; }
 
1045
                }
 
1046
 
 
1047
                public void SetResolvedTarget (LabeledStatement label)
 
1048
                {
 
1049
                        this.label = label;
 
1050
                        label.AddReference ();
 
1051
                }
 
1052
 
 
1053
                protected override void CloneTo (CloneContext clonectx, Statement target)
 
1054
                {
 
1055
                        // Nothing to clone
 
1056
                }
 
1057
 
 
1058
                protected override void DoEmit (EmitContext ec)
 
1059
                {
 
1060
                        if (label == null)
 
1061
                                throw new InternalErrorException ("goto emitted before target resolved");
 
1062
                        Label l = label.LabelTarget (ec);
 
1063
                        ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
 
1064
                }
 
1065
                
 
1066
                public override object Accept (StructuralVisitor visitor)
 
1067
                {
 
1068
                        return visitor.Visit (this);
 
1069
                }
 
1070
        }
 
1071
 
 
1072
        public class LabeledStatement : Statement {
 
1073
                string name;
 
1074
                bool defined;
 
1075
                bool referenced;
 
1076
                Label label;
 
1077
                Block block;
 
1078
 
 
1079
                FlowBranching.UsageVector vectors;
 
1080
                
 
1081
                public LabeledStatement (string name, Block block, Location l)
 
1082
                {
 
1083
                        this.name = name;
 
1084
                        this.block = block;
 
1085
                        this.loc = l;
 
1086
                }
 
1087
 
 
1088
                public Label LabelTarget (EmitContext ec)
 
1089
                {
 
1090
                        if (defined)
 
1091
                                return label;
 
1092
 
 
1093
                        label = ec.DefineLabel ();
 
1094
                        defined = true;
 
1095
                        return label;
 
1096
                }
 
1097
 
 
1098
                public Block Block {
 
1099
                        get {
 
1100
                                return block;
 
1101
                        }
 
1102
                }
 
1103
 
 
1104
                public string Name {
 
1105
                        get { return name; }
 
1106
                }
 
1107
 
 
1108
                public bool IsDefined {
 
1109
                        get { return defined; }
 
1110
                }
 
1111
 
 
1112
                public bool HasBeenReferenced {
 
1113
                        get { return referenced; }
 
1114
                }
 
1115
 
 
1116
                public FlowBranching.UsageVector JumpOrigins {
 
1117
                        get { return vectors; }
 
1118
                }
 
1119
 
 
1120
                public void AddUsageVector (FlowBranching.UsageVector vector)
 
1121
                {
 
1122
                        vector = vector.Clone ();
 
1123
                        vector.Next = vectors;
 
1124
                        vectors = vector;
 
1125
                }
 
1126
 
 
1127
                protected override void CloneTo (CloneContext clonectx, Statement target)
 
1128
                {
 
1129
                        // nothing to clone
 
1130
                }
 
1131
 
 
1132
                public override bool Resolve (BlockContext ec)
 
1133
                {
 
1134
                        // this flow-branching will be terminated when the surrounding block ends
 
1135
                        ec.StartFlowBranching (this);
 
1136
                        return true;
 
1137
                }
 
1138
 
 
1139
                protected override void DoEmit (EmitContext ec)
 
1140
                {
 
1141
                        if (!HasBeenReferenced)
 
1142
                                ec.Report.Warning (164, 2, loc, "This label has not been referenced");
 
1143
 
 
1144
                        LabelTarget (ec);
 
1145
                        ec.MarkLabel (label);
 
1146
                }
 
1147
 
 
1148
                public void AddReference ()
 
1149
                {
 
1150
                        referenced = true;
 
1151
                }
 
1152
                
 
1153
                public override object Accept (StructuralVisitor visitor)
 
1154
                {
 
1155
                        return visitor.Visit (this);
 
1156
                }
 
1157
        }
 
1158
        
 
1159
 
 
1160
        /// <summary>
 
1161
        ///   `goto default' statement
 
1162
        /// </summary>
 
1163
        public class GotoDefault : Statement {
 
1164
                
 
1165
                public GotoDefault (Location l)
 
1166
                {
 
1167
                        loc = l;
 
1168
                }
 
1169
 
 
1170
                protected override void CloneTo (CloneContext clonectx, Statement target)
 
1171
                {
 
1172
                        // nothing to clone
 
1173
                }
 
1174
 
 
1175
                public override bool Resolve (BlockContext ec)
 
1176
                {
 
1177
                        ec.CurrentBranching.CurrentUsageVector.Goto ();
 
1178
 
 
1179
                        if (ec.Switch == null) {
 
1180
                                ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
 
1181
                                return false;
 
1182
                        }
 
1183
 
 
1184
                        if (ec.Switch.DefaultLabel == null) {
 
1185
                                FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
 
1186
                                return false;
 
1187
                        }
 
1188
 
 
1189
                        return true;
 
1190
                }
 
1191
 
 
1192
                protected override void DoEmit (EmitContext ec)
 
1193
                {
 
1194
                        ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
 
1195
                }
 
1196
                
 
1197
                public override object Accept (StructuralVisitor visitor)
 
1198
                {
 
1199
                        return visitor.Visit (this);
 
1200
                }
 
1201
        }
 
1202
 
 
1203
        /// <summary>
 
1204
        ///   `goto case' statement
 
1205
        /// </summary>
 
1206
        public class GotoCase : Statement {
 
1207
                Expression expr;
 
1208
                SwitchLabel sl;
 
1209
                
 
1210
                public GotoCase (Expression e, Location l)
 
1211
                {
 
1212
                        expr = e;
 
1213
                        loc = l;
 
1214
                }
 
1215
 
 
1216
                public Expression Expr {
 
1217
                        get {
 
1218
                                return this.expr;
 
1219
                        }
 
1220
                }
 
1221
                
 
1222
                public override bool Resolve (BlockContext ec)
 
1223
                {
 
1224
                        if (ec.Switch == null){
 
1225
                                ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
 
1226
                                return false;
 
1227
                        }
 
1228
 
 
1229
                        ec.CurrentBranching.CurrentUsageVector.Goto ();
 
1230
 
 
1231
                        expr = expr.Resolve (ec);
 
1232
                        if (expr == null)
 
1233
                                return false;
 
1234
 
 
1235
                        Constant c = expr as Constant;
 
1236
                        if (c == null) {
 
1237
                                ec.Report.Error (150, expr.Location, "A constant value is expected");
 
1238
                                return false;
 
1239
                        }
 
1240
 
 
1241
                        Constant res;
 
1242
                        if (ec.Switch.IsNullable && c is NullLiteral) {
 
1243
                                res = c;
 
1244
                        } else {
 
1245
                                TypeSpec type = ec.Switch.SwitchType;
 
1246
                                res = c.Reduce (ec, type);
 
1247
                                if (res == null) {
 
1248
                                        c.Error_ValueCannotBeConverted (ec, type, true);
 
1249
                                        return false;
 
1250
                                }
 
1251
 
 
1252
                                if (!Convert.ImplicitStandardConversionExists (c, type))
 
1253
                                        ec.Report.Warning (469, 2, loc,
 
1254
                                                "The `goto case' value is not implicitly convertible to type `{0}'",
 
1255
                                                TypeManager.CSharpName (type));
 
1256
 
 
1257
                        }
 
1258
 
 
1259
                        sl = ec.Switch.ResolveGotoCase (ec, res);
 
1260
                        return true;
 
1261
                }
 
1262
 
 
1263
                protected override void DoEmit (EmitContext ec)
 
1264
                {
 
1265
                        ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
 
1266
                }
 
1267
 
 
1268
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
1269
                {
 
1270
                        GotoCase target = (GotoCase) t;
 
1271
 
 
1272
                        target.expr = expr.Clone (clonectx);
 
1273
                }
 
1274
                
 
1275
                public override object Accept (StructuralVisitor visitor)
 
1276
                {
 
1277
                        return visitor.Visit (this);
 
1278
                }
 
1279
        }
 
1280
        
 
1281
        public class Throw : Statement {
 
1282
                Expression expr;
 
1283
                
 
1284
                public Throw (Expression expr, Location l)
 
1285
                {
 
1286
                        this.expr = expr;
 
1287
                        loc = l;
 
1288
                }
 
1289
 
 
1290
                public Expression Expr {
 
1291
                        get {
 
1292
                                return this.expr;
 
1293
                        }
 
1294
                }
 
1295
 
 
1296
                public override bool Resolve (BlockContext ec)
 
1297
                {
 
1298
                        if (expr == null) {
 
1299
                                ec.CurrentBranching.CurrentUsageVector.Goto ();
 
1300
                                return ec.CurrentBranching.CheckRethrow (loc);
 
1301
                        }
 
1302
 
 
1303
                        expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
 
1304
                        ec.CurrentBranching.CurrentUsageVector.Goto ();
 
1305
 
 
1306
                        if (expr == null)
 
1307
                                return false;
 
1308
 
 
1309
                        var et = ec.BuiltinTypes.Exception;
 
1310
                        if (Convert.ImplicitConversionExists (ec, expr, et))
 
1311
                                expr = Convert.ImplicitConversion (ec, expr, et, loc);
 
1312
                        else
 
1313
                                ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
 
1314
 
 
1315
                        return true;
 
1316
                }
 
1317
                        
 
1318
                protected override void DoEmit (EmitContext ec)
 
1319
                {
 
1320
                        if (expr == null)
 
1321
                                ec.Emit (OpCodes.Rethrow);
 
1322
                        else {
 
1323
                                expr.Emit (ec);
 
1324
 
 
1325
                                ec.Emit (OpCodes.Throw);
 
1326
                        }
 
1327
                }
 
1328
 
 
1329
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
1330
                {
 
1331
                        Throw target = (Throw) t;
 
1332
 
 
1333
                        if (expr != null)
 
1334
                                target.expr = expr.Clone (clonectx);
 
1335
                }
 
1336
                
 
1337
                public override object Accept (StructuralVisitor visitor)
 
1338
                {
 
1339
                        return visitor.Visit (this);
 
1340
                }
 
1341
        }
 
1342
 
 
1343
        public class Break : Statement {
 
1344
                
 
1345
                public Break (Location l)
 
1346
                {
 
1347
                        loc = l;
 
1348
                }
 
1349
 
 
1350
                bool unwind_protect;
 
1351
 
 
1352
                public override bool Resolve (BlockContext ec)
 
1353
                {
 
1354
                        unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
 
1355
                        ec.CurrentBranching.CurrentUsageVector.Goto ();
 
1356
                        return true;
 
1357
                }
 
1358
 
 
1359
                protected override void DoEmit (EmitContext ec)
 
1360
                {
 
1361
                        ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
 
1362
                }
 
1363
 
 
1364
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
1365
                {
 
1366
                        // nothing needed
 
1367
                }
 
1368
                
 
1369
                public override object Accept (StructuralVisitor visitor)
 
1370
                {
 
1371
                        return visitor.Visit (this);
 
1372
                }
 
1373
        }
 
1374
 
 
1375
        public class Continue : Statement {
 
1376
                
 
1377
                public Continue (Location l)
 
1378
                {
 
1379
                        loc = l;
 
1380
                }
 
1381
 
 
1382
                bool unwind_protect;
 
1383
 
 
1384
                public override bool Resolve (BlockContext ec)
 
1385
                {
 
1386
                        unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
 
1387
                        ec.CurrentBranching.CurrentUsageVector.Goto ();
 
1388
                        return true;
 
1389
                }
 
1390
 
 
1391
                protected override void DoEmit (EmitContext ec)
 
1392
                {
 
1393
                        ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
 
1394
                }
 
1395
 
 
1396
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
1397
                {
 
1398
                        // nothing needed.
 
1399
                }
 
1400
                
 
1401
                public override object Accept (StructuralVisitor visitor)
 
1402
                {
 
1403
                        return visitor.Visit (this);
 
1404
                }
 
1405
        }
 
1406
 
 
1407
        public interface ILocalVariable
 
1408
        {
 
1409
                void Emit (EmitContext ec);
 
1410
                void EmitAssign (EmitContext ec);
 
1411
                void EmitAddressOf (EmitContext ec);
 
1412
        }
 
1413
 
 
1414
        public interface INamedBlockVariable
 
1415
        {
 
1416
                Block Block { get; }
 
1417
                Expression CreateReferenceExpression (ResolveContext rc, Location loc);
 
1418
                bool IsDeclared { get; }
 
1419
                bool IsParameter { get; }
 
1420
                Location Location { get; }
 
1421
        }
 
1422
 
 
1423
        public class BlockVariableDeclaration : Statement
 
1424
        {
 
1425
                public class Declarator
 
1426
                {
 
1427
                        LocalVariable li;
 
1428
                        Expression initializer;
 
1429
 
 
1430
                        public Declarator (LocalVariable li, Expression initializer)
 
1431
                        {
 
1432
                                if (li.Type != null)
 
1433
                                        throw new ArgumentException ("Expected null variable type");
 
1434
 
 
1435
                                this.li = li;
 
1436
                                this.initializer = initializer;
 
1437
                        }
 
1438
 
 
1439
                        public Declarator (Declarator clone, Expression initializer)
 
1440
                        {
 
1441
                                this.li = clone.li;
 
1442
                                this.initializer = initializer;
 
1443
                        }
 
1444
 
 
1445
                        #region Properties
 
1446
 
 
1447
                        public LocalVariable Variable {
 
1448
                                get {
 
1449
                                        return li;
 
1450
                                }
 
1451
                        }
 
1452
 
 
1453
                        public Expression Initializer {
 
1454
                                get {
 
1455
                                        return initializer;
 
1456
                                }
 
1457
                                set {
 
1458
                                        initializer = value;
 
1459
                                }
 
1460
                        }
 
1461
 
 
1462
                        #endregion
 
1463
                }
 
1464
 
 
1465
                Expression initializer;
 
1466
                protected FullNamedExpression type_expr;
 
1467
                protected LocalVariable li;
 
1468
                protected List<Declarator> declarators;
 
1469
                TypeSpec type;
 
1470
 
 
1471
                public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
 
1472
                {
 
1473
                        this.type_expr = type;
 
1474
                        this.li = li;
 
1475
                        this.loc = type_expr.Location;
 
1476
                }
 
1477
 
 
1478
                protected BlockVariableDeclaration (LocalVariable li)
 
1479
                {
 
1480
                        this.li = li;
 
1481
                }
 
1482
 
 
1483
                #region Properties
 
1484
 
 
1485
                public List<Declarator> Declarators {
 
1486
                        get {
 
1487
                                return declarators;
 
1488
                        }
 
1489
                }
 
1490
 
 
1491
                public Expression Initializer {
 
1492
                        get {
 
1493
                                return initializer;
 
1494
                        }
 
1495
                        set {
 
1496
                                initializer = value;
 
1497
                        }
 
1498
                }
 
1499
 
 
1500
                public FullNamedExpression TypeExpression {
 
1501
                        get {
 
1502
                                return type_expr;
 
1503
                        }
 
1504
                }
 
1505
 
 
1506
                public LocalVariable Variable {
 
1507
                        get {
 
1508
                                return li;
 
1509
                        }
 
1510
                }
 
1511
 
 
1512
                #endregion
 
1513
 
 
1514
                public void AddDeclarator (Declarator decl)
 
1515
                {
 
1516
                        if (declarators == null)
 
1517
                                declarators = new List<Declarator> ();
 
1518
 
 
1519
                        declarators.Add (decl);
 
1520
                }
 
1521
 
 
1522
                static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
 
1523
                {
 
1524
                        if (bc.Report.Errors != 0)
 
1525
                                return;
 
1526
 
 
1527
                        var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
 
1528
 
 
1529
                        Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
 
1530
                                new MemberName (li.Name, li.Location), null);
 
1531
 
 
1532
                        container.AddField (f);
 
1533
                        f.Define ();
 
1534
 
 
1535
                        li.HoistedVariant = new HoistedEvaluatorVariable (f);
 
1536
                        li.SetIsUsed ();
 
1537
                }
 
1538
 
 
1539
                public override bool Resolve (BlockContext bc)
 
1540
                {
 
1541
                        return Resolve (bc, true);
 
1542
                }
 
1543
 
 
1544
                public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
 
1545
                {
 
1546
                        if (type == null && !li.IsCompilerGenerated) {
 
1547
                                var vexpr = type_expr as VarExpr;
 
1548
 
 
1549
                                //
 
1550
                                // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
 
1551
                                // same name exists or as a keyword when no type was found
 
1552
                                //
 
1553
                                if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
 
1554
                                        if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
 
1555
                                                bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
 
1556
 
 
1557
                                        if (li.IsFixed) {
 
1558
                                                bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
 
1559
                                                return false;
 
1560
                                        }
 
1561
 
 
1562
                                        if (li.IsConstant) {
 
1563
                                                bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
 
1564
                                                return false;
 
1565
                                        }
 
1566
 
 
1567
                                        if (Initializer == null) {
 
1568
                                                bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
 
1569
                                                return false;
 
1570
                                        }
 
1571
 
 
1572
                                        if (declarators != null) {
 
1573
                                                bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
 
1574
                                                declarators = null;
 
1575
                                        }
 
1576
 
 
1577
                                        Initializer = Initializer.Resolve (bc);
 
1578
                                        if (Initializer != null) {
 
1579
                                                ((VarExpr) type_expr).InferType (bc, Initializer);
 
1580
                                                type = type_expr.Type;
 
1581
                                        } else {
 
1582
                                                // Set error type to indicate the var was placed correctly but could
 
1583
                                                // not be infered
 
1584
                                                //
 
1585
                                                // var a = missing ();
 
1586
                                                //
 
1587
                                                type = InternalType.ErrorType;
 
1588
                                        }
 
1589
                                }
 
1590
 
 
1591
                                if (type == null) {
 
1592
                                        type = type_expr.ResolveAsType (bc);
 
1593
                                        if (type == null)
 
1594
                                                return false;
 
1595
 
 
1596
                                        if (li.IsConstant && !type.IsConstantCompatible) {
 
1597
                                                Const.Error_InvalidConstantType (type, loc, bc.Report);
 
1598
                                        }
 
1599
                                }
 
1600
 
 
1601
                                if (type.IsStatic)
 
1602
                                        FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
 
1603
 
 
1604
                                li.Type = type;
 
1605
                        }
 
1606
 
 
1607
                        bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
 
1608
                        if (eval_global) {
 
1609
                                CreateEvaluatorVariable (bc, li);
 
1610
                        } else {
 
1611
                                li.PrepareForFlowAnalysis (bc);
 
1612
                        }
 
1613
 
 
1614
                        if (initializer != null) {
 
1615
                                initializer = ResolveInitializer (bc, li, initializer);
 
1616
                                // li.Variable.DefinitelyAssigned 
 
1617
                        }
 
1618
 
 
1619
                        if (declarators != null) {
 
1620
                                foreach (var d in declarators) {
 
1621
                                        d.Variable.Type = li.Type;
 
1622
                                        if (eval_global) {
 
1623
                                                CreateEvaluatorVariable (bc, d.Variable);
 
1624
                                        } else {
 
1625
                                                d.Variable.PrepareForFlowAnalysis (bc);
 
1626
                                        }
 
1627
 
 
1628
                                        if (d.Initializer != null && resolveDeclaratorInitializers) {
 
1629
                                                d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
 
1630
                                                // d.Variable.DefinitelyAssigned 
 
1631
                                        } 
 
1632
                                }
 
1633
                        }
 
1634
 
 
1635
                        return true;
 
1636
                }
 
1637
 
 
1638
                protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
 
1639
                {
 
1640
                        var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
 
1641
                        return a.ResolveStatement (bc);
 
1642
                }
 
1643
 
 
1644
                protected override void DoEmit (EmitContext ec)
 
1645
                {
 
1646
                        li.CreateBuilder (ec);
 
1647
 
 
1648
                        if (Initializer != null)
 
1649
                                ((ExpressionStatement) Initializer).EmitStatement (ec);
 
1650
 
 
1651
                        if (declarators != null) {
 
1652
                                foreach (var d in declarators) {
 
1653
                                        d.Variable.CreateBuilder (ec);
 
1654
                                        if (d.Initializer != null) {
 
1655
                                                ec.Mark (d.Variable.Location);
 
1656
                                                ((ExpressionStatement) d.Initializer).EmitStatement (ec);
 
1657
                                        }
 
1658
                                }
 
1659
                        }
 
1660
                }
 
1661
 
 
1662
                protected override void CloneTo (CloneContext clonectx, Statement target)
 
1663
                {
 
1664
                        BlockVariableDeclaration t = (BlockVariableDeclaration) target;
 
1665
 
 
1666
                        if (type_expr != null)
 
1667
                                t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
 
1668
 
 
1669
                        if (initializer != null)
 
1670
                                t.initializer = initializer.Clone (clonectx);
 
1671
 
 
1672
                        if (declarators != null) {
 
1673
                                t.declarators = null;
 
1674
                                foreach (var d in declarators)
 
1675
                                        t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
 
1676
                        }
 
1677
                }
 
1678
 
 
1679
                public override object Accept (StructuralVisitor visitor)
 
1680
                {
 
1681
                        return visitor.Visit (this);
 
1682
                }
 
1683
        }
 
1684
 
 
1685
        public class BlockConstantDeclaration : BlockVariableDeclaration
 
1686
        {
 
1687
                public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
 
1688
                        : base (type, li)
 
1689
                {
 
1690
                }
 
1691
 
 
1692
                public override void Emit (EmitContext ec)
 
1693
                {
 
1694
                        // Nothing to emit, not even sequence point
 
1695
                }
 
1696
 
 
1697
                protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
 
1698
                {
 
1699
                        initializer = initializer.Resolve (bc);
 
1700
                        if (initializer == null)
 
1701
                                return null;
 
1702
 
 
1703
                        var c = initializer as Constant;
 
1704
                        if (c == null) {
 
1705
                                initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
 
1706
                                return null;
 
1707
                        }
 
1708
 
 
1709
                        c = c.ConvertImplicitly (li.Type);
 
1710
                        if (c == null) {
 
1711
                                if (TypeSpec.IsReferenceType (li.Type))
 
1712
                                        initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
 
1713
                                else
 
1714
                                        initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
 
1715
 
 
1716
                                return null;
 
1717
                        }
 
1718
 
 
1719
                        li.ConstantValue = c;
 
1720
                        return initializer;
 
1721
                }
 
1722
                
 
1723
                public override object Accept (StructuralVisitor visitor)
 
1724
                {
 
1725
                        return visitor.Visit (this);
 
1726
                }
 
1727
        }
 
1728
 
 
1729
        //
 
1730
        // The information about a user-perceived local variable
 
1731
        //
 
1732
        public class LocalVariable : INamedBlockVariable, ILocalVariable
 
1733
        {
 
1734
                [Flags]
 
1735
                public enum Flags
 
1736
                {
 
1737
                        Used = 1,
 
1738
                        IsThis = 1 << 1,
 
1739
                        AddressTaken = 1 << 2,
 
1740
                        CompilerGenerated = 1 << 3,
 
1741
                        Constant = 1 << 4,
 
1742
                        ForeachVariable = 1 << 5,
 
1743
                        FixedVariable = 1 << 6,
 
1744
                        UsingVariable = 1 << 7,
 
1745
//                      DefinitelyAssigned = 1 << 8,
 
1746
                        IsLocked = 1 << 9,
 
1747
 
 
1748
                        ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
 
1749
                }
 
1750
 
 
1751
                TypeSpec type;
 
1752
                readonly string name;
 
1753
                readonly Location loc;
 
1754
                readonly Block block;
 
1755
                Flags flags;
 
1756
                Constant const_value;
 
1757
 
 
1758
                public VariableInfo VariableInfo;
 
1759
                HoistedVariable hoisted_variant;
 
1760
 
 
1761
                LocalBuilder builder;
 
1762
 
 
1763
                public LocalVariable (Block block, string name, Location loc)
 
1764
                {
 
1765
                        this.block = block;
 
1766
                        this.name = name;
 
1767
                        this.loc = loc;
 
1768
                }
 
1769
 
 
1770
                public LocalVariable (Block block, string name, Flags flags, Location loc)
 
1771
                        : this (block, name, loc)
 
1772
                {
 
1773
                        this.flags = flags;
 
1774
                }
 
1775
 
 
1776
                //
 
1777
                // Used by variable declarators
 
1778
                //
 
1779
                public LocalVariable (LocalVariable li, string name, Location loc)
 
1780
                        : this (li.block, name, li.flags, loc)
 
1781
                {
 
1782
                }
 
1783
 
 
1784
                #region Properties
 
1785
 
 
1786
                public bool AddressTaken {
 
1787
                        get {
 
1788
                                return (flags & Flags.AddressTaken) != 0;
 
1789
                        }
 
1790
                }
 
1791
 
 
1792
                public Block Block {
 
1793
                        get {
 
1794
                                return block;
 
1795
                        }
 
1796
                }
 
1797
 
 
1798
                public Constant ConstantValue {
 
1799
                        get {
 
1800
                                return const_value;
 
1801
                        }
 
1802
                        set {
 
1803
                                const_value = value;
 
1804
                        }
 
1805
                }
 
1806
 
 
1807
                //
 
1808
                // Hoisted local variable variant
 
1809
                //
 
1810
                public HoistedVariable HoistedVariant {
 
1811
                        get {
 
1812
                                return hoisted_variant;
 
1813
                        }
 
1814
                        set {
 
1815
                                hoisted_variant = value;
 
1816
                        }
 
1817
                }
 
1818
 
 
1819
                public bool IsDeclared {
 
1820
                        get {
 
1821
                                return type != null;
 
1822
                        }
 
1823
                }
 
1824
 
 
1825
                public bool IsCompilerGenerated {
 
1826
                        get {
 
1827
                                return (flags & Flags.CompilerGenerated) != 0;
 
1828
                        }
 
1829
                }
 
1830
 
 
1831
                public bool IsConstant {
 
1832
                        get {
 
1833
                                return (flags & Flags.Constant) != 0;
 
1834
                        }
 
1835
                }
 
1836
 
 
1837
                public bool IsLocked {
 
1838
                        get {
 
1839
                                return (flags & Flags.IsLocked) != 0;
 
1840
                        }
 
1841
                        set {
 
1842
                                flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
 
1843
                        }
 
1844
                }
 
1845
 
 
1846
                public bool IsThis {
 
1847
                        get {
 
1848
                                return (flags & Flags.IsThis) != 0;
 
1849
                        }
 
1850
                }
 
1851
 
 
1852
                public bool IsFixed {
 
1853
                        get {
 
1854
                                return (flags & Flags.FixedVariable) != 0;
 
1855
                        }
 
1856
                }
 
1857
 
 
1858
                bool INamedBlockVariable.IsParameter {
 
1859
                        get {
 
1860
                                return false;
 
1861
                        }
 
1862
                }
 
1863
 
 
1864
                public bool IsReadonly {
 
1865
                        get {
 
1866
                                return (flags & Flags.ReadonlyMask) != 0;
 
1867
                        }
 
1868
                }
 
1869
 
 
1870
                public Location Location {
 
1871
                        get {
 
1872
                                return loc;
 
1873
                        }
 
1874
                }
 
1875
 
 
1876
                public string Name {
 
1877
                        get {
 
1878
                                return name;
 
1879
                        }
 
1880
                }
 
1881
 
 
1882
                public TypeSpec Type {
 
1883
                    get {
 
1884
                                return type;
 
1885
                        }
 
1886
                    set {
 
1887
                                type = value;
 
1888
                        }
 
1889
                }
 
1890
 
 
1891
                #endregion
 
1892
 
 
1893
                public void CreateBuilder (EmitContext ec)
 
1894
                {
 
1895
                        if ((flags & Flags.Used) == 0) {
 
1896
                                if (VariableInfo == null) {
 
1897
                                        // Missing flow analysis or wrong variable flags
 
1898
                                        throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
 
1899
                                }
 
1900
 
 
1901
                                if (VariableInfo.IsEverAssigned)
 
1902
                                        ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
 
1903
                                else
 
1904
                                        ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
 
1905
                        }
 
1906
 
 
1907
                        if (HoistedVariant != null)
 
1908
                                return;
 
1909
 
 
1910
                        if (builder != null) {
 
1911
                                if ((flags & Flags.CompilerGenerated) != 0)
 
1912
                                        return;
 
1913
 
 
1914
                                // To avoid Used warning duplicates
 
1915
                                throw new InternalErrorException ("Already created variable `{0}'", name);
 
1916
                        }
 
1917
 
 
1918
                        //
 
1919
                        // All fixed variabled are pinned, a slot has to be alocated
 
1920
                        //
 
1921
                        builder = ec.DeclareLocal (Type, IsFixed);
 
1922
                        if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
 
1923
                                ec.DefineLocalVariable (name, builder);
 
1924
                }
 
1925
 
 
1926
                public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
 
1927
                {
 
1928
                        LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
 
1929
                        li.Type = type;
 
1930
                        return li;
 
1931
                }
 
1932
 
 
1933
                public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
 
1934
                {
 
1935
                        if (IsConstant && const_value != null)
 
1936
                                return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
 
1937
 
 
1938
                        return new LocalVariableReference (this, loc);
 
1939
                }
 
1940
 
 
1941
                public void Emit (EmitContext ec)
 
1942
                {
 
1943
                        // TODO: Need something better for temporary variables
 
1944
                        if ((flags & Flags.CompilerGenerated) != 0)
 
1945
                                CreateBuilder (ec);
 
1946
 
 
1947
                        ec.Emit (OpCodes.Ldloc, builder);
 
1948
                }
 
1949
 
 
1950
                public void EmitAssign (EmitContext ec)
 
1951
                {
 
1952
                        // TODO: Need something better for temporary variables
 
1953
                        if ((flags & Flags.CompilerGenerated) != 0)
 
1954
                                CreateBuilder (ec);
 
1955
 
 
1956
                        ec.Emit (OpCodes.Stloc, builder);
 
1957
                }
 
1958
 
 
1959
                public void EmitAddressOf (EmitContext ec)
 
1960
                {
 
1961
                        ec.Emit (OpCodes.Ldloca, builder);
 
1962
                }
 
1963
 
 
1964
                public static string GetCompilerGeneratedName (Block block)
 
1965
                {
 
1966
                        // HACK: Debugger depends on the name semantics
 
1967
                        return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
 
1968
                }
 
1969
 
 
1970
                public string GetReadOnlyContext ()
 
1971
                {
 
1972
                        switch (flags & Flags.ReadonlyMask) {
 
1973
                        case Flags.FixedVariable:
 
1974
                                return "fixed variable";
 
1975
                        case Flags.ForeachVariable:
 
1976
                                return "foreach iteration variable";
 
1977
                        case Flags.UsingVariable:
 
1978
                                return "using variable";
 
1979
                        }
 
1980
 
 
1981
                        throw new InternalErrorException ("Variable is not readonly");
 
1982
                }
 
1983
 
 
1984
                public bool IsThisAssigned (BlockContext ec, Block block)
 
1985
                {
 
1986
                        if (VariableInfo == null)
 
1987
                                throw new Exception ();
 
1988
 
 
1989
                        if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
 
1990
                                return true;
 
1991
 
 
1992
                        return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
 
1993
                }
 
1994
 
 
1995
                public bool IsAssigned (BlockContext ec)
 
1996
                {
 
1997
                        if (VariableInfo == null)
 
1998
                                throw new Exception ();
 
1999
 
 
2000
                        return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
 
2001
                }
 
2002
 
 
2003
                public void PrepareForFlowAnalysis (BlockContext bc)
 
2004
                {
 
2005
                        //
 
2006
                        // No need for definitely assigned check for these guys
 
2007
                        //
 
2008
                        if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
 
2009
                                return;
 
2010
 
 
2011
                        VariableInfo = new VariableInfo (this, bc.FlowOffset);
 
2012
                        bc.FlowOffset += VariableInfo.Length;
 
2013
                }
 
2014
 
 
2015
                //
 
2016
                // Mark the variables as referenced in the user code
 
2017
                //
 
2018
                public void SetIsUsed ()
 
2019
                {
 
2020
                        flags |= Flags.Used;
 
2021
                }
 
2022
 
 
2023
                public void SetHasAddressTaken ()
 
2024
                {
 
2025
                        flags |= (Flags.AddressTaken | Flags.Used);
 
2026
                }
 
2027
 
 
2028
                public override string ToString ()
 
2029
                {
 
2030
                        return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
 
2031
                }
 
2032
        }
 
2033
 
 
2034
        /// <summary>
 
2035
        ///   Block represents a C# block.
 
2036
        /// </summary>
 
2037
        ///
 
2038
        /// <remarks>
 
2039
        ///   This class is used in a number of places: either to represent
 
2040
        ///   explicit blocks that the programmer places or implicit blocks.
 
2041
        ///
 
2042
        ///   Implicit blocks are used as labels or to introduce variable
 
2043
        ///   declarations.
 
2044
        ///
 
2045
        ///   Top-level blocks derive from Block, and they are called ToplevelBlock
 
2046
        ///   they contain extra information that is not necessary on normal blocks.
 
2047
        /// </remarks>
 
2048
        public class Block : Statement {
 
2049
                [Flags]
 
2050
                public enum Flags
 
2051
                {
 
2052
                        Unchecked = 1,
 
2053
                        HasRet = 8,
 
2054
                        Unsafe = 16,
 
2055
                        HasCapturedVariable = 64,
 
2056
                        HasCapturedThis = 1 << 7,
 
2057
                        IsExpressionTree = 1 << 8,
 
2058
                        CompilerGenerated = 1 << 9,
 
2059
                        HasAsyncModifier = 1 << 10,
 
2060
                        Resolved = 1 << 11,
 
2061
                        YieldBlock = 1 << 12,
 
2062
                        AwaitBlock = 1 << 13
 
2063
                }
 
2064
 
 
2065
                public Block Parent;
 
2066
                public Location StartLocation;
 
2067
                public Location EndLocation;
 
2068
 
 
2069
                public ExplicitBlock Explicit;
 
2070
                public ParametersBlock ParametersBlock;
 
2071
 
 
2072
                protected Flags flags;
 
2073
 
 
2074
                //
 
2075
                // The statements in this block
 
2076
                //
 
2077
                protected List<Statement> statements;
 
2078
 
 
2079
                protected List<Statement> scope_initializers;
 
2080
 
 
2081
                int? resolving_init_idx;
 
2082
 
 
2083
                Block original;
 
2084
 
 
2085
#if DEBUG
 
2086
                static int id;
 
2087
                public int ID = id++;
 
2088
 
 
2089
                static int clone_id_counter;
 
2090
                int clone_id;
 
2091
#endif
 
2092
 
 
2093
//              int assignable_slots;
 
2094
 
 
2095
                public Block (Block parent, Location start, Location end)
 
2096
                        : this (parent, 0, start, end)
 
2097
                {
 
2098
                }
 
2099
 
 
2100
                public Block (Block parent, Flags flags, Location start, Location end)
 
2101
                {
 
2102
                        if (parent != null) {
 
2103
                                // the appropriate constructors will fixup these fields
 
2104
                                ParametersBlock = parent.ParametersBlock;
 
2105
                                Explicit = parent.Explicit;
 
2106
                        }
 
2107
                        
 
2108
                        this.Parent = parent;
 
2109
                        this.flags = flags;
 
2110
                        this.StartLocation = start;
 
2111
                        this.EndLocation = end;
 
2112
                        this.loc = start;
 
2113
                        statements = new List<Statement> (4);
 
2114
 
 
2115
                        this.original = this;
 
2116
                }
 
2117
 
 
2118
                #region Properties
 
2119
 
 
2120
                public bool HasUnreachableClosingBrace {
 
2121
                        get {
 
2122
                                return (flags & Flags.HasRet) != 0;
 
2123
                        }
 
2124
                        set {
 
2125
                                flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
 
2126
                        }
 
2127
                }
 
2128
 
 
2129
                public Block Original {
 
2130
                        get {
 
2131
                                return original;
 
2132
                        }
 
2133
                        protected set {
 
2134
                                original = value;
 
2135
                        }
 
2136
                }
 
2137
 
 
2138
                public bool IsCompilerGenerated {
 
2139
                        get { return (flags & Flags.CompilerGenerated) != 0; }
 
2140
                        set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
 
2141
                }
 
2142
 
 
2143
                public bool Unchecked {
 
2144
                        get { return (flags & Flags.Unchecked) != 0; }
 
2145
                        set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
 
2146
                }
 
2147
 
 
2148
                public bool Unsafe {
 
2149
                        get { return (flags & Flags.Unsafe) != 0; }
 
2150
                        set { flags |= Flags.Unsafe; }
 
2151
                }
 
2152
 
 
2153
                public List<Statement> Statements {
 
2154
                        get { return statements; }
 
2155
                }
 
2156
 
 
2157
                #endregion
 
2158
 
 
2159
                public void SetEndLocation (Location loc)
 
2160
                {
 
2161
                        EndLocation = loc;
 
2162
                }
 
2163
 
 
2164
                public void AddLabel (LabeledStatement target)
 
2165
                {
 
2166
                        ParametersBlock.TopBlock.AddLabel (target.Name, target);
 
2167
                }
 
2168
 
 
2169
                public void AddLocalName (LocalVariable li)
 
2170
                {
 
2171
                        AddLocalName (li.Name, li);
 
2172
                }
 
2173
 
 
2174
                public void AddLocalName (string name, INamedBlockVariable li)
 
2175
                {
 
2176
                        ParametersBlock.TopBlock.AddLocalName (name, li, false);
 
2177
                }
 
2178
 
 
2179
                public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
 
2180
                {
 
2181
                        if (reason == null) {
 
2182
                                Error_AlreadyDeclared (name, variable);
 
2183
                                return;
 
2184
                        }
 
2185
 
 
2186
                        ParametersBlock.TopBlock.Report.Error (136, variable.Location,
 
2187
                                "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
 
2188
                                "to `{0}', which is already used in a `{1}' scope to denote something else",
 
2189
                                name, reason);
 
2190
                }
 
2191
 
 
2192
                public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
 
2193
                {
 
2194
                        var pi = variable as ParametersBlock.ParameterInfo;
 
2195
                        if (pi != null) {
 
2196
                                pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
 
2197
                        } else {
 
2198
                                ParametersBlock.TopBlock.Report.Error (128, variable.Location,
 
2199
                                        "A local variable named `{0}' is already defined in this scope", name);
 
2200
                        }
 
2201
                }
 
2202
                                        
 
2203
                public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
 
2204
                {
 
2205
                        ParametersBlock.TopBlock.Report.Error (412, loc,
 
2206
                                "The type parameter name `{0}' is the same as local variable or parameter name",
 
2207
                                name);
 
2208
                }
 
2209
 
 
2210
                //
 
2211
                // It should be used by expressions which require to
 
2212
                // register a statement during resolve process.
 
2213
                //
 
2214
                public void AddScopeStatement (Statement s)
 
2215
                {
 
2216
                        if (scope_initializers == null)
 
2217
                                scope_initializers = new List<Statement> ();
 
2218
 
 
2219
                        //
 
2220
                        // Simple recursive helper, when resolve scope initializer another
 
2221
                        // new scope initializer can be added, this ensures it's initialized
 
2222
                        // before existing one. For now this can happen with expression trees
 
2223
                        // in base ctor initializer only
 
2224
                        //
 
2225
                        if (resolving_init_idx.HasValue) {
 
2226
                                scope_initializers.Insert (resolving_init_idx.Value, s);
 
2227
                                ++resolving_init_idx;
 
2228
                        } else {
 
2229
                                scope_initializers.Add (s);
 
2230
                        }
 
2231
                }
 
2232
                
 
2233
                public void AddStatement (Statement s)
 
2234
                {
 
2235
                        statements.Add (s);
 
2236
                }
 
2237
 
 
2238
                public int AssignableSlots {
 
2239
                        get {
 
2240
                                // FIXME: HACK, we don't know the block available variables count now, so set this high enough
 
2241
                                return 4096;
 
2242
//                              return assignable_slots;
 
2243
                        }
 
2244
                }
 
2245
 
 
2246
                public LabeledStatement LookupLabel (string name)
 
2247
                {
 
2248
                        return ParametersBlock.TopBlock.GetLabel (name, this);
 
2249
                }
 
2250
 
 
2251
                public override bool Resolve (BlockContext ec)
 
2252
                {
 
2253
                        if ((flags & Flags.Resolved) != 0)
 
2254
                                return true;
 
2255
 
 
2256
                        Block prev_block = ec.CurrentBlock;
 
2257
                        bool ok = true;
 
2258
                        bool unreachable = ec.IsUnreachable;
 
2259
                        bool prev_unreachable = unreachable;
 
2260
 
 
2261
                        ec.CurrentBlock = this;
 
2262
                        ec.StartFlowBranching (this);
 
2263
 
 
2264
                        //
 
2265
                        // Compiler generated scope statements
 
2266
                        //
 
2267
                        if (scope_initializers != null) {
 
2268
                                for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
 
2269
                                        scope_initializers[resolving_init_idx.Value].Resolve (ec);
 
2270
                                }
 
2271
 
 
2272
                                resolving_init_idx = null;
 
2273
                        }
 
2274
 
 
2275
                        //
 
2276
                        // This flag is used to notate nested statements as unreachable from the beginning of this block.
 
2277
                        // For the purposes of this resolution, it doesn't matter that the whole block is unreachable 
 
2278
                        // from the beginning of the function.  The outer Resolve() that detected the unreachability is
 
2279
                        // responsible for handling the situation.
 
2280
                        //
 
2281
                        int statement_count = statements.Count;
 
2282
                        for (int ix = 0; ix < statement_count; ix++){
 
2283
                                Statement s = statements [ix];
 
2284
 
 
2285
                                //
 
2286
                                // Warn if we detect unreachable code.
 
2287
                                //
 
2288
                                if (unreachable) {
 
2289
                                        if (s is EmptyStatement)
 
2290
                                                continue;
 
2291
 
 
2292
                                        if (!ec.UnreachableReported && !(s is LabeledStatement) && !(s is SwitchLabel)) {
 
2293
                                                ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
 
2294
                                                ec.UnreachableReported = true;
 
2295
                                        }
 
2296
                                }
 
2297
 
 
2298
                                //
 
2299
                                // Note that we're not using ResolveUnreachable() for unreachable
 
2300
                                // statements here.  ResolveUnreachable() creates a temporary
 
2301
                                // flow branching and kills it afterwards.  This leads to problems
 
2302
                                // if you have two unreachable statements where the first one
 
2303
                                // assigns a variable and the second one tries to access it.
 
2304
                                //
 
2305
 
 
2306
                                if (!s.Resolve (ec)) {
 
2307
                                        ok = false;
 
2308
                                        if (!ec.IsInProbingMode)
 
2309
                                                statements [ix] = new EmptyStatement (s.loc);
 
2310
 
 
2311
                                        continue;
 
2312
                                }
 
2313
 
 
2314
                                if (unreachable && !(s is LabeledStatement) && !(s is SwitchLabel) && !(s is Block))
 
2315
                                        statements [ix] = new EmptyStatement (s.loc);
 
2316
 
 
2317
                                unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
 
2318
                                if (unreachable) {
 
2319
                                        ec.IsUnreachable = true;
 
2320
                                } else if (ec.IsUnreachable)
 
2321
                                        ec.IsUnreachable = false;
 
2322
                        }
 
2323
 
 
2324
                        if (unreachable != prev_unreachable) {
 
2325
                                ec.IsUnreachable = prev_unreachable;
 
2326
                                ec.UnreachableReported = false;
 
2327
                        }
 
2328
 
 
2329
                        while (ec.CurrentBranching is FlowBranchingLabeled)
 
2330
                                ec.EndFlowBranching ();
 
2331
 
 
2332
                        bool flow_unreachable = ec.EndFlowBranching ();
 
2333
 
 
2334
                        ec.CurrentBlock = prev_block;
 
2335
 
 
2336
                        if (flow_unreachable)
 
2337
                                flags |= Flags.HasRet;
 
2338
 
 
2339
                        // If we're a non-static `struct' constructor which doesn't have an
 
2340
                        // initializer, then we must initialize all of the struct's fields.
 
2341
                        if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
 
2342
                                ok = false;
 
2343
 
 
2344
                        flags |= Flags.Resolved;
 
2345
                        return ok;
 
2346
                }
 
2347
 
 
2348
                public override bool ResolveUnreachable (BlockContext ec, bool warn)
 
2349
                {
 
2350
                        bool unreachable = false;
 
2351
                        if (warn && !ec.UnreachableReported) {
 
2352
                                ec.UnreachableReported = true;
 
2353
                                unreachable = true;
 
2354
                                ec.Report.Warning (162, 2, loc, "Unreachable code detected");
 
2355
                        }
 
2356
 
 
2357
                        var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
 
2358
                        fb.CurrentUsageVector.IsUnreachable = true;
 
2359
                        bool ok = Resolve (ec);
 
2360
                        ec.KillFlowBranching ();
 
2361
 
 
2362
                        if (unreachable)
 
2363
                                ec.UnreachableReported = false;
 
2364
 
 
2365
                        return ok;
 
2366
                }
 
2367
                
 
2368
                protected override void DoEmit (EmitContext ec)
 
2369
                {
 
2370
                        for (int ix = 0; ix < statements.Count; ix++){
 
2371
                                statements [ix].Emit (ec);
 
2372
                        }
 
2373
                }
 
2374
 
 
2375
                public override void Emit (EmitContext ec)
 
2376
                {
 
2377
                        if (scope_initializers != null)
 
2378
                                EmitScopeInitializers (ec);
 
2379
 
 
2380
                        DoEmit (ec);
 
2381
                }
 
2382
 
 
2383
                protected void EmitScopeInitializers (EmitContext ec)
 
2384
                {
 
2385
                        foreach (Statement s in scope_initializers)
 
2386
                                s.Emit (ec);
 
2387
                }
 
2388
 
 
2389
#if DEBUG
 
2390
                public override string ToString ()
 
2391
                {
 
2392
                        return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
 
2393
                }
 
2394
#endif
 
2395
 
 
2396
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
2397
                {
 
2398
                        Block target = (Block) t;
 
2399
#if DEBUG
 
2400
                        target.clone_id = clone_id_counter++;
 
2401
#endif
 
2402
 
 
2403
                        clonectx.AddBlockMap (this, target);
 
2404
                        if (original != this)
 
2405
                                clonectx.AddBlockMap (original, target);
 
2406
 
 
2407
                        target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
 
2408
                        target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
 
2409
 
 
2410
                        if (Parent != null)
 
2411
                                target.Parent = clonectx.RemapBlockCopy (Parent);
 
2412
 
 
2413
                        target.statements = new List<Statement> (statements.Count);
 
2414
                        foreach (Statement s in statements)
 
2415
                                target.statements.Add (s.Clone (clonectx));
 
2416
                }
 
2417
 
 
2418
                public override object Accept (StructuralVisitor visitor)
 
2419
                {
 
2420
                        return visitor.Visit (this);
 
2421
                }
 
2422
        }
 
2423
 
 
2424
        public class ExplicitBlock : Block
 
2425
        {
 
2426
                protected AnonymousMethodStorey am_storey;
 
2427
 
 
2428
                public ExplicitBlock (Block parent, Location start, Location end)
 
2429
                        : this (parent, (Flags) 0, start, end)
 
2430
                {
 
2431
                }
 
2432
 
 
2433
                public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
 
2434
                        : base (parent, flags, start, end)
 
2435
                {
 
2436
                        this.Explicit = this;
 
2437
                }
 
2438
 
 
2439
                #region Properties
 
2440
 
 
2441
                public AnonymousMethodStorey AnonymousMethodStorey {
 
2442
                        get {
 
2443
                                return am_storey;
 
2444
                        }
 
2445
                }
 
2446
 
 
2447
                public bool HasAwait {
 
2448
                        get {
 
2449
                                return (flags & Flags.AwaitBlock) != 0;
 
2450
                        }
 
2451
                }
 
2452
 
 
2453
                public bool HasCapturedThis {
 
2454
                        set {
 
2455
                                flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
 
2456
                        }
 
2457
                        get {
 
2458
                                return (flags & Flags.HasCapturedThis) != 0;
 
2459
                        }
 
2460
                }
 
2461
 
 
2462
                //
 
2463
                // Used to indicate that the block has reference to parent
 
2464
                // block and cannot be made static when defining anonymous method
 
2465
                //
 
2466
                public bool HasCapturedVariable {
 
2467
                        set {
 
2468
                                flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
 
2469
                        }
 
2470
                        get {
 
2471
                                return (flags & Flags.HasCapturedVariable) != 0;
 
2472
                        }
 
2473
                }
 
2474
 
 
2475
                public bool HasYield {
 
2476
                        get {
 
2477
                                return (flags & Flags.YieldBlock) != 0;
 
2478
                        }
 
2479
                }
 
2480
 
 
2481
                #endregion
 
2482
 
 
2483
                //
 
2484
                // Creates anonymous method storey in current block
 
2485
                //
 
2486
                public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
 
2487
                {
 
2488
                        //
 
2489
                        // Return same story for iterator and async blocks unless we are
 
2490
                        // in nested anonymous method
 
2491
                        //
 
2492
                        if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
 
2493
                                return ec.CurrentAnonymousMethod.Storey;
 
2494
 
 
2495
                        if (am_storey == null) {
 
2496
                                MemberBase mc = ec.MemberContext as MemberBase;
 
2497
 
 
2498
                                //
 
2499
                                // Creates anonymous method storey for this block
 
2500
                                //
 
2501
                                am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
 
2502
                        }
 
2503
 
 
2504
                        return am_storey;
 
2505
                }
 
2506
 
 
2507
                public override void Emit (EmitContext ec)
 
2508
                {
 
2509
                        if (am_storey != null) {
 
2510
                                DefineStoreyContainer (ec, am_storey);
 
2511
                                am_storey.EmitStoreyInstantiation (ec, this);
 
2512
                        }
 
2513
 
 
2514
                        if (scope_initializers != null)
 
2515
                                EmitScopeInitializers (ec);
 
2516
 
 
2517
                        if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
 
2518
                                ec.Emit (OpCodes.Nop);
 
2519
                        }
 
2520
 
 
2521
                        if (Parent != null)
 
2522
                                ec.BeginScope ();
 
2523
 
 
2524
                        DoEmit (ec);
 
2525
 
 
2526
                        if (Parent != null)
 
2527
                                ec.EndScope ();
 
2528
 
 
2529
                        if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
 
2530
                                ec.Emit (OpCodes.Nop);
 
2531
                        }
 
2532
                }
 
2533
 
 
2534
                protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
 
2535
                {
 
2536
                        if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
 
2537
                                storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
 
2538
                                storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
 
2539
                        }
 
2540
 
 
2541
                        //
 
2542
                        // Creates anonymous method storey
 
2543
                        //
 
2544
                        storey.CreateContainer ();
 
2545
                        storey.DefineContainer ();
 
2546
 
 
2547
                        if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
 
2548
 
 
2549
                                //
 
2550
                                // Only first storey in path will hold this reference. All children blocks will
 
2551
                                // reference it indirectly using $ref field
 
2552
                                //
 
2553
                                for (Block b = Original.Explicit; b != null; b = b.Parent) {
 
2554
                                        if (b.Parent != null) {
 
2555
                                                var s = b.Parent.Explicit.AnonymousMethodStorey;
 
2556
                                                if (s != null) {
 
2557
                                                        storey.HoistedThis = s.HoistedThis;
 
2558
                                                        break;
 
2559
                                                }
 
2560
                                        }
 
2561
 
 
2562
                                        if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
 
2563
                                                storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
 
2564
 
 
2565
                                                if (storey.HoistedThis != null)
 
2566
                                                        break;
 
2567
                                        }
 
2568
                                }
 
2569
                                
 
2570
                                //
 
2571
                                // We are the first storey on path and 'this' has to be hoisted
 
2572
                                //
 
2573
                                if (storey.HoistedThis == null) {
 
2574
                                        foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
 
2575
                                                //
 
2576
                                                // ThisReferencesFromChildrenBlock holds all reference even if they
 
2577
                                                // are not on this path. It saves some memory otherwise it'd have to
 
2578
                                                // be in every explicit block. We run this check to see if the reference
 
2579
                                                // is valid for this storey
 
2580
                                                //
 
2581
                                                Block block_on_path = ref_block;
 
2582
                                                for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
 
2583
 
 
2584
                                                if (block_on_path == null)
 
2585
                                                        continue;
 
2586
 
 
2587
                                                if (storey.HoistedThis == null) {
 
2588
                                                        storey.AddCapturedThisField (ec);
 
2589
                                                }
 
2590
 
 
2591
                                                for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
 
2592
                                                        if (b.AnonymousMethodStorey != null) {
 
2593
                                                                b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
 
2594
                                                                b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
 
2595
 
 
2596
                                                                //
 
2597
                                                                // Stop propagation inside same top block
 
2598
                                                                //
 
2599
                                                                if (b.ParametersBlock == ParametersBlock.Original)
 
2600
                                                                        break;
 
2601
 
 
2602
                                                                b = b.ParametersBlock;
 
2603
                                                        }
 
2604
 
 
2605
                                                        var pb = b as ParametersBlock;
 
2606
                                                        if (pb != null && pb.StateMachine != null) {
 
2607
                                                                if (pb.StateMachine == storey)
 
2608
                                                                        break;
 
2609
 
 
2610
                                                                pb.StateMachine.AddParentStoreyReference (ec, storey);
 
2611
                                                        }
 
2612
                                                        
 
2613
                                                        b.HasCapturedVariable = true;
 
2614
                                                }
 
2615
                                        }
 
2616
                                }
 
2617
                        }
 
2618
 
 
2619
                        var ref_blocks = storey.ReferencesFromChildrenBlock;
 
2620
                        if (ref_blocks != null) {
 
2621
                                foreach (ExplicitBlock ref_block in ref_blocks) {
 
2622
                                        for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
 
2623
                                                if (b.AnonymousMethodStorey != null) {
 
2624
                                                        b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
 
2625
 
 
2626
                                                        //
 
2627
                                                        // Stop propagation inside same top block
 
2628
                                                        //
 
2629
                                                        if (b.ParametersBlock == ParametersBlock.Original)
 
2630
                                                                break;
 
2631
 
 
2632
                                                        b = b.ParametersBlock;
 
2633
                                                }
 
2634
 
 
2635
                                                var pb = b as ParametersBlock;
 
2636
                                                if (pb != null && pb.StateMachine != null) {
 
2637
                                                        if (pb.StateMachine == storey)
 
2638
                                                                break;
 
2639
 
 
2640
                                                        pb.StateMachine.AddParentStoreyReference (ec, storey);
 
2641
                                                }
 
2642
 
 
2643
                                                b.HasCapturedVariable = true;
 
2644
                                        }
 
2645
                                }
 
2646
                        }
 
2647
 
 
2648
                        storey.Define ();
 
2649
                        storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
 
2650
                }
 
2651
 
 
2652
                public void RegisterAsyncAwait ()
 
2653
                {
 
2654
                        var block = this;
 
2655
                        while ((block.flags & Flags.AwaitBlock) == 0) {
 
2656
                                block.flags |= Flags.AwaitBlock;
 
2657
 
 
2658
                                if (block is ParametersBlock)
 
2659
                                        return;
 
2660
 
 
2661
                                block = block.Parent.Explicit;
 
2662
                        }
 
2663
                }
 
2664
 
 
2665
                public void RegisterIteratorYield ()
 
2666
                {
 
2667
                        var block = this;
 
2668
                        while ((block.flags & Flags.YieldBlock) == 0) {
 
2669
                                block.flags |= Flags.YieldBlock;
 
2670
 
 
2671
                                if (block.Parent == null)
 
2672
                                        return;
 
2673
 
 
2674
                                block = block.Parent.Explicit;
 
2675
                        }
 
2676
                }
 
2677
 
 
2678
                public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
 
2679
                {
 
2680
                        tryBlock.statements = statements;
 
2681
                        statements = new List<Statement> (1);
 
2682
                        statements.Add (tf);
 
2683
                }
 
2684
        }
 
2685
 
 
2686
        //
 
2687
        // ParametersBlock was introduced to support anonymous methods
 
2688
        // and lambda expressions
 
2689
        // 
 
2690
        public class ParametersBlock : ExplicitBlock
 
2691
        {
 
2692
                public class ParameterInfo : INamedBlockVariable
 
2693
                {
 
2694
                        readonly ParametersBlock block;
 
2695
                        readonly int index;
 
2696
                        public VariableInfo VariableInfo;
 
2697
                        bool is_locked;
 
2698
 
 
2699
                        public ParameterInfo (ParametersBlock block, int index)
 
2700
                        {
 
2701
                                this.block = block;
 
2702
                                this.index = index;
 
2703
                        }
 
2704
 
 
2705
                        #region Properties
 
2706
 
 
2707
                        public ParametersBlock Block {
 
2708
                                get {
 
2709
                                        return block;
 
2710
                                }
 
2711
                        }
 
2712
 
 
2713
                        Block INamedBlockVariable.Block {
 
2714
                                get {
 
2715
                                        return block;
 
2716
                                }
 
2717
                        }
 
2718
 
 
2719
                        public bool IsDeclared {
 
2720
                                get {
 
2721
                                        return true;
 
2722
                                }
 
2723
                        }
 
2724
 
 
2725
                        public bool IsParameter {
 
2726
                                get {
 
2727
                                        return true;
 
2728
                                }
 
2729
                        }
 
2730
 
 
2731
                        public bool IsLocked {
 
2732
                                get {
 
2733
                                        return is_locked;
 
2734
                                }
 
2735
                                set {
 
2736
                                        is_locked = value;
 
2737
                                }
 
2738
                        }
 
2739
 
 
2740
                        public Location Location {
 
2741
                                get {
 
2742
                                        return Parameter.Location;
 
2743
                                }
 
2744
                        }
 
2745
 
 
2746
                        public Parameter Parameter {
 
2747
                                get {
 
2748
                                        return block.Parameters [index];
 
2749
                                }
 
2750
                        }
 
2751
 
 
2752
                        public TypeSpec ParameterType {
 
2753
                                get {
 
2754
                                        return Parameter.Type;
 
2755
                                }
 
2756
                        }
 
2757
 
 
2758
                        #endregion
 
2759
 
 
2760
                        public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
 
2761
                        {
 
2762
                                return new ParameterReference (this, loc);
 
2763
                        }
 
2764
                }
 
2765
 
 
2766
                // 
 
2767
                // Block is converted into an expression
 
2768
                //
 
2769
                sealed class BlockScopeExpression : Expression
 
2770
                {
 
2771
                        Expression child;
 
2772
                        readonly ParametersBlock block;
 
2773
 
 
2774
                        public BlockScopeExpression (Expression child, ParametersBlock block)
 
2775
                        {
 
2776
                                this.child = child;
 
2777
                                this.block = block;
 
2778
                        }
 
2779
 
 
2780
                        public override bool ContainsEmitWithAwait ()
 
2781
                        {
 
2782
                                return child.ContainsEmitWithAwait ();
 
2783
                        }
 
2784
 
 
2785
                        public override Expression CreateExpressionTree (ResolveContext ec)
 
2786
                        {
 
2787
                                throw new NotSupportedException ();
 
2788
                        }
 
2789
 
 
2790
                        protected override Expression DoResolve (ResolveContext ec)
 
2791
                        {
 
2792
                                if (child == null)
 
2793
                                        return null;
 
2794
 
 
2795
                                child = child.Resolve (ec);
 
2796
                                if (child == null)
 
2797
                                        return null;
 
2798
 
 
2799
                                eclass = child.eclass;
 
2800
                                type = child.Type;
 
2801
                                return this;
 
2802
                        }
 
2803
 
 
2804
                        public override void Emit (EmitContext ec)
 
2805
                        {
 
2806
                                block.EmitScopeInitializers (ec);
 
2807
                                child.Emit (ec);
 
2808
                        }
 
2809
                }
 
2810
 
 
2811
                protected ParametersCompiled parameters;
 
2812
                protected ParameterInfo[] parameter_info;
 
2813
                bool resolved;
 
2814
                protected bool unreachable;
 
2815
                protected ToplevelBlock top_block;
 
2816
                protected StateMachine state_machine;
 
2817
 
 
2818
                public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
 
2819
                        : base (parent, 0, start, start)
 
2820
                {
 
2821
                        if (parameters == null)
 
2822
                                throw new ArgumentNullException ("parameters");
 
2823
 
 
2824
                        this.parameters = parameters;
 
2825
                        ParametersBlock = this;
 
2826
 
 
2827
                        flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
 
2828
 
 
2829
                        this.top_block = parent.ParametersBlock.top_block;
 
2830
                        ProcessParameters ();
 
2831
                }
 
2832
 
 
2833
                protected ParametersBlock (ParametersCompiled parameters, Location start)
 
2834
                        : base (null, 0, start, start)
 
2835
                {
 
2836
                        if (parameters == null)
 
2837
                                throw new ArgumentNullException ("parameters");
 
2838
 
 
2839
                        this.parameters = parameters;
 
2840
                        ParametersBlock = this;
 
2841
                }
 
2842
 
 
2843
                //
 
2844
                // It's supposed to be used by method body implementation of anonymous methods
 
2845
                //
 
2846
                protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
 
2847
                        : base (null, 0, source.StartLocation, source.EndLocation)
 
2848
                {
 
2849
                        this.parameters = parameters;
 
2850
                        this.statements = source.statements;
 
2851
                        this.scope_initializers = source.scope_initializers;
 
2852
 
 
2853
                        this.resolved = true;
 
2854
                        this.unreachable = source.unreachable;
 
2855
                        this.am_storey = source.am_storey;
 
2856
                        this.state_machine = source.state_machine;
 
2857
 
 
2858
                        ParametersBlock = this;
 
2859
 
 
2860
                        //
 
2861
                        // Overwrite original for comparison purposes when linking cross references
 
2862
                        // between anonymous methods
 
2863
                        //
 
2864
                        Original = source.Original;
 
2865
                }
 
2866
 
 
2867
                #region Properties
 
2868
 
 
2869
                public bool IsAsync {
 
2870
                        get {
 
2871
                                return (flags & Flags.HasAsyncModifier) != 0;
 
2872
                        }
 
2873
                        set {
 
2874
                                flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
 
2875
                        }
 
2876
                }
 
2877
 
 
2878
                //
 
2879
                // Block has been converted to expression tree
 
2880
                //
 
2881
                public bool IsExpressionTree {
 
2882
                        get {
 
2883
                                return (flags & Flags.IsExpressionTree) != 0;
 
2884
                        }
 
2885
                }
 
2886
 
 
2887
                //
 
2888
                // The parameters for the block.
 
2889
                //
 
2890
                public ParametersCompiled Parameters {
 
2891
                        get {
 
2892
                                return parameters;
 
2893
                        }
 
2894
                }
 
2895
 
 
2896
                public StateMachine StateMachine {
 
2897
                        get {
 
2898
                                return state_machine;
 
2899
                        }
 
2900
                }
 
2901
 
 
2902
                public ToplevelBlock TopBlock {
 
2903
                        get {
 
2904
                                return top_block;
 
2905
                        }
 
2906
                }
 
2907
 
 
2908
                public bool Resolved {
 
2909
                        get {
 
2910
                                return (flags & Flags.Resolved) != 0;
 
2911
                        }
 
2912
                }
 
2913
 
 
2914
                public int TemporaryLocalsCount { get; set; }
 
2915
 
 
2916
                #endregion
 
2917
 
 
2918
                // <summary>
 
2919
                //   Check whether all `out' parameters have been assigned.
 
2920
                // </summary>
 
2921
                public void CheckOutParameters (FlowBranching.UsageVector vector)
 
2922
                {
 
2923
                        if (vector.IsUnreachable)
 
2924
                                return;
 
2925
 
 
2926
                        int n = parameter_info == null ? 0 : parameter_info.Length;
 
2927
 
 
2928
                        for (int i = 0; i < n; i++) {
 
2929
                                VariableInfo var = parameter_info[i].VariableInfo;
 
2930
 
 
2931
                                if (var == null)
 
2932
                                        continue;
 
2933
 
 
2934
                                if (vector.IsAssigned (var, false))
 
2935
                                        continue;
 
2936
 
 
2937
                                var p = parameter_info[i].Parameter;
 
2938
                                TopBlock.Report.Error (177, p.Location,
 
2939
                                        "The out parameter `{0}' must be assigned to before control leaves the current method",
 
2940
                                        p.Name);
 
2941
                        }
 
2942
                }
 
2943
 
 
2944
                public override Expression CreateExpressionTree (ResolveContext ec)
 
2945
                {
 
2946
                        if (statements.Count == 1) {
 
2947
                                Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
 
2948
                                if (scope_initializers != null)
 
2949
                                        expr = new BlockScopeExpression (expr, this);
 
2950
 
 
2951
                                return expr;
 
2952
                        }
 
2953
 
 
2954
                        return base.CreateExpressionTree (ec);
 
2955
                }
 
2956
 
 
2957
                public override void Emit (EmitContext ec)
 
2958
                {
 
2959
                        if (state_machine != null && state_machine.OriginalSourceBlock != this) {
 
2960
                                DefineStoreyContainer (ec, state_machine);
 
2961
                                state_machine.EmitStoreyInstantiation (ec, this);
 
2962
                        }
 
2963
 
 
2964
                        base.Emit (ec);
 
2965
                }
 
2966
 
 
2967
                public void EmitEmbedded (EmitContext ec)
 
2968
                {
 
2969
                        if (state_machine != null && state_machine.OriginalSourceBlock != this) {
 
2970
                                DefineStoreyContainer (ec, state_machine);
 
2971
                                state_machine.EmitStoreyInstantiation (ec, this);
 
2972
                        }
 
2973
 
 
2974
                        base.Emit (ec);
 
2975
                }
 
2976
 
 
2977
                public ParameterInfo GetParameterInfo (Parameter p)
 
2978
                {
 
2979
                        for (int i = 0; i < parameters.Count; ++i) {
 
2980
                                if (parameters[i] == p)
 
2981
                                        return parameter_info[i];
 
2982
                        }
 
2983
 
 
2984
                        throw new ArgumentException ("Invalid parameter");
 
2985
                }
 
2986
 
 
2987
                public ParameterReference GetParameterReference (int index, Location loc)
 
2988
                {
 
2989
                        return new ParameterReference (parameter_info[index], loc);
 
2990
                }
 
2991
 
 
2992
                public Statement PerformClone ()
 
2993
                {
 
2994
                        CloneContext clonectx = new CloneContext ();
 
2995
                        return Clone (clonectx);
 
2996
                }
 
2997
 
 
2998
                protected void ProcessParameters ()
 
2999
                {
 
3000
                        if (parameters.Count == 0)
 
3001
                                return;
 
3002
 
 
3003
                        parameter_info = new ParameterInfo[parameters.Count];
 
3004
                        for (int i = 0; i < parameter_info.Length; ++i) {
 
3005
                                var p = parameters.FixedParameters[i];
 
3006
                                if (p == null)
 
3007
                                        continue;
 
3008
 
 
3009
                                // TODO: Should use Parameter only and more block there
 
3010
                                parameter_info[i] = new ParameterInfo (this, i);
 
3011
                                if (p.Name != null)
 
3012
                                        AddLocalName (p.Name, parameter_info[i]);
 
3013
                        }
 
3014
                }
 
3015
 
 
3016
                public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
 
3017
                {
 
3018
                        if (resolved)
 
3019
                                return true;
 
3020
 
 
3021
                        resolved = true;
 
3022
 
 
3023
                        if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
 
3024
                                flags |= Flags.IsExpressionTree;
 
3025
 
 
3026
                        try {
 
3027
                                ResolveMeta (rc);
 
3028
 
 
3029
                                using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
 
3030
                                        FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
 
3031
 
 
3032
                                        if (!Resolve (rc))
 
3033
                                                return false;
 
3034
 
 
3035
                                        unreachable = top_level.End ();
 
3036
                                }
 
3037
                        } catch (Exception e) {
 
3038
                                if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
 
3039
                                        throw;
 
3040
 
 
3041
                                if (rc.CurrentBlock != null) {
 
3042
                                        rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
 
3043
                                } else {
 
3044
                                        rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
 
3045
                                }
 
3046
 
 
3047
                                if (rc.Module.Compiler.Settings.DebugFlags > 0)
 
3048
                                        throw;
 
3049
                        }
 
3050
 
 
3051
                        if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
 
3052
                                if (rc.CurrentAnonymousMethod == null) {
 
3053
                                        // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
 
3054
                                        if (md is StateMachineMethod) {
 
3055
                                                unreachable = true;
 
3056
                                        } else {
 
3057
                                                rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
 
3058
                                                return false;
 
3059
                                        }
 
3060
                                } else {
 
3061
                                        //
 
3062
                                        // If an asynchronous body of F is either an expression classified as nothing, or a 
 
3063
                                        // statement block where no return statements have expressions, the inferred return type is Task
 
3064
                                        //
 
3065
                                        if (IsAsync) {
 
3066
                                                var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
 
3067
                                                if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
 
3068
                                                        am.ReturnTypeInference = null;
 
3069
                                                        am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
 
3070
                                                        return true;
 
3071
                                                }
 
3072
                                        }
 
3073
 
 
3074
                                        rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
 
3075
                                                          rc.CurrentAnonymousMethod.GetSignatureForError ());
 
3076
                                        return false;
 
3077
                                }
 
3078
                        }
 
3079
 
 
3080
                        return true;
 
3081
                }
 
3082
 
 
3083
                void ResolveMeta (BlockContext ec)
 
3084
                {
 
3085
                        int orig_count = parameters.Count;
 
3086
 
 
3087
                        for (int i = 0; i < orig_count; ++i) {
 
3088
                                Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
 
3089
 
 
3090
                                if ((mod & Parameter.Modifier.OUT) == 0)
 
3091
                                        continue;
 
3092
 
 
3093
                                VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
 
3094
                                parameter_info[i].VariableInfo = vi;
 
3095
                                ec.FlowOffset += vi.Length;
 
3096
                        }
 
3097
                }
 
3098
 
 
3099
                public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
 
3100
                {
 
3101
                        var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
 
3102
                        var stateMachine = new IteratorStorey (iterator);
 
3103
 
 
3104
                        state_machine = stateMachine;
 
3105
                        iterator.SetStateMachine (stateMachine);
 
3106
 
 
3107
                        var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
 
3108
                        tlb.Original = this;
 
3109
                        tlb.IsCompilerGenerated = true;
 
3110
                        tlb.state_machine = stateMachine;
 
3111
                        tlb.AddStatement (new Return (iterator, iterator.Location));
 
3112
                        return tlb;
 
3113
                }
 
3114
 
 
3115
                public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, Location loc)
 
3116
                {
 
3117
                        for (int i = 0; i < parameters.Count; i++) {
 
3118
                                Parameter p = parameters[i];
 
3119
                                Parameter.Modifier mod = p.ModFlags;
 
3120
                                if ((mod & Parameter.Modifier.RefOutMask) != 0) {
 
3121
                                        host.Compiler.Report.Error (1988, p.Location,
 
3122
                                                "Async methods cannot have ref or out parameters");
 
3123
                                        return this;
 
3124
                                }
 
3125
 
 
3126
                                if (p is ArglistParameter) {
 
3127
                                        host.Compiler.Report.Error (4006, p.Location,
 
3128
                                                "__arglist is not allowed in parameter list of async methods");
 
3129
                                        return this;
 
3130
                                }
 
3131
 
 
3132
                                if (parameters.Types[i].IsPointer) {
 
3133
                                        host.Compiler.Report.Error (4005, p.Location,
 
3134
                                                "Async methods cannot have unsafe parameters");
 
3135
                                        return this;
 
3136
                                }
 
3137
                        }
 
3138
 
 
3139
                        if (!HasAwait) {
 
3140
                                host.Compiler.Report.Warning (1998, 1, loc,
 
3141
                                        "Async block lacks `await' operator and will run synchronously");
 
3142
                        }
 
3143
 
 
3144
                        var block_type = host.Module.Compiler.BuiltinTypes.Void;
 
3145
                        var initializer = new AsyncInitializer (this, host, block_type);
 
3146
                        initializer.Type = block_type;
 
3147
 
 
3148
                        var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
 
3149
 
 
3150
                        state_machine = stateMachine;
 
3151
                        initializer.SetStateMachine (stateMachine);
 
3152
 
 
3153
                        var b = this is ToplevelBlock ?
 
3154
                                new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
 
3155
                                new ParametersBlock (Parent, parameters, Location.Null) {
 
3156
                                        IsAsync = true,
 
3157
                                };
 
3158
 
 
3159
                        b.Original = this;
 
3160
                        b.IsCompilerGenerated = true;
 
3161
                        b.state_machine = stateMachine;
 
3162
                        b.AddStatement (new StatementExpression (initializer));
 
3163
                        return b;
 
3164
                }
 
3165
        }
 
3166
 
 
3167
        //
 
3168
        //
 
3169
        //
 
3170
        public class ToplevelBlock : ParametersBlock
 
3171
        {
 
3172
                LocalVariable this_variable;
 
3173
                CompilerContext compiler;
 
3174
                Dictionary<string, object> names;
 
3175
                Dictionary<string, object> labels;
 
3176
 
 
3177
                List<ExplicitBlock> this_references;
 
3178
 
 
3179
                public ToplevelBlock (CompilerContext ctx, Location loc)
 
3180
                        : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
 
3181
                {
 
3182
                }
 
3183
 
 
3184
                public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
 
3185
                        : base (parameters, start)
 
3186
                {
 
3187
                        this.compiler = ctx;
 
3188
                        top_block = this;
 
3189
                        flags |= Flags.HasRet;
 
3190
 
 
3191
                        ProcessParameters ();
 
3192
                }
 
3193
 
 
3194
                //
 
3195
                // Recreates a top level block from parameters block. Used for
 
3196
                // compiler generated methods where the original block comes from
 
3197
                // explicit child block. This works for already resolved blocks
 
3198
                // only to ensure we resolve them in the correct flow order
 
3199
                //
 
3200
                public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
 
3201
                        : base (source, parameters)
 
3202
                {
 
3203
                        this.compiler = source.TopBlock.compiler;
 
3204
                        top_block = this;
 
3205
                        flags |= Flags.HasRet;
 
3206
                }
 
3207
 
 
3208
                public bool IsIterator {
 
3209
                        get {
 
3210
                                return HasYield;
 
3211
                        }
 
3212
                }
 
3213
 
 
3214
                public Report Report {
 
3215
                        get {
 
3216
                                return compiler.Report;
 
3217
                        }
 
3218
                }
 
3219
 
 
3220
                //
 
3221
                // Used by anonymous blocks to track references of `this' variable
 
3222
                //
 
3223
                public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
 
3224
                        get {
 
3225
                                return this_references;
 
3226
                        }
 
3227
                }
 
3228
 
 
3229
                //
 
3230
                // Returns the "this" instance variable of this block.
 
3231
                // See AddThisVariable() for more information.
 
3232
                //
 
3233
                public LocalVariable ThisVariable {
 
3234
                        get {
 
3235
                                return this_variable;
 
3236
                        }
 
3237
                }
 
3238
 
 
3239
                public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
 
3240
                {
 
3241
                        if (names == null)
 
3242
                                names = new Dictionary<string, object> ();
 
3243
 
 
3244
                        object value;
 
3245
                        if (!names.TryGetValue (name, out value)) {
 
3246
                                names.Add (name, li);
 
3247
                                return;
 
3248
                        }
 
3249
 
 
3250
                        INamedBlockVariable existing = value as INamedBlockVariable;
 
3251
                        List<INamedBlockVariable> existing_list;
 
3252
                        if (existing != null) {
 
3253
                                existing_list = new List<INamedBlockVariable> ();
 
3254
                                existing_list.Add (existing);
 
3255
                                names[name] = existing_list;
 
3256
                        } else {
 
3257
                                existing_list = (List<INamedBlockVariable>) value;
 
3258
                        }
 
3259
 
 
3260
                        //
 
3261
                        // A collision checking between local names
 
3262
                        //
 
3263
                        var variable_block = li.Block.Explicit;
 
3264
                        for (int i = 0; i < existing_list.Count; ++i) {
 
3265
                                existing = existing_list[i];
 
3266
                                Block b = existing.Block.Explicit;
 
3267
 
 
3268
                                // Collision at same level
 
3269
                                if (variable_block == b) {
 
3270
                                        li.Block.Error_AlreadyDeclared (name, li);
 
3271
                                        break;
 
3272
                                }
 
3273
 
 
3274
                                // Collision with parent
 
3275
                                Block parent = variable_block;
 
3276
                                while ((parent = parent.Parent) != null) {
 
3277
                                        if (parent == b) {
 
3278
                                                li.Block.Error_AlreadyDeclared (name, li, "parent or current");
 
3279
                                                i = existing_list.Count;
 
3280
                                                break;
 
3281
                                        }
 
3282
                                }
 
3283
 
 
3284
                                if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
 
3285
                                        // Collision with children
 
3286
                                        while ((b = b.Parent) != null) {
 
3287
                                                if (variable_block == b) {
 
3288
                                                        li.Block.Error_AlreadyDeclared (name, li, "child");
 
3289
                                                        i = existing_list.Count;
 
3290
                                                        break;
 
3291
                                                }
 
3292
                                        }
 
3293
                                }
 
3294
                        }
 
3295
 
 
3296
                        existing_list.Add (li);
 
3297
                }
 
3298
 
 
3299
                public void AddLabel (string name, LabeledStatement label)
 
3300
                {
 
3301
                        if (labels == null)
 
3302
                                labels = new Dictionary<string, object> ();
 
3303
 
 
3304
                        object value;
 
3305
                        if (!labels.TryGetValue (name, out value)) {
 
3306
                                labels.Add (name, label);
 
3307
                                return;
 
3308
                        }
 
3309
 
 
3310
                        LabeledStatement existing = value as LabeledStatement;
 
3311
                        List<LabeledStatement> existing_list;
 
3312
                        if (existing != null) {
 
3313
                                existing_list = new List<LabeledStatement> ();
 
3314
                                existing_list.Add (existing);
 
3315
                                labels[name] = existing_list;
 
3316
                        } else {
 
3317
                                existing_list = (List<LabeledStatement>) value;
 
3318
                        }
 
3319
 
 
3320
                        //
 
3321
                        // A collision checking between labels
 
3322
                        //
 
3323
                        for (int i = 0; i < existing_list.Count; ++i) {
 
3324
                                existing = existing_list[i];
 
3325
                                Block b = existing.Block;
 
3326
 
 
3327
                                // Collision at same level
 
3328
                                if (label.Block == b) {
 
3329
                                        Report.SymbolRelatedToPreviousError (existing.loc, name);
 
3330
                                        Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
 
3331
                                        break;
 
3332
                                }
 
3333
 
 
3334
                                // Collision with parent
 
3335
                                b = label.Block;
 
3336
                                while ((b = b.Parent) != null) {
 
3337
                                        if (existing.Block == b) {
 
3338
                                                Report.Error (158, label.loc,
 
3339
                                                        "The label `{0}' shadows another label by the same name in a contained scope", name);
 
3340
                                                i = existing_list.Count;
 
3341
                                                break;
 
3342
                                        }
 
3343
                                }
 
3344
 
 
3345
                                // Collision with with children
 
3346
                                b = existing.Block;
 
3347
                                while ((b = b.Parent) != null) {
 
3348
                                        if (label.Block == b) {
 
3349
                                                Report.Error (158, label.loc,
 
3350
                                                        "The label `{0}' shadows another label by the same name in a contained scope", name);
 
3351
                                                i = existing_list.Count;
 
3352
                                                break;
 
3353
                                        }
 
3354
                                }
 
3355
                        }
 
3356
 
 
3357
                        existing_list.Add (label);
 
3358
                }
 
3359
 
 
3360
                public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
 
3361
                {
 
3362
                        if (this_references == null)
 
3363
                                this_references = new List<ExplicitBlock> ();
 
3364
 
 
3365
                        if (!this_references.Contains (block))
 
3366
                                this_references.Add (block);
 
3367
                }
 
3368
 
 
3369
                public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
 
3370
                {
 
3371
                        this_references.Remove (block);
 
3372
                }
 
3373
 
 
3374
                //
 
3375
                // Creates an arguments set from all parameters, useful for method proxy calls
 
3376
                //
 
3377
                public Arguments GetAllParametersArguments ()
 
3378
                {
 
3379
                        int count = parameters.Count;
 
3380
                        Arguments args = new Arguments (count);
 
3381
                        for (int i = 0; i < count; ++i) {
 
3382
                                var arg_expr = GetParameterReference (i, parameter_info[i].Location);
 
3383
                                args.Add (new Argument (arg_expr));
 
3384
                        }
 
3385
 
 
3386
                        return args;
 
3387
                }
 
3388
 
 
3389
                //
 
3390
                // Lookup inside a block, the returned value can represent 3 states
 
3391
                //
 
3392
                // true+variable: A local name was found and it's valid
 
3393
                // false+variable: A local name was found in a child block only
 
3394
                // false+null: No local name was found
 
3395
                //
 
3396
                public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
 
3397
                {
 
3398
                        if (names == null)
 
3399
                                return false;
 
3400
 
 
3401
                        object value;
 
3402
                        if (!names.TryGetValue (name, out value))
 
3403
                                return false;
 
3404
 
 
3405
                        variable = value as INamedBlockVariable;
 
3406
                        Block b = block;
 
3407
                        if (variable != null) {
 
3408
                                do {
 
3409
                                        if (variable.Block == b.Original)
 
3410
                                                return true;
 
3411
 
 
3412
                                        b = b.Parent;
 
3413
                                } while (b != null);
 
3414
 
 
3415
                                b = variable.Block;
 
3416
                                do {
 
3417
                                        if (block == b)
 
3418
                                                return false;
 
3419
 
 
3420
                                        b = b.Parent;
 
3421
                                } while (b != null);
 
3422
                        } else {
 
3423
                                List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
 
3424
                                for (int i = 0; i < list.Count; ++i) {
 
3425
                                        variable = list[i];
 
3426
                                        do {
 
3427
                                                if (variable.Block == b.Original)
 
3428
                                                        return true;
 
3429
 
 
3430
                                                b = b.Parent;
 
3431
                                        } while (b != null);
 
3432
 
 
3433
                                        b = variable.Block;
 
3434
                                        do {
 
3435
                                                if (block == b)
 
3436
                                                        return false;
 
3437
 
 
3438
                                                b = b.Parent;
 
3439
                                        } while (b != null);
 
3440
 
 
3441
                                        b = block;
 
3442
                                }
 
3443
                        }
 
3444
 
 
3445
                        variable = null;
 
3446
                        return false;
 
3447
                }
 
3448
 
 
3449
                public LabeledStatement GetLabel (string name, Block block)
 
3450
                {
 
3451
                        if (labels == null)
 
3452
                                return null;
 
3453
 
 
3454
                        object value;
 
3455
                        if (!labels.TryGetValue (name, out value)) {
 
3456
                                return null;
 
3457
                        }
 
3458
 
 
3459
                        var label = value as LabeledStatement;
 
3460
                        Block b = block;
 
3461
                        if (label != null) {
 
3462
                                if (label.Block == b.Original)
 
3463
                                        return label;
 
3464
                        } else {
 
3465
                                List<LabeledStatement> list = (List<LabeledStatement>) value;
 
3466
                                for (int i = 0; i < list.Count; ++i) {
 
3467
                                        label = list[i];
 
3468
                                        if (label.Block == b.Original)
 
3469
                                                return label;
 
3470
                                }
 
3471
                        }
 
3472
                                
 
3473
                        return null;
 
3474
                }
 
3475
 
 
3476
                // <summary>
 
3477
                //   This is used by non-static `struct' constructors which do not have an
 
3478
                //   initializer - in this case, the constructor must initialize all of the
 
3479
                //   struct's fields.  To do this, we add a "this" variable and use the flow
 
3480
                //   analysis code to ensure that it's been fully initialized before control
 
3481
                //   leaves the constructor.
 
3482
                // </summary>
 
3483
                public void AddThisVariable (BlockContext bc)
 
3484
                {
 
3485
                        if (this_variable != null)
 
3486
                                throw new InternalErrorException (StartLocation.ToString ());
 
3487
 
 
3488
                        this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
 
3489
                        this_variable.Type = bc.CurrentType;
 
3490
                        this_variable.PrepareForFlowAnalysis (bc);
 
3491
                }
 
3492
 
 
3493
                public bool IsThisAssigned (BlockContext ec)
 
3494
                {
 
3495
                        return this_variable == null || this_variable.IsThisAssigned (ec, this);
 
3496
                }
 
3497
 
 
3498
                public override void Emit (EmitContext ec)
 
3499
                {
 
3500
                        if (Report.Errors > 0)
 
3501
                                return;
 
3502
 
 
3503
                        try {
 
3504
                        if (IsCompilerGenerated) {
 
3505
                                using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
 
3506
                                        base.Emit (ec);
 
3507
                                }
 
3508
                        } else {
 
3509
                                base.Emit (ec);
 
3510
                        }
 
3511
 
 
3512
                        //
 
3513
                        // If `HasReturnLabel' is set, then we already emitted a
 
3514
                        // jump to the end of the method, so we must emit a `ret'
 
3515
                        // there.
 
3516
                        //
 
3517
                        // Unfortunately, System.Reflection.Emit automatically emits
 
3518
                        // a leave to the end of a finally block.  This is a problem
 
3519
                        // if no code is following the try/finally block since we may
 
3520
                        // jump to a point after the end of the method.
 
3521
                        // As a workaround, we're always creating a return label in
 
3522
                        // this case.
 
3523
                        //
 
3524
                        if (ec.HasReturnLabel || !unreachable) {
 
3525
                                if (ec.HasReturnLabel)
 
3526
                                        ec.MarkLabel (ec.ReturnLabel);
 
3527
 
 
3528
                                if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
 
3529
                                        ec.Mark (EndLocation);
 
3530
 
 
3531
                                if (ec.ReturnType.Kind != MemberKind.Void)
 
3532
                                        ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
 
3533
 
 
3534
                                ec.Emit (OpCodes.Ret);
 
3535
                        }
 
3536
 
 
3537
                        } catch (Exception e) {
 
3538
                                throw new InternalErrorException (e, StartLocation);
 
3539
                        }
 
3540
                }
 
3541
        }
 
3542
        
 
3543
        public class SwitchLabel : Statement
 
3544
        {
 
3545
                Expression label;
 
3546
                Constant converted;
 
3547
 
 
3548
                Label? il_label;
 
3549
 
 
3550
                //
 
3551
                // if expr == null, then it is the default case.
 
3552
                //
 
3553
                public SwitchLabel (Expression expr, Location l)
 
3554
                {
 
3555
                        label = expr;
 
3556
                        loc = l;
 
3557
                }
 
3558
 
 
3559
                public bool IsDefault {
 
3560
                        get {
 
3561
                                return label == null;
 
3562
                        }
 
3563
                }
 
3564
 
 
3565
                public Expression Label {
 
3566
                        get {
 
3567
                                return label;
 
3568
                        }
 
3569
                }
 
3570
 
 
3571
                public Location Location {
 
3572
                        get {
 
3573
                                return loc;
 
3574
                        }
 
3575
                }
 
3576
 
 
3577
                public Constant Converted {
 
3578
                        get {
 
3579
                                return converted;
 
3580
                        }
 
3581
                        set {
 
3582
                                converted = value;
 
3583
                        }
 
3584
                }
 
3585
 
 
3586
                public bool SectionStart { get; set; }
 
3587
 
 
3588
                public Label GetILLabel (EmitContext ec)
 
3589
                {
 
3590
                        if (il_label == null){
 
3591
                                il_label = ec.DefineLabel ();
 
3592
                        }
 
3593
 
 
3594
                        return il_label.Value;
 
3595
                }
 
3596
 
 
3597
                protected override void DoEmit (EmitContext ec)
 
3598
                {
 
3599
                        ec.MarkLabel (GetILLabel (ec));
 
3600
                }
 
3601
 
 
3602
                public override bool Resolve (BlockContext bc)
 
3603
                {
 
3604
                        bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
 
3605
 
 
3606
                        return base.Resolve (bc);
 
3607
                }
 
3608
 
 
3609
                //
 
3610
                // Resolves the expression, reduces it to a literal if possible
 
3611
                // and then converts it to the requested type.
 
3612
                //
 
3613
                public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
 
3614
                {       
 
3615
                        Expression e = label.Resolve (ec);
 
3616
 
 
3617
                        if (e == null)
 
3618
                                return false;
 
3619
 
 
3620
                        Constant c = e as Constant;
 
3621
                        if (c == null){
 
3622
                                ec.Report.Error (150, loc, "A constant value is expected");
 
3623
                                return false;
 
3624
                        }
 
3625
 
 
3626
                        if (allow_nullable && c is NullLiteral) {
 
3627
                                converted = c;
 
3628
                                return true;
 
3629
                        }
 
3630
 
 
3631
                        converted = c.ImplicitConversionRequired (ec, required_type, loc);
 
3632
                        return converted != null;
 
3633
                }
 
3634
 
 
3635
                public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
 
3636
                {
 
3637
                        string label;
 
3638
                        if (converted == null)
 
3639
                                label = "default";
 
3640
                        else
 
3641
                                label = converted.GetValueAsLiteral ();
 
3642
                        
 
3643
                        ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
 
3644
                        ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
 
3645
                }
 
3646
 
 
3647
                protected override void CloneTo (CloneContext clonectx, Statement target)
 
3648
                {
 
3649
                        var t = (SwitchLabel) target;
 
3650
                        if (label != null)
 
3651
                                t.label = label.Clone (clonectx);
 
3652
                }
 
3653
 
 
3654
                public override object Accept (StructuralVisitor visitor)
 
3655
                {
 
3656
                        return visitor.Visit (this);
 
3657
                }
 
3658
        }
 
3659
 
 
3660
        public class Switch : Statement
 
3661
        {
 
3662
                // structure used to hold blocks of keys while calculating table switch
 
3663
                sealed class LabelsRange : IComparable<LabelsRange>
 
3664
                {
 
3665
                        public readonly long min;
 
3666
                        public long max;
 
3667
                        public readonly List<long> label_values;
 
3668
 
 
3669
                        public LabelsRange (long value)
 
3670
                        {
 
3671
                                min = max = value;
 
3672
                                label_values = new List<long> ();
 
3673
                                label_values.Add (value);
 
3674
                        }
 
3675
 
 
3676
                        public LabelsRange (long min, long max, ICollection<long> values)
 
3677
                        {
 
3678
                                this.min = min;
 
3679
                                this.max = max;
 
3680
                                this.label_values = new List<long> (values);
 
3681
                        }
 
3682
 
 
3683
                        public long Range {
 
3684
                                get {
 
3685
                                        return max - min + 1;
 
3686
                                }
 
3687
                        }
 
3688
 
 
3689
                        public bool AddValue (long value)
 
3690
                        {
 
3691
                                var gap = value - min + 1;
 
3692
                                // Ensure the range has > 50% occupancy
 
3693
                                if (gap > 2 * (label_values.Count + 1) || gap <= 0)
 
3694
                                        return false;
 
3695
 
 
3696
                                max = value;
 
3697
                                label_values.Add (value);
 
3698
                                return true;
 
3699
                        }
 
3700
 
 
3701
                        public int CompareTo (LabelsRange other)
 
3702
                        {
 
3703
                                int nLength = label_values.Count;
 
3704
                                int nLengthOther = other.label_values.Count;
 
3705
                                if (nLengthOther == nLength)
 
3706
                                        return (int) (other.min - min);
 
3707
 
 
3708
                                return nLength - nLengthOther;
 
3709
                        }
 
3710
                }
 
3711
 
 
3712
                sealed class DispatchStatement : Statement
 
3713
                {
 
3714
                        readonly Switch body;
 
3715
 
 
3716
                        public DispatchStatement (Switch body)
 
3717
                        {
 
3718
                                this.body = body;
 
3719
                        }
 
3720
 
 
3721
                        protected override void CloneTo (CloneContext clonectx, Statement target)
 
3722
                        {
 
3723
                                throw new NotImplementedException ();
 
3724
                        }
 
3725
 
 
3726
                        protected override void DoEmit (EmitContext ec)
 
3727
                        {
 
3728
                                body.EmitDispatch (ec);
 
3729
                        }
 
3730
                }
 
3731
 
 
3732
                public Expression Expr;
 
3733
 
 
3734
                //
 
3735
                // Mapping of all labels to their SwitchLabels
 
3736
                //
 
3737
                Dictionary<long, SwitchLabel> labels;
 
3738
                Dictionary<string, SwitchLabel> string_labels;
 
3739
                List<SwitchLabel> case_labels;
 
3740
 
 
3741
                /// <summary>
 
3742
                ///   The governing switch type
 
3743
                /// </summary>
 
3744
                public TypeSpec SwitchType;
 
3745
 
 
3746
                Expression new_expr;
 
3747
 
 
3748
                SwitchLabel case_null;
 
3749
                SwitchLabel case_default;
 
3750
 
 
3751
                Label defaultLabel, nullLabel;
 
3752
                VariableReference value;
 
3753
                ExpressionStatement string_dictionary;
 
3754
                FieldExpr switch_cache_field;
 
3755
                ExplicitBlock block;
 
3756
 
 
3757
                //
 
3758
                // Nullable Types support
 
3759
                //
 
3760
                Nullable.Unwrap unwrap;
 
3761
 
 
3762
                public Switch (Expression e, ExplicitBlock block, Location l)
 
3763
                {
 
3764
                        Expr = e;
 
3765
                        this.block = block;
 
3766
                        loc = l;
 
3767
                }
 
3768
 
 
3769
                public ExplicitBlock Block {
 
3770
                        get {
 
3771
                                return block;
 
3772
                        }
 
3773
                }
 
3774
 
 
3775
                public SwitchLabel DefaultLabel {
 
3776
                        get {
 
3777
                                return case_default;
 
3778
                        }
 
3779
                }
 
3780
 
 
3781
                public bool IsNullable {
 
3782
                        get {
 
3783
                                return unwrap != null;
 
3784
                        }
 
3785
                }
 
3786
 
 
3787
                //
 
3788
                // Determines the governing type for a switch.  The returned
 
3789
                // expression might be the expression from the switch, or an
 
3790
                // expression that includes any potential conversions to
 
3791
                //
 
3792
                Expression SwitchGoverningType (ResolveContext ec, Expression expr)
 
3793
                {
 
3794
                        switch (expr.Type.BuiltinType) {
 
3795
                        case BuiltinTypeSpec.Type.Byte:
 
3796
                        case BuiltinTypeSpec.Type.SByte:
 
3797
                        case BuiltinTypeSpec.Type.UShort:
 
3798
                        case BuiltinTypeSpec.Type.Short:
 
3799
                        case BuiltinTypeSpec.Type.UInt:
 
3800
                        case BuiltinTypeSpec.Type.Int:
 
3801
                        case BuiltinTypeSpec.Type.ULong:
 
3802
                        case BuiltinTypeSpec.Type.Long:
 
3803
                        case BuiltinTypeSpec.Type.Char:
 
3804
                        case BuiltinTypeSpec.Type.String:
 
3805
                        case BuiltinTypeSpec.Type.Bool:
 
3806
                                return expr;
 
3807
                        }
 
3808
 
 
3809
                        if (expr.Type.IsEnum)
 
3810
                                return expr;
 
3811
 
 
3812
                        //
 
3813
                        // Try to find a *user* defined implicit conversion.
 
3814
                        //
 
3815
                        // If there is no implicit conversion, or if there are multiple
 
3816
                        // conversions, we have to report an error
 
3817
                        //
 
3818
                        Expression converted = null;
 
3819
                        foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
 
3820
                                Expression e;
 
3821
                                
 
3822
                                e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
 
3823
                                if (e == null)
 
3824
                                        continue;
 
3825
 
 
3826
                                //
 
3827
                                // Ignore over-worked ImplicitUserConversions that do
 
3828
                                // an implicit conversion in addition to the user conversion.
 
3829
                                // 
 
3830
                                if (!(e is UserCast))
 
3831
                                        continue;
 
3832
 
 
3833
                                if (converted != null){
 
3834
                                        ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
 
3835
                                        return null;
 
3836
                                }
 
3837
 
 
3838
                                converted = e;
 
3839
                        }
 
3840
                        return converted;
 
3841
                }
 
3842
 
 
3843
                public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
 
3844
                {
 
3845
                        // LAMESPEC: For some reason it does not contain bool which looks like csc bug
 
3846
                        return new[] {
 
3847
                                types.SByte,
 
3848
                                types.Byte,
 
3849
                                types.Short,
 
3850
                                types.UShort,
 
3851
                                types.Int,
 
3852
                                types.UInt,
 
3853
                                types.Long,
 
3854
                                types.ULong,
 
3855
                                types.Char,
 
3856
                                types.String
 
3857
                        };
 
3858
                }
 
3859
 
 
3860
                //
 
3861
                // Performs the basic sanity checks on the switch statement
 
3862
                // (looks for duplicate keys and non-constant expressions).
 
3863
                //
 
3864
                // It also returns a hashtable with the keys that we will later
 
3865
                // use to compute the switch tables
 
3866
                //
 
3867
                bool ResolveLabels (ResolveContext ec, Constant value)
 
3868
                {
 
3869
                        bool error = false;
 
3870
                        if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
 
3871
                                string_labels = new Dictionary<string, SwitchLabel> ();
 
3872
                        } else {
 
3873
                                labels = new Dictionary<long, SwitchLabel> ();
 
3874
                        }
 
3875
 
 
3876
                        case_labels = new List<SwitchLabel> ();
 
3877
                        int default_label_index = -1;
 
3878
                        bool constant_label_found = false;
 
3879
 
 
3880
                        for (int i = 0; i < block.Statements.Count; ++i) {
 
3881
                                var s = block.Statements[i];
 
3882
 
 
3883
                                var sl = s as SwitchLabel;
 
3884
                                if (sl == null) {
 
3885
                                        continue;
 
3886
                                }
 
3887
 
 
3888
                                case_labels.Add (sl);
 
3889
 
 
3890
                                if (sl.IsDefault) {
 
3891
                                        if (case_default != null) {
 
3892
                                                sl.Error_AlreadyOccurs (ec, SwitchType, case_default);
 
3893
                                                error = true;
 
3894
                                        }
 
3895
 
 
3896
                                        default_label_index = i;
 
3897
                                        case_default = sl;
 
3898
                                        continue;
 
3899
                                }
 
3900
 
 
3901
                                if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
 
3902
                                        error = true;
 
3903
                                        continue;
 
3904
                                }
 
3905
 
 
3906
                                try {
 
3907
                                        if (string_labels != null) {
 
3908
                                                string string_value = sl.Converted.GetValue () as string;
 
3909
                                                if (string_value == null)
 
3910
                                                        case_null = sl;
 
3911
                                                else
 
3912
                                                        string_labels.Add (string_value, sl);
 
3913
                                        } else {
 
3914
                                                if (sl.Converted is NullLiteral) {
 
3915
                                                        case_null = sl;
 
3916
                                                } else {
 
3917
                                                        labels.Add (sl.Converted.GetValueAsLong (), sl);
 
3918
                                                }
 
3919
                                        }
 
3920
                                } catch (ArgumentException) {
 
3921
                                        if (string_labels != null)
 
3922
                                                sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
 
3923
                                        else
 
3924
                                                sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
 
3925
 
 
3926
                                        error = true;
 
3927
                                }
 
3928
 
 
3929
                                if (value != null) {
 
3930
                                        var constant_label = constant_label_found ? null : FindLabel (value);
 
3931
                                        if (constant_label == null || constant_label != sl)
 
3932
                                                block.Statements[i] = new EmptyStatement (s.loc);
 
3933
                                        else
 
3934
                                                constant_label_found = true;
 
3935
                                }
 
3936
                        }
 
3937
 
 
3938
                        if (value != null && constant_label_found && default_label_index >= 0)
 
3939
                                block.Statements[default_label_index] = new EmptyStatement (case_default.loc);
 
3940
 
 
3941
                        return !error;
 
3942
                }
 
3943
                
 
3944
                //
 
3945
                // This method emits code for a lookup-based switch statement (non-string)
 
3946
                // Basically it groups the cases into blocks that are at least half full,
 
3947
                // and then spits out individual lookup opcodes for each block.
 
3948
                // It emits the longest blocks first, and short blocks are just
 
3949
                // handled with direct compares.
 
3950
                //
 
3951
                void EmitTableSwitch (EmitContext ec, Expression val)
 
3952
                {
 
3953
                        if (labels != null && labels.Count > 0) {
 
3954
                                List<LabelsRange> ranges;
 
3955
                                if (string_labels != null) {
 
3956
                                        // We have done all hard work for string already
 
3957
                                        // setup single range only
 
3958
                                        ranges = new List<LabelsRange> (1);
 
3959
                                        ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
 
3960
                                } else {
 
3961
                                        var element_keys = new long[labels.Count];
 
3962
                                        labels.Keys.CopyTo (element_keys, 0);
 
3963
                                        Array.Sort (element_keys);
 
3964
 
 
3965
                                        //
 
3966
                                        // Build possible ranges of switch labes to reduce number
 
3967
                                        // of comparisons
 
3968
                                        //
 
3969
                                        ranges = new List<LabelsRange> (element_keys.Length);
 
3970
                                        var range = new LabelsRange (element_keys[0]);
 
3971
                                        ranges.Add (range);
 
3972
                                        for (int i = 1; i < element_keys.Length; ++i) {
 
3973
                                                var l = element_keys[i];
 
3974
                                                if (range.AddValue (l))
 
3975
                                                        continue;
 
3976
 
 
3977
                                                range = new LabelsRange (l);
 
3978
                                                ranges.Add (range);
 
3979
                                        }
 
3980
 
 
3981
                                        // sort the blocks so we can tackle the largest ones first
 
3982
                                        ranges.Sort ();
 
3983
                                }
 
3984
 
 
3985
                                Label lbl_default = defaultLabel;
 
3986
                                TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
 
3987
 
 
3988
                                for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
 
3989
                                        LabelsRange kb = ranges[range_index];
 
3990
                                        lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
 
3991
 
 
3992
                                        // Optimize small ranges using simple equality check
 
3993
                                        if (kb.Range <= 2) {
 
3994
                                                foreach (var key in kb.label_values) {
 
3995
                                                        SwitchLabel sl = labels[key];
 
3996
                                                        if (sl == case_default || sl == case_null)
 
3997
                                                                continue;
 
3998
 
 
3999
                                                        if (sl.Converted.IsZeroInteger) {
 
4000
                                                                val.EmitBranchable (ec, sl.GetILLabel (ec), false);
 
4001
                                                        } else {
 
4002
                                                                val.Emit (ec);
 
4003
                                                                sl.Converted.Emit (ec);
 
4004
                                                                ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
 
4005
                                                        }
 
4006
                                                }
 
4007
                                        } else {
 
4008
                                                // TODO: if all the keys in the block are the same and there are
 
4009
                                                //       no gaps/defaults then just use a range-check.
 
4010
                                                if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
 
4011
                                                        // TODO: optimize constant/I4 cases
 
4012
 
 
4013
                                                        // check block range (could be > 2^31)
 
4014
                                                        val.Emit (ec);
 
4015
                                                        ec.EmitLong (kb.min);
 
4016
                                                        ec.Emit (OpCodes.Blt, lbl_default);
 
4017
 
 
4018
                                                        val.Emit (ec);
 
4019
                                                        ec.EmitLong (kb.max);
 
4020
                                                        ec.Emit (OpCodes.Bgt, lbl_default);
 
4021
 
 
4022
                                                        // normalize range
 
4023
                                                        val.Emit (ec);
 
4024
                                                        if (kb.min != 0) {
 
4025
                                                                ec.EmitLong (kb.min);
 
4026
                                                                ec.Emit (OpCodes.Sub);
 
4027
                                                        }
 
4028
 
 
4029
                                                        ec.Emit (OpCodes.Conv_I4);      // assumes < 2^31 labels!
 
4030
                                                } else {
 
4031
                                                        // normalize range
 
4032
                                                        val.Emit (ec);
 
4033
                                                        int first = (int) kb.min;
 
4034
                                                        if (first > 0) {
 
4035
                                                                ec.EmitInt (first);
 
4036
                                                                ec.Emit (OpCodes.Sub);
 
4037
                                                        } else if (first < 0) {
 
4038
                                                                ec.EmitInt (-first);
 
4039
                                                                ec.Emit (OpCodes.Add);
 
4040
                                                        }
 
4041
                                                }
 
4042
 
 
4043
                                                // first, build the list of labels for the switch
 
4044
                                                int iKey = 0;
 
4045
                                                long cJumps = kb.Range;
 
4046
                                                Label[] switch_labels = new Label[cJumps];
 
4047
                                                for (int iJump = 0; iJump < cJumps; iJump++) {
 
4048
                                                        var key = kb.label_values[iKey];
 
4049
                                                        if (key == kb.min + iJump) {
 
4050
                                                                switch_labels[iJump] = labels[key].GetILLabel (ec);
 
4051
                                                                iKey++;
 
4052
                                                        } else {
 
4053
                                                                switch_labels[iJump] = lbl_default;
 
4054
                                                        }
 
4055
                                                }
 
4056
 
 
4057
                                                // emit the switch opcode
 
4058
                                                ec.Emit (OpCodes.Switch, switch_labels);
 
4059
                                        }
 
4060
 
 
4061
                                        // mark the default for this block
 
4062
                                        if (range_index != 0)
 
4063
                                                ec.MarkLabel (lbl_default);
 
4064
                                }
 
4065
 
 
4066
                                // the last default just goes to the end
 
4067
                                if (ranges.Count > 0)
 
4068
                                        ec.Emit (OpCodes.Br, lbl_default);
 
4069
                        }
 
4070
                }
 
4071
                
 
4072
                SwitchLabel FindLabel (Constant value)
 
4073
                {
 
4074
                        SwitchLabel sl = null;
 
4075
 
 
4076
                        if (string_labels != null) {
 
4077
                                string s = value.GetValue () as string;
 
4078
                                if (s == null) {
 
4079
                                        if (case_null != null)
 
4080
                                                sl = case_null;
 
4081
                                        else if (case_default != null)
 
4082
                                                sl = case_default;
 
4083
                                } else {
 
4084
                                        string_labels.TryGetValue (s, out sl);
 
4085
                                }
 
4086
                        } else {
 
4087
                                if (value is NullLiteral) {
 
4088
                                        sl = case_null;
 
4089
                                } else {
 
4090
                                        labels.TryGetValue (value.GetValueAsLong (), out sl);
 
4091
                                }
 
4092
                        }
 
4093
 
 
4094
                        return sl;
 
4095
                }
 
4096
 
 
4097
                public override bool Resolve (BlockContext ec)
 
4098
                {
 
4099
                        Expr = Expr.Resolve (ec);
 
4100
                        if (Expr == null)
 
4101
                                return false;
 
4102
 
 
4103
                        new_expr = SwitchGoverningType (ec, Expr);
 
4104
 
 
4105
                        if (new_expr == null && Expr.Type.IsNullableType) {
 
4106
                                unwrap = Nullable.Unwrap.Create (Expr, false);
 
4107
                                if (unwrap == null)
 
4108
                                        return false;
 
4109
 
 
4110
                                new_expr = SwitchGoverningType (ec, unwrap);
 
4111
                        }
 
4112
 
 
4113
                        if (new_expr == null){
 
4114
                                ec.Report.Error (151, loc,
 
4115
                                        "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
 
4116
                                        TypeManager.CSharpName (Expr.Type));
 
4117
                                return false;
 
4118
                        }
 
4119
 
 
4120
                        // Validate switch.
 
4121
                        SwitchType = new_expr.Type;
 
4122
 
 
4123
                        if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
 
4124
                                ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
 
4125
                                return false;
 
4126
                        }
 
4127
 
 
4128
                        if (block.Statements.Count == 0)
 
4129
                                return true;
 
4130
 
 
4131
                        var constant = new_expr as Constant;
 
4132
 
 
4133
                        if (!ResolveLabels (ec, constant))
 
4134
                                return false;
 
4135
 
 
4136
                        //
 
4137
                        // Don't need extra variable for constant switch or switch with
 
4138
                        // only default case
 
4139
                        //
 
4140
                        if (constant == null && (case_labels.Count - (case_default != null ? 1 : 0)) != 0) {
 
4141
                                //
 
4142
                                // Store switch expression for comparison purposes
 
4143
                                //
 
4144
                                value = new_expr as VariableReference;
 
4145
                                if (value == null) {
 
4146
                                        // Create temporary variable inside switch scope
 
4147
                                        var current_block = ec.CurrentBlock;
 
4148
                                        ec.CurrentBlock = Block;
 
4149
                                        value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
 
4150
                                        value.Resolve (ec);
 
4151
                                        ec.CurrentBlock = current_block;
 
4152
                                }
 
4153
                        }
 
4154
 
 
4155
                        Switch old_switch = ec.Switch;
 
4156
                        ec.Switch = this;
 
4157
                        ec.Switch.SwitchType = SwitchType;
 
4158
 
 
4159
                        ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
 
4160
 
 
4161
                        ec.CurrentBranching.CurrentUsageVector.Goto ();
 
4162
 
 
4163
                        var ok = block.Resolve (ec);
 
4164
 
 
4165
                        if (case_default == null)
 
4166
                                ec.CurrentBranching.CurrentUsageVector.ResetBarrier ();
 
4167
 
 
4168
                        ec.EndFlowBranching ();
 
4169
                        ec.Switch = old_switch;
 
4170
 
 
4171
                        if (!ok)
 
4172
                                return false;
 
4173
 
 
4174
                        if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
 
4175
                                ResolveStringSwitchMap (ec);
 
4176
                        }
 
4177
 
 
4178
                        //
 
4179
                        // Needed to emit anonymous storey initialization before
 
4180
                        // any generated switch dispatch
 
4181
                        //
 
4182
                        block.AddScopeStatement (new DispatchStatement (this));
 
4183
 
 
4184
                        return true;
 
4185
                }
 
4186
 
 
4187
                public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
 
4188
                {
 
4189
                        var sl = FindLabel (value);
 
4190
 
 
4191
                        if (sl == null) {
 
4192
                                FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
 
4193
                        }
 
4194
 
 
4195
                        return sl;
 
4196
                }
 
4197
 
 
4198
                //
 
4199
                // Converts string switch into string hashtable
 
4200
                //
 
4201
                void ResolveStringSwitchMap (ResolveContext ec)
 
4202
                {
 
4203
                        FullNamedExpression string_dictionary_type;
 
4204
                        if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
 
4205
                                string_dictionary_type = new TypeExpression (
 
4206
                                        ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
 
4207
                                                new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
 
4208
                                        loc);
 
4209
                        } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
 
4210
                                string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
 
4211
                        } else {
 
4212
                                ec.Module.PredefinedTypes.Dictionary.Resolve ();
 
4213
                                return;
 
4214
                        }
 
4215
 
 
4216
                        var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
 
4217
                        Field field = new Field (ctype, string_dictionary_type,
 
4218
                                Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
 
4219
                                new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
 
4220
                        if (!field.Define ())
 
4221
                                return;
 
4222
                        ctype.AddField (field);
 
4223
 
 
4224
                        var init = new List<Expression> ();
 
4225
                        int counter = -1;
 
4226
                        labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
 
4227
                        string value = null;
 
4228
 
 
4229
                        foreach (SwitchLabel sl in case_labels) {
 
4230
 
 
4231
                                if (sl.SectionStart)
 
4232
                                        labels.Add (++counter, sl);
 
4233
 
 
4234
                                if (sl == case_default || sl == case_null)
 
4235
                                        continue;
 
4236
 
 
4237
                                value = (string) sl.Converted.GetValue ();
 
4238
                                var init_args = new List<Expression> (2);
 
4239
                                init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
 
4240
 
 
4241
                                sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
 
4242
                                init_args.Add (sl.Converted);
 
4243
 
 
4244
                                init.Add (new CollectionElementInitializer (init_args, loc));
 
4245
                        }
 
4246
        
 
4247
                        Arguments args = new Arguments (1);
 
4248
                        args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
 
4249
                        Expression initializer = new NewInitialize (string_dictionary_type, args,
 
4250
                                new CollectionOrObjectInitializers (init, loc), loc);
 
4251
 
 
4252
                        switch_cache_field = new FieldExpr (field, loc);
 
4253
                        string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
 
4254
                }
 
4255
 
 
4256
                void DoEmitStringSwitch (EmitContext ec)
 
4257
                {
 
4258
                        Label l_initialized = ec.DefineLabel ();
 
4259
 
 
4260
                        //
 
4261
                        // Skip initialization when value is null
 
4262
                        //
 
4263
                        value.EmitBranchable (ec, nullLabel, false);
 
4264
 
 
4265
                        //
 
4266
                        // Check if string dictionary is initialized and initialize
 
4267
                        //
 
4268
                        switch_cache_field.EmitBranchable (ec, l_initialized, true);
 
4269
                        using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
 
4270
                                string_dictionary.EmitStatement (ec);
 
4271
                        }
 
4272
                        ec.MarkLabel (l_initialized);
 
4273
 
 
4274
                        LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
 
4275
 
 
4276
                        ResolveContext rc = new ResolveContext (ec.MemberContext);
 
4277
 
 
4278
                        if (switch_cache_field.Type.IsGeneric) {
 
4279
                                Arguments get_value_args = new Arguments (2);
 
4280
                                get_value_args.Add (new Argument (value));
 
4281
                                get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
 
4282
                                Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
 
4283
                                if (get_item == null)
 
4284
                                        return;
 
4285
 
 
4286
                                //
 
4287
                                // A value was not found, go to default case
 
4288
                                //
 
4289
                                get_item.EmitBranchable (ec, defaultLabel, false);
 
4290
                        } else {
 
4291
                                Arguments get_value_args = new Arguments (1);
 
4292
                                get_value_args.Add (new Argument (value));
 
4293
 
 
4294
                                Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
 
4295
                                if (get_item == null)
 
4296
                                        return;
 
4297
 
 
4298
                                LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
 
4299
                                get_item_object.EmitAssign (ec, get_item, true, false);
 
4300
                                ec.Emit (OpCodes.Brfalse, defaultLabel);
 
4301
 
 
4302
                                ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
 
4303
                                        new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
 
4304
 
 
4305
                                get_item_int.EmitStatement (ec);
 
4306
                                get_item_object.Release (ec);
 
4307
                        }
 
4308
 
 
4309
                        EmitTableSwitch (ec, string_switch_variable);
 
4310
                        string_switch_variable.Release (ec);
 
4311
                }
 
4312
 
 
4313
                //
 
4314
                // Emits switch using simple if/else comparison for small label count (4 + optional default)
 
4315
                //
 
4316
                void EmitShortSwitch (EmitContext ec)
 
4317
                {
 
4318
                        MethodSpec equal_method = null;
 
4319
                        if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
 
4320
                                equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
 
4321
                        }
 
4322
 
 
4323
                        if (equal_method != null) {
 
4324
                                value.EmitBranchable (ec, nullLabel, false);
 
4325
                        }
 
4326
 
 
4327
                        for (int i = 0; i < case_labels.Count; ++i) {
 
4328
                                var label = case_labels [i];
 
4329
                                if (label == case_default || label == case_null)
 
4330
                                        continue;
 
4331
 
 
4332
                                var constant = label.Converted;
 
4333
 
 
4334
                                if (equal_method != null) {
 
4335
                                        value.Emit (ec);
 
4336
                                        constant.Emit (ec);
 
4337
 
 
4338
                                        var call = new CallEmitter ();
 
4339
                                        call.EmitPredefined (ec, equal_method, new Arguments (0));
 
4340
                                        ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
 
4341
                                        continue;
 
4342
                                }
 
4343
 
 
4344
                                if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
 
4345
                                        value.EmitBranchable (ec, label.GetILLabel (ec), false);
 
4346
                                        continue;
 
4347
                                }
 
4348
 
 
4349
                                value.Emit (ec);
 
4350
                                constant.Emit (ec);
 
4351
                                ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
 
4352
                        }
 
4353
 
 
4354
                        ec.Emit (OpCodes.Br, defaultLabel);
 
4355
                }
 
4356
 
 
4357
                void EmitDispatch (EmitContext ec)
 
4358
                {
 
4359
                        if (value == null) {
 
4360
                                //
 
4361
                                // Constant switch, we already done the work
 
4362
                                //
 
4363
                                return;
 
4364
                        }
 
4365
 
 
4366
                        //
 
4367
                        // Mark sequence point explicitly to switch
 
4368
                        //
 
4369
                        ec.Mark (block.StartLocation);
 
4370
                        block.IsCompilerGenerated = true;
 
4371
 
 
4372
                        if (string_dictionary != null) {
 
4373
                                DoEmitStringSwitch (ec);
 
4374
                        } else if (case_labels.Count < 4 || string_labels != null) {
 
4375
                                EmitShortSwitch (ec);
 
4376
                        } else {
 
4377
                                EmitTableSwitch (ec, value);
 
4378
                        }
 
4379
                }
 
4380
 
 
4381
                protected override void DoEmit (EmitContext ec)
 
4382
                {
 
4383
                        // Workaround broken flow-analysis
 
4384
                        block.HasUnreachableClosingBrace = true;
 
4385
 
 
4386
                        //
 
4387
                        // Setup the codegen context
 
4388
                        //
 
4389
                        Label old_end = ec.LoopEnd;
 
4390
                        Switch old_switch = ec.Switch;
 
4391
 
 
4392
                        ec.LoopEnd = ec.DefineLabel ();
 
4393
                        ec.Switch = this;
 
4394
 
 
4395
                        defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
 
4396
                        nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
 
4397
 
 
4398
                        if (value != null) {
 
4399
                                ec.Mark (loc);
 
4400
                                if (IsNullable) {
 
4401
                                        unwrap.EmitCheck (ec);
 
4402
                                        ec.Emit (OpCodes.Brfalse, nullLabel);
 
4403
                                        value.EmitAssign (ec, new_expr, false, false);
 
4404
                                } else if (new_expr != value) {
 
4405
                                        value.EmitAssign (ec, new_expr, false, false);
 
4406
                                }
 
4407
                        }
 
4408
 
 
4409
                        block.Emit (ec);
 
4410
 
 
4411
                        // Restore context state. 
 
4412
                        ec.MarkLabel (ec.LoopEnd);
 
4413
 
 
4414
                        //
 
4415
                        // Restore the previous context
 
4416
                        //
 
4417
                        ec.LoopEnd = old_end;
 
4418
                        ec.Switch = old_switch;
 
4419
                }
 
4420
 
 
4421
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
4422
                {
 
4423
                        Switch target = (Switch) t;
 
4424
 
 
4425
                        target.Expr = Expr.Clone (clonectx);
 
4426
                        target.block = (ExplicitBlock) block.Clone (clonectx);
 
4427
                }
 
4428
                
 
4429
                public override object Accept (StructuralVisitor visitor)
 
4430
                {
 
4431
                        return visitor.Visit (this);
 
4432
                }
 
4433
        }
 
4434
 
 
4435
        // A place where execution can restart in an iterator
 
4436
        public abstract class ResumableStatement : Statement
 
4437
        {
 
4438
                bool prepared;
 
4439
                protected Label resume_point;
 
4440
 
 
4441
                public Label PrepareForEmit (EmitContext ec)
 
4442
                {
 
4443
                        if (!prepared) {
 
4444
                                prepared = true;
 
4445
                                resume_point = ec.DefineLabel ();
 
4446
                        }
 
4447
                        return resume_point;
 
4448
                }
 
4449
 
 
4450
                public virtual Label PrepareForDispose (EmitContext ec, Label end)
 
4451
                {
 
4452
                        return end;
 
4453
                }
 
4454
 
 
4455
                public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
 
4456
                {
 
4457
                }
 
4458
        }
 
4459
 
 
4460
        public abstract class TryFinallyBlock : ExceptionStatement
 
4461
        {
 
4462
                protected Statement stmt;
 
4463
                Label dispose_try_block;
 
4464
                bool prepared_for_dispose, emitted_dispose;
 
4465
                Method finally_host;
 
4466
 
 
4467
                protected TryFinallyBlock (Statement stmt, Location loc)
 
4468
                        : base (loc)
 
4469
                {
 
4470
                        this.stmt = stmt;
 
4471
                }
 
4472
 
 
4473
                #region Properties
 
4474
 
 
4475
                public Statement Statement {
 
4476
                        get {
 
4477
                                return stmt;
 
4478
                        }
 
4479
                }
 
4480
 
 
4481
                #endregion
 
4482
 
 
4483
                protected abstract void EmitTryBody (EmitContext ec);
 
4484
                public abstract void EmitFinallyBody (EmitContext ec);
 
4485
 
 
4486
                public override Label PrepareForDispose (EmitContext ec, Label end)
 
4487
                {
 
4488
                        if (!prepared_for_dispose) {
 
4489
                                prepared_for_dispose = true;
 
4490
                                dispose_try_block = ec.DefineLabel ();
 
4491
                        }
 
4492
                        return dispose_try_block;
 
4493
                }
 
4494
 
 
4495
                protected sealed override void DoEmit (EmitContext ec)
 
4496
                {
 
4497
                        EmitTryBodyPrepare (ec);
 
4498
                        EmitTryBody (ec);
 
4499
 
 
4500
                        ec.BeginFinallyBlock ();
 
4501
 
 
4502
                        Label start_finally = ec.DefineLabel ();
 
4503
                        if (resume_points != null) {
 
4504
                                var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
 
4505
 
 
4506
                                ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
 
4507
                                ec.Emit (OpCodes.Brfalse_S, start_finally);
 
4508
                                ec.Emit (OpCodes.Endfinally);
 
4509
                        }
 
4510
 
 
4511
                        ec.MarkLabel (start_finally);
 
4512
 
 
4513
                        if (finally_host != null) {
 
4514
                                finally_host.Define ();
 
4515
                                finally_host.Emit ();
 
4516
 
 
4517
                                // Now it's safe to add, to close it properly and emit sequence points
 
4518
                                finally_host.Parent.AddMember (finally_host);
 
4519
 
 
4520
                                var ce = new CallEmitter ();
 
4521
                                ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
 
4522
                                ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
 
4523
                        } else {
 
4524
                                EmitFinallyBody (ec);
 
4525
                        }
 
4526
 
 
4527
                        ec.EndExceptionBlock ();
 
4528
                }
 
4529
 
 
4530
                public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
 
4531
                {
 
4532
                        if (emitted_dispose)
 
4533
                                return;
 
4534
 
 
4535
                        emitted_dispose = true;
 
4536
 
 
4537
                        Label end_of_try = ec.DefineLabel ();
 
4538
 
 
4539
                        // Ensure that the only way we can get into this code is through a dispatcher
 
4540
                        if (have_dispatcher)
 
4541
                                ec.Emit (OpCodes.Br, end);
 
4542
 
 
4543
                        ec.BeginExceptionBlock ();
 
4544
 
 
4545
                        ec.MarkLabel (dispose_try_block);
 
4546
 
 
4547
                        Label[] labels = null;
 
4548
                        for (int i = 0; i < resume_points.Count; ++i) {
 
4549
                                ResumableStatement s = resume_points[i];
 
4550
                                Label ret = s.PrepareForDispose (ec, end_of_try);
 
4551
                                if (ret.Equals (end_of_try) && labels == null)
 
4552
                                        continue;
 
4553
                                if (labels == null) {
 
4554
                                        labels = new Label[resume_points.Count];
 
4555
                                        for (int j = 0; j < i; ++j)
 
4556
                                                labels[j] = end_of_try;
 
4557
                                }
 
4558
                                labels[i] = ret;
 
4559
                        }
 
4560
 
 
4561
                        if (labels != null) {
 
4562
                                int j;
 
4563
                                for (j = 1; j < labels.Length; ++j)
 
4564
                                        if (!labels[0].Equals (labels[j]))
 
4565
                                                break;
 
4566
                                bool emit_dispatcher = j < labels.Length;
 
4567
 
 
4568
                                if (emit_dispatcher) {
 
4569
                                        ec.Emit (OpCodes.Ldloc, pc);
 
4570
                                        ec.EmitInt (first_resume_pc);
 
4571
                                        ec.Emit (OpCodes.Sub);
 
4572
                                        ec.Emit (OpCodes.Switch, labels);
 
4573
                                }
 
4574
 
 
4575
                                foreach (ResumableStatement s in resume_points)
 
4576
                                        s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
 
4577
                        }
 
4578
 
 
4579
                        ec.MarkLabel (end_of_try);
 
4580
 
 
4581
                        ec.BeginFinallyBlock ();
 
4582
 
 
4583
                        if (finally_host != null) {
 
4584
                                var ce = new CallEmitter ();
 
4585
                                ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
 
4586
                                ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
 
4587
                        } else {
 
4588
                                EmitFinallyBody (ec);
 
4589
                        }
 
4590
 
 
4591
                        ec.EndExceptionBlock ();
 
4592
                }
 
4593
 
 
4594
                public override bool Resolve (BlockContext bc)
 
4595
                {
 
4596
                        //
 
4597
                        // Finally block inside iterator is called from MoveNext and
 
4598
                        // Dispose methods that means we need to lift the block into
 
4599
                        // newly created host method to emit the body only once. The
 
4600
                        // original block then simply calls the newly generated method.
 
4601
                        //
 
4602
                        if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
 
4603
                                var b = stmt as Block;
 
4604
                                if (b != null && b.Explicit.HasYield) {
 
4605
                                        finally_host = bc.CurrentIterator.CreateFinallyHost (this);
 
4606
                                }
 
4607
                        }
 
4608
 
 
4609
                        return base.Resolve (bc);
 
4610
                }
 
4611
        }
 
4612
 
 
4613
        //
 
4614
        // Base class for blocks using exception handling
 
4615
        //
 
4616
        public abstract class ExceptionStatement : ResumableStatement
 
4617
        {
 
4618
#if !STATIC
 
4619
                bool code_follows;
 
4620
#endif
 
4621
                protected List<ResumableStatement> resume_points;
 
4622
                protected int first_resume_pc;
 
4623
 
 
4624
                protected ExceptionStatement (Location loc)
 
4625
                {
 
4626
                        this.loc = loc;
 
4627
                }
 
4628
 
 
4629
                protected virtual void EmitTryBodyPrepare (EmitContext ec)
 
4630
                {
 
4631
                        StateMachineInitializer state_machine = null;
 
4632
                        if (resume_points != null) {
 
4633
                                state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
 
4634
 
 
4635
                                ec.EmitInt ((int) IteratorStorey.State.Running);
 
4636
                                ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
 
4637
                        }
 
4638
 
 
4639
                        ec.BeginExceptionBlock ();
 
4640
 
 
4641
                        if (resume_points != null) {
 
4642
                                ec.MarkLabel (resume_point);
 
4643
 
 
4644
                                // For normal control flow, we want to fall-through the Switch
 
4645
                                // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
 
4646
                                ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
 
4647
                                ec.EmitInt (first_resume_pc);
 
4648
                                ec.Emit (OpCodes.Sub);
 
4649
 
 
4650
                                Label[] labels = new Label[resume_points.Count];
 
4651
                                for (int i = 0; i < resume_points.Count; ++i)
 
4652
                                        labels[i] = resume_points[i].PrepareForEmit (ec);
 
4653
                                ec.Emit (OpCodes.Switch, labels);
 
4654
                        }
 
4655
                }
 
4656
 
 
4657
                public void SomeCodeFollows ()
 
4658
                {
 
4659
#if !STATIC
 
4660
                        code_follows = true;
 
4661
#endif
 
4662
                }
 
4663
 
 
4664
                public override bool Resolve (BlockContext ec)
 
4665
                {
 
4666
#if !STATIC
 
4667
                        // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
 
4668
                        // So, ensure there's some IL code after this statement.
 
4669
                        if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
 
4670
                                ec.NeedReturnLabel ();
 
4671
#endif
 
4672
                        return true;
 
4673
                }
 
4674
 
 
4675
                public void AddResumePoint (ResumableStatement stmt, int pc)
 
4676
                {
 
4677
                        if (resume_points == null) {
 
4678
                                resume_points = new List<ResumableStatement> ();
 
4679
                                first_resume_pc = pc;
 
4680
                        }
 
4681
 
 
4682
                        if (pc != first_resume_pc + resume_points.Count)
 
4683
                                throw new InternalErrorException ("missed an intervening AddResumePoint?");
 
4684
 
 
4685
                        resume_points.Add (stmt);
 
4686
                }
 
4687
 
 
4688
        }
 
4689
 
 
4690
        public class Lock : TryFinallyBlock
 
4691
        {
 
4692
                Expression expr;
 
4693
                TemporaryVariableReference expr_copy;
 
4694
                TemporaryVariableReference lock_taken;
 
4695
                        
 
4696
                public Lock (Expression expr, Statement stmt, Location loc)
 
4697
                        : base (stmt, loc)
 
4698
                {
 
4699
                        this.expr = expr;
 
4700
                }
 
4701
 
 
4702
                public Expression Expr {
 
4703
                        get {
 
4704
                                return this.expr;
 
4705
                        }
 
4706
                }
 
4707
 
 
4708
                public override bool Resolve (BlockContext ec)
 
4709
                {
 
4710
                        expr = expr.Resolve (ec);
 
4711
                        if (expr == null)
 
4712
                                return false;
 
4713
 
 
4714
                        if (!TypeSpec.IsReferenceType (expr.Type)) {
 
4715
                                ec.Report.Error (185, loc,
 
4716
                                        "`{0}' is not a reference type as required by the lock statement",
 
4717
                                        expr.Type.GetSignatureForError ());
 
4718
                        }
 
4719
 
 
4720
                        if (expr.Type.IsGenericParameter) {
 
4721
                                expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
 
4722
                        }
 
4723
 
 
4724
                        VariableReference lv = expr as VariableReference;
 
4725
                        bool locked;
 
4726
                        if (lv != null) {
 
4727
                                locked = lv.IsLockedByStatement;
 
4728
                                lv.IsLockedByStatement = true;
 
4729
                        } else {
 
4730
                                lv = null;
 
4731
                                locked = false;
 
4732
                        }
 
4733
 
 
4734
                        //
 
4735
                        // Have to keep original lock value around to unlock same location
 
4736
                        // in the case of original value has changed or is null
 
4737
                        //
 
4738
                        expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
 
4739
                        expr_copy.Resolve (ec);
 
4740
 
 
4741
                        //
 
4742
                        // Ensure Monitor methods are available
 
4743
                        //
 
4744
                        if (ResolvePredefinedMethods (ec) > 1) {
 
4745
                                lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
 
4746
                                lock_taken.Resolve (ec);
 
4747
                        }
 
4748
 
 
4749
                        using (ec.Set (ResolveContext.Options.LockScope)) {
 
4750
                                ec.StartFlowBranching (this);
 
4751
                                Statement.Resolve (ec);
 
4752
                                ec.EndFlowBranching ();
 
4753
                        }
 
4754
 
 
4755
                        if (lv != null) {
 
4756
                                lv.IsLockedByStatement = locked;
 
4757
                        }
 
4758
 
 
4759
                        base.Resolve (ec);
 
4760
 
 
4761
                        return true;
 
4762
                }
 
4763
                
 
4764
                protected override void EmitTryBodyPrepare (EmitContext ec)
 
4765
                {
 
4766
                        expr_copy.EmitAssign (ec, expr);
 
4767
 
 
4768
                        if (lock_taken != null) {
 
4769
                                //
 
4770
                                // Initialize ref variable
 
4771
                                //
 
4772
                                lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
 
4773
                        } else {
 
4774
                                //
 
4775
                                // Monitor.Enter (expr_copy)
 
4776
                                //
 
4777
                                expr_copy.Emit (ec);
 
4778
                                ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
 
4779
                        }
 
4780
 
 
4781
                        base.EmitTryBodyPrepare (ec);
 
4782
                }
 
4783
 
 
4784
                protected override void EmitTryBody (EmitContext ec)
 
4785
                {
 
4786
                        //
 
4787
                        // Monitor.Enter (expr_copy, ref lock_taken)
 
4788
                        //
 
4789
                        if (lock_taken != null) {
 
4790
                                expr_copy.Emit (ec);
 
4791
                                lock_taken.LocalInfo.CreateBuilder (ec);
 
4792
                                lock_taken.AddressOf (ec, AddressOp.Load);
 
4793
                                ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
 
4794
                        }
 
4795
 
 
4796
                        Statement.Emit (ec);
 
4797
                }
 
4798
 
 
4799
                public override void EmitFinallyBody (EmitContext ec)
 
4800
                {
 
4801
                        //
 
4802
                        // if (lock_taken) Monitor.Exit (expr_copy)
 
4803
                        //
 
4804
                        Label skip = ec.DefineLabel ();
 
4805
 
 
4806
                        if (lock_taken != null) {
 
4807
                                lock_taken.Emit (ec);
 
4808
                                ec.Emit (OpCodes.Brfalse_S, skip);
 
4809
                        }
 
4810
 
 
4811
                        expr_copy.Emit (ec);
 
4812
                        var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
 
4813
                        if (m != null)
 
4814
                                ec.Emit (OpCodes.Call, m);
 
4815
 
 
4816
                        ec.MarkLabel (skip);
 
4817
                }
 
4818
 
 
4819
                int ResolvePredefinedMethods (ResolveContext rc)
 
4820
                {
 
4821
                        // Try 4.0 Monitor.Enter (object, ref bool) overload first
 
4822
                        var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
 
4823
                        if (m != null)
 
4824
                                return 4;
 
4825
 
 
4826
                        m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
 
4827
                        if (m != null)
 
4828
                                return 1;
 
4829
 
 
4830
                        rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
 
4831
                        return 0;
 
4832
                }
 
4833
 
 
4834
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
4835
                {
 
4836
                        Lock target = (Lock) t;
 
4837
 
 
4838
                        target.expr = expr.Clone (clonectx);
 
4839
                        target.stmt = Statement.Clone (clonectx);
 
4840
                }
 
4841
                
 
4842
                public override object Accept (StructuralVisitor visitor)
 
4843
                {
 
4844
                        return visitor.Visit (this);
 
4845
                }
 
4846
 
 
4847
        }
 
4848
 
 
4849
        public class Unchecked : Statement {
 
4850
                public Block Block;
 
4851
                
 
4852
                public Unchecked (Block b, Location loc)
 
4853
                {
 
4854
                        Block = b;
 
4855
                        b.Unchecked = true;
 
4856
                        this.loc = loc;
 
4857
                }
 
4858
 
 
4859
                public override bool Resolve (BlockContext ec)
 
4860
                {
 
4861
                        using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
 
4862
                                return Block.Resolve (ec);
 
4863
                }
 
4864
                
 
4865
                protected override void DoEmit (EmitContext ec)
 
4866
                {
 
4867
                        using (ec.With (EmitContext.Options.CheckedScope, false))
 
4868
                                Block.Emit (ec);
 
4869
                }
 
4870
 
 
4871
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
4872
                {
 
4873
                        Unchecked target = (Unchecked) t;
 
4874
 
 
4875
                        target.Block = clonectx.LookupBlock (Block);
 
4876
                }
 
4877
                
 
4878
                public override object Accept (StructuralVisitor visitor)
 
4879
                {
 
4880
                        return visitor.Visit (this);
 
4881
                }
 
4882
        }
 
4883
 
 
4884
        public class Checked : Statement {
 
4885
                public Block Block;
 
4886
                
 
4887
                public Checked (Block b, Location loc)
 
4888
                {
 
4889
                        Block = b;
 
4890
                        b.Unchecked = false;
 
4891
                        this.loc = loc;
 
4892
                }
 
4893
 
 
4894
                public override bool Resolve (BlockContext ec)
 
4895
                {
 
4896
                        using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
 
4897
                                return Block.Resolve (ec);
 
4898
                }
 
4899
 
 
4900
                protected override void DoEmit (EmitContext ec)
 
4901
                {
 
4902
                        using (ec.With (EmitContext.Options.CheckedScope, true))
 
4903
                                Block.Emit (ec);
 
4904
                }
 
4905
 
 
4906
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
4907
                {
 
4908
                        Checked target = (Checked) t;
 
4909
 
 
4910
                        target.Block = clonectx.LookupBlock (Block);
 
4911
                }
 
4912
                
 
4913
                public override object Accept (StructuralVisitor visitor)
 
4914
                {
 
4915
                        return visitor.Visit (this);
 
4916
                }
 
4917
        }
 
4918
 
 
4919
        public class Unsafe : Statement {
 
4920
                public Block Block;
 
4921
 
 
4922
                public Unsafe (Block b, Location loc)
 
4923
                {
 
4924
                        Block = b;
 
4925
                        Block.Unsafe = true;
 
4926
                        this.loc = loc;
 
4927
                }
 
4928
 
 
4929
                public override bool Resolve (BlockContext ec)
 
4930
                {
 
4931
                        if (ec.CurrentIterator != null)
 
4932
                                ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
 
4933
 
 
4934
                        using (ec.Set (ResolveContext.Options.UnsafeScope))
 
4935
                                return Block.Resolve (ec);
 
4936
                }
 
4937
                
 
4938
                protected override void DoEmit (EmitContext ec)
 
4939
                {
 
4940
                        Block.Emit (ec);
 
4941
                }
 
4942
 
 
4943
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
4944
                {
 
4945
                        Unsafe target = (Unsafe) t;
 
4946
 
 
4947
                        target.Block = clonectx.LookupBlock (Block);
 
4948
                }
 
4949
                
 
4950
                public override object Accept (StructuralVisitor visitor)
 
4951
                {
 
4952
                        return visitor.Visit (this);
 
4953
                }
 
4954
        }
 
4955
 
 
4956
        // 
 
4957
        // Fixed statement
 
4958
        //
 
4959
        public class Fixed : Statement
 
4960
        {
 
4961
                abstract class Emitter : ShimExpression
 
4962
                {
 
4963
                        protected LocalVariable vi;
 
4964
 
 
4965
                        protected Emitter (Expression expr, LocalVariable li)
 
4966
                                : base (expr)
 
4967
                        {
 
4968
                                vi = li;
 
4969
                        }
 
4970
 
 
4971
                        public abstract void EmitExit (EmitContext ec);
 
4972
                }
 
4973
 
 
4974
                class ExpressionEmitter : Emitter {
 
4975
                        public ExpressionEmitter (Expression converted, LocalVariable li) :
 
4976
                                base (converted, li)
 
4977
                        {
 
4978
                        }
 
4979
 
 
4980
                        protected override Expression DoResolve (ResolveContext rc)
 
4981
                        {
 
4982
                                throw new NotImplementedException ();
 
4983
                        }
 
4984
 
 
4985
                        public override void Emit (EmitContext ec) {
 
4986
                                //
 
4987
                                // Store pointer in pinned location
 
4988
                                //
 
4989
                                expr.Emit (ec);
 
4990
                                vi.EmitAssign (ec);
 
4991
                        }
 
4992
 
 
4993
                        public override void EmitExit (EmitContext ec)
 
4994
                        {
 
4995
                                ec.EmitInt (0);
 
4996
                                ec.Emit (OpCodes.Conv_U);
 
4997
                                vi.EmitAssign (ec);
 
4998
                        }
 
4999
                }
 
5000
 
 
5001
                class StringEmitter : Emitter
 
5002
                {
 
5003
                        LocalVariable pinned_string;
 
5004
 
 
5005
                        public StringEmitter (Expression expr, LocalVariable li, Location loc)
 
5006
                                : base (expr, li)
 
5007
                        {
 
5008
                        }
 
5009
 
 
5010
                        protected override Expression DoResolve (ResolveContext rc)
 
5011
                        {
 
5012
                                pinned_string = new LocalVariable (vi.Block, "$pinned",
 
5013
                                        LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
 
5014
                                        vi.Location);
 
5015
                                pinned_string.Type = rc.BuiltinTypes.String;
 
5016
 
 
5017
                                eclass = ExprClass.Variable;
 
5018
                                type = rc.BuiltinTypes.Int;
 
5019
                                return this;
 
5020
                        }
 
5021
 
 
5022
                        public override void Emit (EmitContext ec)
 
5023
                        {
 
5024
                                pinned_string.CreateBuilder (ec);
 
5025
 
 
5026
                                expr.Emit (ec);
 
5027
                                pinned_string.EmitAssign (ec);
 
5028
 
 
5029
                                // TODO: Should use Binary::Add
 
5030
                                pinned_string.Emit (ec);
 
5031
                                ec.Emit (OpCodes.Conv_I);
 
5032
 
 
5033
                                var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
 
5034
                                if (m == null)
 
5035
                                        return;
 
5036
 
 
5037
                                PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
 
5038
                                //pe.InstanceExpression = pinned_string;
 
5039
                                pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
 
5040
 
 
5041
                                ec.Emit (OpCodes.Add);
 
5042
                                vi.EmitAssign (ec);
 
5043
                        }
 
5044
 
 
5045
                        public override void EmitExit (EmitContext ec)
 
5046
                        {
 
5047
                                ec.EmitNull ();
 
5048
                                pinned_string.EmitAssign (ec);
 
5049
                        }
 
5050
                }
 
5051
 
 
5052
 
 
5053
                public class VariableDeclaration : BlockVariableDeclaration
 
5054
                {
 
5055
                        public VariableDeclaration (FullNamedExpression type, LocalVariable li)
 
5056
                                : base (type, li)
 
5057
                        {
 
5058
                        }
 
5059
 
 
5060
                        protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
 
5061
                        {
 
5062
                                if (!Variable.Type.IsPointer && li == Variable) {
 
5063
                                        bc.Report.Error (209, TypeExpression.Location,
 
5064
                                                "The type of locals declared in a fixed statement must be a pointer type");
 
5065
                                        return null;
 
5066
                                }
 
5067
 
 
5068
                                //
 
5069
                                // The rules for the possible declarators are pretty wise,
 
5070
                                // but the production on the grammar is more concise.
 
5071
                                //
 
5072
                                // So we have to enforce these rules here.
 
5073
                                //
 
5074
                                // We do not resolve before doing the case 1 test,
 
5075
                                // because the grammar is explicit in that the token &
 
5076
                                // is present, so we need to test for this particular case.
 
5077
                                //
 
5078
 
 
5079
                                if (initializer is Cast) {
 
5080
                                        bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
 
5081
                                        return null;
 
5082
                                }
 
5083
 
 
5084
                                initializer = initializer.Resolve (bc);
 
5085
 
 
5086
                                if (initializer == null)
 
5087
                                        return null;
 
5088
 
 
5089
                                //
 
5090
                                // Case 1: Array
 
5091
                                //
 
5092
                                if (initializer.Type.IsArray) {
 
5093
                                        TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
 
5094
 
 
5095
                                        //
 
5096
                                        // Provided that array_type is unmanaged,
 
5097
                                        //
 
5098
                                        if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
 
5099
                                                return null;
 
5100
 
 
5101
                                        //
 
5102
                                        // and T* is implicitly convertible to the
 
5103
                                        // pointer type given in the fixed statement.
 
5104
                                        //
 
5105
                                        ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
 
5106
 
 
5107
                                        Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
 
5108
                                        if (converted == null)
 
5109
                                                return null;
 
5110
 
 
5111
                                        //
 
5112
                                        // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
 
5113
                                        //
 
5114
                                        converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
 
5115
                                                new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
 
5116
                                                new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
 
5117
                                                        new NullLiteral (loc),
 
5118
                                                        converted, loc);
 
5119
 
 
5120
                                        converted = converted.Resolve (bc);
 
5121
 
 
5122
                                        return new ExpressionEmitter (converted, li);
 
5123
                                }
 
5124
 
 
5125
                                //
 
5126
                                // Case 2: string
 
5127
                                //
 
5128
                                if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
 
5129
                                        return new StringEmitter (initializer, li, loc).Resolve (bc);
 
5130
                                }
 
5131
 
 
5132
                                // Case 3: fixed buffer
 
5133
                                if (initializer is FixedBufferPtr) {
 
5134
                                        return new ExpressionEmitter (initializer, li);
 
5135
                                }
 
5136
 
 
5137
                                //
 
5138
                                // Case 4: & object.
 
5139
                                //
 
5140
                                bool already_fixed = true;
 
5141
                                Unary u = initializer as Unary;
 
5142
                                if (u != null && u.Oper == Unary.Operator.AddressOf) {
 
5143
                                        IVariableReference vr = u.Expr as IVariableReference;
 
5144
                                        if (vr == null || !vr.IsFixed) {
 
5145
                                                already_fixed = false;
 
5146
                                        }
 
5147
                                }
 
5148
 
 
5149
                                if (already_fixed) {
 
5150
                                        bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
 
5151
                                }
 
5152
 
 
5153
                                initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
 
5154
                                return new ExpressionEmitter (initializer, li);
 
5155
                        }
 
5156
                }
 
5157
 
 
5158
 
 
5159
                VariableDeclaration decl;
 
5160
                Statement statement;
 
5161
                bool has_ret;
 
5162
 
 
5163
                public Fixed (VariableDeclaration decl, Statement stmt, Location l)
 
5164
                {
 
5165
                        this.decl = decl;
 
5166
                        statement = stmt;
 
5167
                        loc = l;
 
5168
                }
 
5169
 
 
5170
                #region Properties
 
5171
 
 
5172
                public Statement Statement {
 
5173
                        get {
 
5174
                                return statement;
 
5175
                        }
 
5176
                }
 
5177
 
 
5178
                public BlockVariableDeclaration Variables {
 
5179
                        get {
 
5180
                                return decl;
 
5181
                        }
 
5182
                }
 
5183
 
 
5184
                #endregion
 
5185
 
 
5186
                public override bool Resolve (BlockContext ec)
 
5187
                {
 
5188
                        using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
 
5189
                                if (!decl.Resolve (ec))
 
5190
                                        return false;
 
5191
                        }
 
5192
 
 
5193
                        ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
 
5194
                        bool ok = statement.Resolve (ec);
 
5195
                        bool flow_unreachable = ec.EndFlowBranching ();
 
5196
                        has_ret = flow_unreachable;
 
5197
 
 
5198
                        return ok;
 
5199
                }
 
5200
                
 
5201
                protected override void DoEmit (EmitContext ec)
 
5202
                {
 
5203
                        decl.Variable.CreateBuilder (ec);
 
5204
                        decl.Initializer.Emit (ec);
 
5205
                        if (decl.Declarators != null) {
 
5206
                                foreach (var d in decl.Declarators) {
 
5207
                                        d.Variable.CreateBuilder (ec);
 
5208
                                        d.Initializer.Emit (ec);
 
5209
                                }
 
5210
                        }
 
5211
 
 
5212
                        statement.Emit (ec);
 
5213
 
 
5214
                        if (has_ret)
 
5215
                                return;
 
5216
 
 
5217
                        //
 
5218
                        // Clear the pinned variable
 
5219
                        //
 
5220
                        ((Emitter) decl.Initializer).EmitExit (ec);
 
5221
                        if (decl.Declarators != null) {
 
5222
                                foreach (var d in decl.Declarators) {
 
5223
                                        ((Emitter)d.Initializer).EmitExit (ec);
 
5224
                                }
 
5225
                        }
 
5226
                }
 
5227
 
 
5228
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
5229
                {
 
5230
                        Fixed target = (Fixed) t;
 
5231
 
 
5232
                        target.decl = (VariableDeclaration) decl.Clone (clonectx);
 
5233
                        target.statement = statement.Clone (clonectx);
 
5234
                }
 
5235
                
 
5236
                public override object Accept (StructuralVisitor visitor)
 
5237
                {
 
5238
                        return visitor.Visit (this);
 
5239
                }
 
5240
        }
 
5241
 
 
5242
        public class Catch : Statement
 
5243
        {
 
5244
                Block block;
 
5245
                LocalVariable li;
 
5246
                FullNamedExpression type_expr;
 
5247
                CompilerAssign assign;
 
5248
                TypeSpec type;
 
5249
 
 
5250
                public Catch (Block block, Location loc)
 
5251
                {
 
5252
                        this.block = block;
 
5253
                        this.loc = loc;
 
5254
                }
 
5255
 
 
5256
                #region Properties
 
5257
 
 
5258
                public Block Block {
 
5259
                        get {
 
5260
                                return block;
 
5261
                        }
 
5262
                }
 
5263
 
 
5264
                public TypeSpec CatchType {
 
5265
                        get {
 
5266
                                return type;
 
5267
                        }
 
5268
                }
 
5269
 
 
5270
                public bool IsGeneral {
 
5271
                        get {
 
5272
                                return type_expr == null;
 
5273
                        }
 
5274
                }
 
5275
 
 
5276
                public FullNamedExpression TypeExpression {
 
5277
                        get {
 
5278
                                return type_expr;
 
5279
                        }
 
5280
                        set {
 
5281
                                type_expr = value;
 
5282
                        }
 
5283
                }
 
5284
 
 
5285
                public LocalVariable Variable {
 
5286
                        get {
 
5287
                                return li;
 
5288
                        }
 
5289
                        set {
 
5290
                                li = value;
 
5291
                        }
 
5292
                }
 
5293
 
 
5294
                #endregion
 
5295
 
 
5296
                protected override void DoEmit (EmitContext ec)
 
5297
                {
 
5298
                        if (IsGeneral)
 
5299
                                ec.BeginCatchBlock (ec.BuiltinTypes.Object);
 
5300
                        else
 
5301
                                ec.BeginCatchBlock (CatchType);
 
5302
 
 
5303
                        if (li != null) {
 
5304
                                li.CreateBuilder (ec);
 
5305
 
 
5306
                                //
 
5307
                                // Special case hoisted catch variable, we have to use a temporary variable
 
5308
                                // to pass via anonymous storey initialization with the value still on top
 
5309
                                // of the stack
 
5310
                                //
 
5311
                                if (li.HoistedVariant != null) {
 
5312
                                        LocalTemporary lt = new LocalTemporary (li.Type);
 
5313
                                        lt.Store (ec);
 
5314
 
 
5315
                                        // switch to assigning from the temporary variable and not from top of the stack
 
5316
                                        assign.UpdateSource (lt);
 
5317
                                }
 
5318
                        } else {
 
5319
                                ec.Emit (OpCodes.Pop);
 
5320
                        }
 
5321
 
 
5322
                        Block.Emit (ec);
 
5323
                }
 
5324
 
 
5325
                public override bool Resolve (BlockContext ec)
 
5326
                {
 
5327
                        using (ec.With (ResolveContext.Options.CatchScope, true)) {
 
5328
                                if (type_expr != null) {
 
5329
                                        type = type_expr.ResolveAsType (ec);
 
5330
                                        if (type == null)
 
5331
                                                return false;
 
5332
 
 
5333
                                        if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
 
5334
                                                ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
 
5335
                                        } else if (li != null) {
 
5336
                                                li.Type = type;
 
5337
                                                li.PrepareForFlowAnalysis (ec);
 
5338
 
 
5339
                                                // source variable is at the top of the stack
 
5340
                                                Expression source = new EmptyExpression (li.Type);
 
5341
                                                if (li.Type.IsGenericParameter)
 
5342
                                                        source = new UnboxCast (source, li.Type);
 
5343
 
 
5344
                                                //
 
5345
                                                // Uses Location.Null to hide from symbol file
 
5346
                                                //
 
5347
                                                assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
 
5348
                                                Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
 
5349
                                        }
 
5350
                                }
 
5351
 
 
5352
                                return Block.Resolve (ec);
 
5353
                        }
 
5354
                }
 
5355
 
 
5356
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
5357
                {
 
5358
                        Catch target = (Catch) t;
 
5359
 
 
5360
                        if (type_expr != null)
 
5361
                                target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
 
5362
 
 
5363
                        target.block = clonectx.LookupBlock (block);
 
5364
                }
 
5365
        }
 
5366
 
 
5367
        public class TryFinally : TryFinallyBlock
 
5368
        {
 
5369
                Block fini;
 
5370
                
 
5371
                public Statement Stmt {
 
5372
                        get { return this.stmt; }
 
5373
                }
 
5374
 
 
5375
                public Block Fini {
 
5376
                        get { return this.fini; }
 
5377
                }
 
5378
 
 
5379
                public TryFinally (Statement stmt, Block fini, Location loc)
 
5380
                         : base (stmt, loc)
 
5381
                {
 
5382
                        this.fini = fini;
 
5383
                }
 
5384
 
 
5385
                public Block Finallyblock {
 
5386
                        get {
 
5387
                                return fini;
 
5388
                        }
 
5389
                }
 
5390
 
 
5391
                public override bool Resolve (BlockContext ec)
 
5392
                {
 
5393
                        bool ok = true;
 
5394
 
 
5395
                        ec.StartFlowBranching (this);
 
5396
 
 
5397
                        if (!stmt.Resolve (ec))
 
5398
                                ok = false;
 
5399
 
 
5400
                        if (ok)
 
5401
                                ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
 
5402
 
 
5403
                        using (ec.With (ResolveContext.Options.FinallyScope, true)) {
 
5404
                                if (!fini.Resolve (ec))
 
5405
                                        ok = false;
 
5406
                        }
 
5407
 
 
5408
                        ec.EndFlowBranching ();
 
5409
 
 
5410
                        ok &= base.Resolve (ec);
 
5411
 
 
5412
                        return ok;
 
5413
                }
 
5414
 
 
5415
                protected override void EmitTryBody (EmitContext ec)
 
5416
                {
 
5417
                        stmt.Emit (ec);
 
5418
                }
 
5419
 
 
5420
                public override void EmitFinallyBody (EmitContext ec)
 
5421
                {
 
5422
                        fini.Emit (ec);
 
5423
                }
 
5424
 
 
5425
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
5426
                {
 
5427
                        TryFinally target = (TryFinally) t;
 
5428
 
 
5429
                        target.stmt = (Statement) stmt.Clone (clonectx);
 
5430
                        if (fini != null)
 
5431
                                target.fini = clonectx.LookupBlock (fini);
 
5432
                }
 
5433
                
 
5434
                public override object Accept (StructuralVisitor visitor)
 
5435
                {
 
5436
                        return visitor.Visit (this);
 
5437
                }
 
5438
        }
 
5439
 
 
5440
        public class TryCatch : ExceptionStatement
 
5441
        {
 
5442
                public Block Block;
 
5443
                List<Catch> clauses;
 
5444
                readonly bool inside_try_finally;
 
5445
 
 
5446
                public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
 
5447
                        : base (l)
 
5448
                {
 
5449
                        this.Block = block;
 
5450
                        this.clauses = catch_clauses;
 
5451
                        this.inside_try_finally = inside_try_finally;
 
5452
                }
 
5453
 
 
5454
                public List<Catch> Clauses {
 
5455
                        get {
 
5456
                                return clauses;
 
5457
                        }
 
5458
                }
 
5459
 
 
5460
                public bool IsTryCatchFinally {
 
5461
                        get {
 
5462
                                return inside_try_finally;
 
5463
                        }
 
5464
                }
 
5465
 
 
5466
                public override bool Resolve (BlockContext ec)
 
5467
                {
 
5468
                        bool ok = true;
 
5469
 
 
5470
                        ec.StartFlowBranching (this);
 
5471
 
 
5472
                        if (!Block.Resolve (ec))
 
5473
                                ok = false;
 
5474
 
 
5475
                        for (int i = 0; i < clauses.Count; ++i) {
 
5476
                                var c = clauses[i];
 
5477
                                ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
 
5478
 
 
5479
                                if (!c.Resolve (ec)) {
 
5480
                                        ok = false;
 
5481
                                        continue;
 
5482
                                }
 
5483
 
 
5484
                                TypeSpec resolved_type = c.CatchType;
 
5485
                                for (int ii = 0; ii < clauses.Count; ++ii) {
 
5486
                                        if (ii == i)
 
5487
                                                continue;
 
5488
 
 
5489
                                        if (clauses[ii].IsGeneral) {
 
5490
                                                if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
 
5491
                                                        continue;
 
5492
 
 
5493
                                                if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
 
5494
                                                        continue;
 
5495
 
 
5496
                                                if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
 
5497
                                                        continue;
 
5498
 
 
5499
                                                ec.Report.Warning (1058, 1, c.loc,
 
5500
                                                        "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
 
5501
 
 
5502
                                                continue;
 
5503
                                        }
 
5504
 
 
5505
                                        if (ii >= i)
 
5506
                                                continue;
 
5507
 
 
5508
                                        var ct = clauses[ii].CatchType;
 
5509
                                        if (ct == null)
 
5510
                                                continue;
 
5511
 
 
5512
                                        if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
 
5513
                                                ec.Report.Error (160, c.loc,
 
5514
                                                        "A previous catch clause already catches all exceptions of this or a super type `{0}'",
 
5515
                                                        ct.GetSignatureForError ());
 
5516
                                                ok = false;
 
5517
                                        }
 
5518
                                }
 
5519
                        }
 
5520
 
 
5521
                        ec.EndFlowBranching ();
 
5522
 
 
5523
                        return base.Resolve (ec) && ok;
 
5524
                }
 
5525
 
 
5526
                protected sealed override void DoEmit (EmitContext ec)
 
5527
                {
 
5528
                        if (!inside_try_finally)
 
5529
                                EmitTryBodyPrepare (ec);
 
5530
 
 
5531
                        Block.Emit (ec);
 
5532
 
 
5533
                        foreach (Catch c in clauses)
 
5534
                                c.Emit (ec);
 
5535
 
 
5536
                        if (!inside_try_finally)
 
5537
                                ec.EndExceptionBlock ();
 
5538
                }
 
5539
 
 
5540
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
5541
                {
 
5542
                        TryCatch target = (TryCatch) t;
 
5543
 
 
5544
                        target.Block = clonectx.LookupBlock (Block);
 
5545
                        if (clauses != null){
 
5546
                                target.clauses = new List<Catch> ();
 
5547
                                foreach (Catch c in clauses)
 
5548
                                        target.clauses.Add ((Catch) c.Clone (clonectx));
 
5549
                        }
 
5550
                }
 
5551
 
 
5552
                public override object Accept (StructuralVisitor visitor)
 
5553
                {
 
5554
                        return visitor.Visit (this);
 
5555
                }
 
5556
        }
 
5557
 
 
5558
        public class Using : TryFinallyBlock
 
5559
        {
 
5560
                public class VariableDeclaration : BlockVariableDeclaration
 
5561
                {
 
5562
                        Statement dispose_call;
 
5563
 
 
5564
                        public VariableDeclaration (FullNamedExpression type, LocalVariable li)
 
5565
                                : base (type, li)
 
5566
                        {
 
5567
                        }
 
5568
 
 
5569
                        public VariableDeclaration (LocalVariable li, Location loc)
 
5570
                                : base (li)
 
5571
                        {
 
5572
                                this.loc = loc;
 
5573
                        }
 
5574
 
 
5575
                        public VariableDeclaration (Expression expr)
 
5576
                                : base (null)
 
5577
                        {
 
5578
                                loc = expr.Location;
 
5579
                                Initializer = expr;
 
5580
                        }
 
5581
 
 
5582
                        #region Properties
 
5583
 
 
5584
                        public bool IsNested { get; private set; }
 
5585
 
 
5586
                        #endregion
 
5587
 
 
5588
                        public void EmitDispose (EmitContext ec)
 
5589
                        {
 
5590
                                dispose_call.Emit (ec);
 
5591
                        }
 
5592
 
 
5593
                        public override bool Resolve (BlockContext bc)
 
5594
                        {
 
5595
                                if (IsNested)
 
5596
                                        return true;
 
5597
 
 
5598
                                return base.Resolve (bc, false);
 
5599
                        }
 
5600
 
 
5601
                        public Expression ResolveExpression (BlockContext bc)
 
5602
                        {
 
5603
                                var e = Initializer.Resolve (bc);
 
5604
                                if (e == null)
 
5605
                                        return null;
 
5606
 
 
5607
                                li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
 
5608
                                Initializer = ResolveInitializer (bc, Variable, e);
 
5609
                                return e;
 
5610
                        }
 
5611
 
 
5612
                        protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
 
5613
                        {
 
5614
                                if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
 
5615
                                        initializer = initializer.Resolve (bc);
 
5616
                                        if (initializer == null)
 
5617
                                                return null;
 
5618
 
 
5619
                                        // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
 
5620
                                        Arguments args = new Arguments (1);
 
5621
                                        args.Add (new Argument (initializer));
 
5622
                                        initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
 
5623
                                        if (initializer == null)
 
5624
                                                return null;
 
5625
 
 
5626
                                        var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
 
5627
                                        dispose_call = CreateDisposeCall (bc, var);
 
5628
                                        dispose_call.Resolve (bc);
 
5629
 
 
5630
                                        return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
 
5631
                                }
 
5632
 
 
5633
                                if (li == Variable) {
 
5634
                                        CheckIDiposableConversion (bc, li, initializer);
 
5635
                                        dispose_call = CreateDisposeCall (bc, li);
 
5636
                                        dispose_call.Resolve (bc);
 
5637
                                }
 
5638
 
 
5639
                                return base.ResolveInitializer (bc, li, initializer);
 
5640
                        }
 
5641
 
 
5642
                        protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
 
5643
                        {
 
5644
                                var type = li.Type;
 
5645
 
 
5646
                                if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
 
5647
                                        if (type.IsNullableType) {
 
5648
                                                // it's handled in CreateDisposeCall
 
5649
                                                return;
 
5650
                                        }
 
5651
 
 
5652
                                        bc.Report.SymbolRelatedToPreviousError (type);
 
5653
                                        var loc = type_expr == null ? initializer.Location : type_expr.Location;
 
5654
                                        bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
 
5655
                                                type.GetSignatureForError ());
 
5656
 
 
5657
                                        return;
 
5658
                                }
 
5659
                        }
 
5660
 
 
5661
                        protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
 
5662
                        {
 
5663
                                var lvr = lv.CreateReferenceExpression (bc, lv.Location);
 
5664
                                var type = lv.Type;
 
5665
                                var loc = lv.Location;
 
5666
 
 
5667
                                var idt = bc.BuiltinTypes.IDisposable;
 
5668
                                var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
 
5669
 
 
5670
                                var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
 
5671
                                dispose_mg.InstanceExpression = type.IsNullableType ?
 
5672
                                        new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
 
5673
                                        lvr;
 
5674
 
 
5675
                                //
 
5676
                                // Hide it from symbol file via null location
 
5677
                                //
 
5678
                                Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
 
5679
 
 
5680
                                // Add conditional call when disposing possible null variable
 
5681
                                if (!type.IsStruct || type.IsNullableType)
 
5682
                                        dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
 
5683
 
 
5684
                                return dispose;
 
5685
                        }
 
5686
 
 
5687
                        public void ResolveDeclaratorInitializer (BlockContext bc)
 
5688
                        {
 
5689
                                Initializer = base.ResolveInitializer (bc, Variable, Initializer);
 
5690
                        }
 
5691
 
 
5692
                        public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
 
5693
                        {
 
5694
                                for (int i = declarators.Count - 1; i >= 0; --i) {
 
5695
                                        var d = declarators [i];
 
5696
                                        var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
 
5697
                                        vd.Initializer = d.Initializer;
 
5698
                                        vd.IsNested = true;
 
5699
                                        vd.dispose_call = CreateDisposeCall (bc, d.Variable);
 
5700
                                        vd.dispose_call.Resolve (bc);
 
5701
 
 
5702
                                        stmt = new Using (vd, stmt, d.Variable.Location);
 
5703
                                }
 
5704
 
 
5705
                                declarators = null;
 
5706
                                return stmt;
 
5707
                        }       
 
5708
 
 
5709
                        public override object Accept (StructuralVisitor visitor)
 
5710
                        {
 
5711
                                return visitor.Visit (this);
 
5712
                        }       
 
5713
                }
 
5714
 
 
5715
                VariableDeclaration decl;
 
5716
 
 
5717
                public Using (VariableDeclaration decl, Statement stmt, Location loc)
 
5718
                        : base (stmt, loc)
 
5719
                {
 
5720
                        this.decl = decl;
 
5721
                }
 
5722
 
 
5723
                public Using (Expression expr, Statement stmt, Location loc)
 
5724
                        : base (stmt, loc)
 
5725
                {
 
5726
                        this.decl = new VariableDeclaration (expr);
 
5727
                }
 
5728
 
 
5729
                #region Properties
 
5730
 
 
5731
                public Expression Expr {
 
5732
                        get {
 
5733
                                return decl.Variable == null ? decl.Initializer : null;
 
5734
                        }
 
5735
                }
 
5736
 
 
5737
                public BlockVariableDeclaration Variables {
 
5738
                        get {
 
5739
                                return decl;
 
5740
                        }
 
5741
                }
 
5742
 
 
5743
                #endregion
 
5744
 
 
5745
                public override void Emit (EmitContext ec)
 
5746
                {
 
5747
                        //
 
5748
                        // Don't emit sequence point it will be set on variable declaration
 
5749
                        //
 
5750
                        DoEmit (ec);
 
5751
                }
 
5752
 
 
5753
                protected override void EmitTryBodyPrepare (EmitContext ec)
 
5754
                {
 
5755
                        decl.Emit (ec);
 
5756
                        base.EmitTryBodyPrepare (ec);
 
5757
                }
 
5758
 
 
5759
                protected override void EmitTryBody (EmitContext ec)
 
5760
                {
 
5761
                        stmt.Emit (ec);
 
5762
                }
 
5763
 
 
5764
                public override void EmitFinallyBody (EmitContext ec)
 
5765
                {
 
5766
                        decl.EmitDispose (ec);
 
5767
                }
 
5768
 
 
5769
                public override bool Resolve (BlockContext ec)
 
5770
                {
 
5771
                        VariableReference vr;
 
5772
                        bool vr_locked = false;
 
5773
 
 
5774
                        using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
 
5775
                                if (decl.Variable == null) {
 
5776
                                        vr = decl.ResolveExpression (ec) as VariableReference;
 
5777
                                        if (vr != null) {
 
5778
                                                vr_locked = vr.IsLockedByStatement;
 
5779
                                                vr.IsLockedByStatement = true;
 
5780
                                        }
 
5781
                                } else {
 
5782
                                        if (decl.IsNested) {
 
5783
                                                decl.ResolveDeclaratorInitializer (ec);
 
5784
                                        } else {
 
5785
                                                if (!decl.Resolve (ec))
 
5786
                                                        return false;
 
5787
 
 
5788
                                                if (decl.Declarators != null) {
 
5789
                                                        stmt = decl.RewriteUsingDeclarators (ec, stmt);
 
5790
                                                }
 
5791
                                        }
 
5792
 
 
5793
                                        vr = null;
 
5794
                                }
 
5795
                        }
 
5796
 
 
5797
                        ec.StartFlowBranching (this);
 
5798
 
 
5799
                        stmt.Resolve (ec);
 
5800
 
 
5801
                        ec.EndFlowBranching ();
 
5802
 
 
5803
                        if (vr != null)
 
5804
                                vr.IsLockedByStatement = vr_locked;
 
5805
 
 
5806
                        base.Resolve (ec);
 
5807
 
 
5808
                        return true;
 
5809
                }
 
5810
 
 
5811
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
5812
                {
 
5813
                        Using target = (Using) t;
 
5814
 
 
5815
                        target.decl = (VariableDeclaration) decl.Clone (clonectx);
 
5816
                        target.stmt = stmt.Clone (clonectx);
 
5817
                }
 
5818
 
 
5819
                public override object Accept (StructuralVisitor visitor)
 
5820
                {
 
5821
                        return visitor.Visit (this);
 
5822
                }
 
5823
        }
 
5824
 
 
5825
        /// <summary>
 
5826
        ///   Implementation of the foreach C# statement
 
5827
        /// </summary>
 
5828
        public class Foreach : Statement
 
5829
        {
 
5830
                abstract class IteratorStatement : Statement
 
5831
                {
 
5832
                        protected readonly Foreach for_each;
 
5833
 
 
5834
                        protected IteratorStatement (Foreach @foreach)
 
5835
                        {
 
5836
                                this.for_each = @foreach;
 
5837
                                this.loc = @foreach.expr.Location;
 
5838
                        }
 
5839
 
 
5840
                        protected override void CloneTo (CloneContext clonectx, Statement target)
 
5841
                        {
 
5842
                                throw new NotImplementedException ();
 
5843
                        }
 
5844
 
 
5845
                        public override void Emit (EmitContext ec)
 
5846
                        {
 
5847
                                if (ec.EmitAccurateDebugInfo) {
 
5848
                                        ec.Emit (OpCodes.Nop);
 
5849
                                }
 
5850
 
 
5851
                                base.Emit (ec);
 
5852
                        }
 
5853
                }
 
5854
 
 
5855
                sealed class ArrayForeach : IteratorStatement
 
5856
                {
 
5857
                        TemporaryVariableReference[] lengths;
 
5858
                        Expression [] length_exprs;
 
5859
                        StatementExpression[] counter;
 
5860
                        TemporaryVariableReference[] variables;
 
5861
 
 
5862
                        TemporaryVariableReference copy;
 
5863
 
 
5864
                        public ArrayForeach (Foreach @foreach, int rank)
 
5865
                                : base (@foreach)
 
5866
                        {
 
5867
                                counter = new StatementExpression[rank];
 
5868
                                variables = new TemporaryVariableReference[rank];
 
5869
                                length_exprs = new Expression [rank];
 
5870
 
 
5871
                                //
 
5872
                                // Only use temporary length variables when dealing with
 
5873
                                // multi-dimensional arrays
 
5874
                                //
 
5875
                                if (rank > 1)
 
5876
                                        lengths = new TemporaryVariableReference [rank];
 
5877
                        }
 
5878
 
 
5879
                        public override bool Resolve (BlockContext ec)
 
5880
                        {
 
5881
                                Block variables_block = for_each.variable.Block;
 
5882
                                copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
 
5883
                                copy.Resolve (ec);
 
5884
 
 
5885
                                int rank = length_exprs.Length;
 
5886
                                Arguments list = new Arguments (rank);
 
5887
                                for (int i = 0; i < rank; i++) {
 
5888
                                        var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
 
5889
                                        variables[i] = v;
 
5890
                                        counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
 
5891
                                        counter[i].Resolve (ec);
 
5892
 
 
5893
                                        if (rank == 1) {
 
5894
                                                length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
 
5895
                                        } else {
 
5896
                                                lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
 
5897
                                                lengths[i].Resolve (ec);
 
5898
 
 
5899
                                                Arguments args = new Arguments (1);
 
5900
                                                args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
 
5901
                                                length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
 
5902
                                        }
 
5903
 
 
5904
                                        list.Add (new Argument (v));
 
5905
                                }
 
5906
 
 
5907
                                var access = new ElementAccess (copy, list, loc).Resolve (ec);
 
5908
                                if (access == null)
 
5909
                                        return false;
 
5910
 
 
5911
                                TypeSpec var_type;
 
5912
                                if (for_each.type is VarExpr) {
 
5913
                                        // Infer implicitly typed local variable from foreach array type
 
5914
                                        var_type = access.Type;
 
5915
                                } else {
 
5916
                                        var_type = for_each.type.ResolveAsType (ec);
 
5917
 
 
5918
                                        if (var_type == null)
 
5919
                                                return false;
 
5920
 
 
5921
                                        access = Convert.ExplicitConversion (ec, access, var_type, loc);
 
5922
                                        if (access == null)
 
5923
                                                return false;
 
5924
                                }
 
5925
 
 
5926
                                for_each.variable.Type = var_type;
 
5927
 
 
5928
                                var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
 
5929
                                if (variable_ref == null)
 
5930
                                        return false;
 
5931
 
 
5932
                                for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
 
5933
 
 
5934
                                bool ok = true;
 
5935
 
 
5936
                                ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
 
5937
                                ec.CurrentBranching.CreateSibling ();
 
5938
 
 
5939
                                ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
 
5940
                                if (!for_each.body.Resolve (ec))
 
5941
                                        ok = false;
 
5942
                                ec.EndFlowBranching ();
 
5943
 
 
5944
                                // There's no direct control flow from the end of the embedded statement to the end of the loop
 
5945
                                ec.CurrentBranching.CurrentUsageVector.Goto ();
 
5946
 
 
5947
                                ec.EndFlowBranching ();
 
5948
 
 
5949
                                return ok;
 
5950
                        }
 
5951
 
 
5952
                        protected override void DoEmit (EmitContext ec)
 
5953
                        {
 
5954
                                copy.EmitAssign (ec, for_each.expr);
 
5955
 
 
5956
                                int rank = length_exprs.Length;
 
5957
                                Label[] test = new Label [rank];
 
5958
                                Label[] loop = new Label [rank];
 
5959
 
 
5960
                                for (int i = 0; i < rank; i++) {
 
5961
                                        test [i] = ec.DefineLabel ();
 
5962
                                        loop [i] = ec.DefineLabel ();
 
5963
 
 
5964
                                        if (lengths != null)
 
5965
                                                lengths [i].EmitAssign (ec, length_exprs [i]);
 
5966
                                }
 
5967
 
 
5968
                                IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
 
5969
                                for (int i = 0; i < rank; i++) {
 
5970
                                        variables [i].EmitAssign (ec, zero);
 
5971
 
 
5972
                                        ec.Emit (OpCodes.Br, test [i]);
 
5973
                                        ec.MarkLabel (loop [i]);
 
5974
                                }
 
5975
 
 
5976
                                for_each.body.Emit (ec);
 
5977
 
 
5978
                                ec.MarkLabel (ec.LoopBegin);
 
5979
                                ec.Mark (for_each.expr.Location);
 
5980
 
 
5981
                                for (int i = rank - 1; i >= 0; i--){
 
5982
                                        counter [i].Emit (ec);
 
5983
 
 
5984
                                        ec.MarkLabel (test [i]);
 
5985
                                        variables [i].Emit (ec);
 
5986
 
 
5987
                                        if (lengths != null)
 
5988
                                                lengths [i].Emit (ec);
 
5989
                                        else
 
5990
                                                length_exprs [i].Emit (ec);
 
5991
 
 
5992
                                        ec.Emit (OpCodes.Blt, loop [i]);
 
5993
                                }
 
5994
 
 
5995
                                ec.MarkLabel (ec.LoopEnd);
 
5996
                        }
 
5997
                }
 
5998
 
 
5999
                sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
 
6000
                {
 
6001
                        class RuntimeDispose : Using.VariableDeclaration
 
6002
                        {
 
6003
                                public RuntimeDispose (LocalVariable lv, Location loc)
 
6004
                                        : base (lv, loc)
 
6005
                                {
 
6006
                                }
 
6007
 
 
6008
                                protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
 
6009
                                {
 
6010
                                        // Defered to runtime check
 
6011
                                }
 
6012
 
 
6013
                                protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
 
6014
                                {
 
6015
                                        var idt = bc.BuiltinTypes.IDisposable;
 
6016
 
 
6017
                                        //
 
6018
                                        // Fabricates code like
 
6019
                                        //
 
6020
                                        // if ((temp = vr as IDisposable) != null) temp.Dispose ();
 
6021
                                        //
 
6022
 
 
6023
                                        var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
 
6024
 
 
6025
                                        var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
 
6026
                                                dispose_variable.CreateReferenceExpression (bc, loc),
 
6027
                                                new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
 
6028
                                                loc), new NullLiteral (loc));
 
6029
 
 
6030
                                        var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
 
6031
 
 
6032
                                        var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
 
6033
                                        dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
 
6034
 
 
6035
                                        Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
 
6036
                                        return new If (idisaposable_test, dispose, loc);
 
6037
                                }
 
6038
                        }
 
6039
 
 
6040
                        LocalVariable variable;
 
6041
                        Expression expr;
 
6042
                        Statement statement;
 
6043
                        ExpressionStatement init;
 
6044
                        TemporaryVariableReference enumerator_variable;
 
6045
                        bool ambiguous_getenumerator_name;
 
6046
 
 
6047
                        public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
 
6048
                                : base (@foreach)
 
6049
                        {
 
6050
                                this.variable = var;
 
6051
                                this.expr = expr;
 
6052
                        }
 
6053
 
 
6054
                        void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
 
6055
                        {
 
6056
                                rc.Report.SymbolRelatedToPreviousError (enumerator);
 
6057
                                rc.Report.Error (202, loc,
 
6058
                                        "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
 
6059
                                                enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
 
6060
                        }
 
6061
 
 
6062
                        MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
 
6063
                        {
 
6064
                                //
 
6065
                                // Option 1: Try to match by name GetEnumerator first
 
6066
                                //
 
6067
                                var mexpr = Expression.MemberLookup (rc, false, expr.Type,
 
6068
                                        "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc);               // TODO: What if CS0229 ?
 
6069
 
 
6070
                                var mg = mexpr as MethodGroupExpr;
 
6071
                                if (mg != null) {
 
6072
                                        mg.InstanceExpression = expr;
 
6073
                                        Arguments args = new Arguments (0);
 
6074
                                        mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
 
6075
 
 
6076
                                        // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
 
6077
                                        if (ambiguous_getenumerator_name)
 
6078
                                                mg = null;
 
6079
 
 
6080
                                        if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
 
6081
                                                return mg;
 
6082
                                        }
 
6083
                                }
 
6084
 
 
6085
                                //
 
6086
                                // Option 2: Try to match using IEnumerable interfaces with preference of generic version
 
6087
                                //
 
6088
                                var t = expr.Type;
 
6089
                                PredefinedMember<MethodSpec> iface_candidate = null;
 
6090
                                var ptypes = rc.Module.PredefinedTypes;
 
6091
                                var gen_ienumerable = ptypes.IEnumerableGeneric;
 
6092
                                if (!gen_ienumerable.Define ())
 
6093
                                        gen_ienumerable = null;
 
6094
 
 
6095
                                var ifaces = t.Interfaces;
 
6096
                                if (ifaces != null) {
 
6097
                                        foreach (var iface in ifaces) {
 
6098
                                                if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
 
6099
                                                        if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
 
6100
                                                                rc.Report.SymbolRelatedToPreviousError (expr.Type);
 
6101
                                                                rc.Report.Error (1640, loc,
 
6102
                                                                        "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
 
6103
                                                                        expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
 
6104
 
 
6105
                                                                return null;
 
6106
                                                        }
 
6107
 
 
6108
                                                        // TODO: Cache this somehow
 
6109
                                                        iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
 
6110
                                                                MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
 
6111
 
 
6112
                                                        continue;
 
6113
                                                }
 
6114
 
 
6115
                                                if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
 
6116
                                                        iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
 
6117
                                                }
 
6118
                                        }
 
6119
                                }
 
6120
 
 
6121
                                if (iface_candidate == null) {
 
6122
                                        if (expr.Type != InternalType.ErrorType) {
 
6123
                                                rc.Report.Error (1579, loc,
 
6124
                                                        "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
 
6125
                                                        expr.Type.GetSignatureForError (), "GetEnumerator");
 
6126
                                        }
 
6127
 
 
6128
                                        return null;
 
6129
                                }
 
6130
 
 
6131
                                var method = iface_candidate.Resolve (loc);
 
6132
                                if (method == null)
 
6133
                                        return null;
 
6134
 
 
6135
                                mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
 
6136
                                mg.InstanceExpression = expr;
 
6137
                                return mg;
 
6138
                        }
 
6139
 
 
6140
                        MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
 
6141
                        {
 
6142
                                var ms = MemberCache.FindMember (enumerator.ReturnType,
 
6143
                                        MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
 
6144
                                        BindingRestriction.InstanceOnly) as MethodSpec;
 
6145
 
 
6146
                                if (ms == null || !ms.IsPublic) {
 
6147
                                        Error_WrongEnumerator (rc, enumerator);
 
6148
                                        return null;
 
6149
                                }
 
6150
 
 
6151
                                return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
 
6152
                        }
 
6153
 
 
6154
                        PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
 
6155
                        {
 
6156
                                var ps = MemberCache.FindMember (enumerator.ReturnType,
 
6157
                                        MemberFilter.Property ("Current", null),
 
6158
                                        BindingRestriction.InstanceOnly) as PropertySpec;
 
6159
 
 
6160
                                if (ps == null || !ps.IsPublic) {
 
6161
                                        Error_WrongEnumerator (rc, enumerator);
 
6162
                                        return null;
 
6163
                                }
 
6164
 
 
6165
                                return ps;
 
6166
                        }
 
6167
 
 
6168
                        public override bool Resolve (BlockContext ec)
 
6169
                        {
 
6170
                                bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
 
6171
 
 
6172
                                if (is_dynamic) {
 
6173
                                        expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
 
6174
                                } else if (expr.Type.IsNullableType) {
 
6175
                                        expr = new Nullable.UnwrapCall (expr).Resolve (ec);
 
6176
                                }
 
6177
 
 
6178
                                var get_enumerator_mg = ResolveGetEnumerator (ec);
 
6179
                                if (get_enumerator_mg == null) {
 
6180
                                        return false;
 
6181
                                }
 
6182
 
 
6183
                                var get_enumerator = get_enumerator_mg.BestCandidate;
 
6184
                                enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
 
6185
                                enumerator_variable.Resolve (ec);
 
6186
 
 
6187
                                // Prepare bool MoveNext ()
 
6188
                                var move_next_mg = ResolveMoveNext (ec, get_enumerator);
 
6189
                                if (move_next_mg == null) {
 
6190
                                        return false;
 
6191
                                }
 
6192
 
 
6193
                                move_next_mg.InstanceExpression = enumerator_variable;
 
6194
 
 
6195
                                // Prepare ~T~ Current { get; }
 
6196
                                var current_prop = ResolveCurrent (ec, get_enumerator);
 
6197
                                if (current_prop == null) {
 
6198
                                        return false;
 
6199
                                }
 
6200
 
 
6201
                                var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
 
6202
                                if (current_pe == null)
 
6203
                                        return false;
 
6204
 
 
6205
                                VarExpr ve = for_each.type as VarExpr;
 
6206
 
 
6207
                                if (ve != null) {
 
6208
                                        if (is_dynamic) {
 
6209
                                                // Source type is dynamic, set element type to dynamic too
 
6210
                                                variable.Type = ec.BuiltinTypes.Dynamic;
 
6211
                                        } else {
 
6212
                                                // Infer implicitly typed local variable from foreach enumerable type
 
6213
                                                variable.Type = current_pe.Type;
 
6214
                                        }
 
6215
                                } else {
 
6216
                                        if (is_dynamic) {
 
6217
                                                // Explicit cast of dynamic collection elements has to be done at runtime
 
6218
                                                current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
 
6219
                                        }
 
6220
 
 
6221
                                        variable.Type = for_each.type.ResolveAsType (ec);
 
6222
 
 
6223
                                        if (variable.Type == null)
 
6224
                                                return false;
 
6225
 
 
6226
                                        current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
 
6227
                                        if (current_pe == null)
 
6228
                                                return false;
 
6229
                                }
 
6230
 
 
6231
                                var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
 
6232
                                if (variable_ref == null)
 
6233
                                        return false;
 
6234
 
 
6235
                                for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
 
6236
 
 
6237
                                var init = new Invocation (get_enumerator_mg, null);
 
6238
 
 
6239
                                statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
 
6240
                                         for_each.body, Location.Null);
 
6241
 
 
6242
                                var enum_type = enumerator_variable.Type;
 
6243
 
 
6244
                                //
 
6245
                                // Add Dispose method call when enumerator can be IDisposable
 
6246
                                //
 
6247
                                if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
 
6248
                                        if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
 
6249
                                                //
 
6250
                                                // Runtime Dispose check
 
6251
                                                //
 
6252
                                                var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
 
6253
                                                vd.Initializer = init;
 
6254
                                                statement = new Using (vd, statement, Location.Null);
 
6255
                                        } else {
 
6256
                                                //
 
6257
                                                // No Dispose call needed
 
6258
                                                //
 
6259
                                                this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
 
6260
                                                this.init.Resolve (ec);
 
6261
                                        }
 
6262
                                } else {
 
6263
                                        //
 
6264
                                        // Static Dispose check
 
6265
                                        //
 
6266
                                        var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
 
6267
                                        vd.Initializer = init;
 
6268
                                        statement = new Using (vd, statement, Location.Null);
 
6269
                                }
 
6270
 
 
6271
                                return statement.Resolve (ec);
 
6272
                        }
 
6273
 
 
6274
                        protected override void DoEmit (EmitContext ec)
 
6275
                        {
 
6276
                                enumerator_variable.LocalInfo.CreateBuilder (ec);
 
6277
 
 
6278
                                if (init != null)
 
6279
                                        init.EmitStatement (ec);
 
6280
 
 
6281
                                statement.Emit (ec);
 
6282
                        }
 
6283
 
 
6284
                        #region IErrorHandler Members
 
6285
 
 
6286
                        bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
 
6287
                        {
 
6288
                                ec.Report.SymbolRelatedToPreviousError (best);
 
6289
                                ec.Report.Warning (278, 2, expr.Location,
 
6290
                                        "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
 
6291
                                        expr.Type.GetSignatureForError (), "enumerable",
 
6292
                                        best.GetSignatureForError (), ambiguous.GetSignatureForError ());
 
6293
 
 
6294
                                ambiguous_getenumerator_name = true;
 
6295
                                return true;
 
6296
                        }
 
6297
 
 
6298
                        bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
 
6299
                        {
 
6300
                                return false;
 
6301
                        }
 
6302
 
 
6303
                        bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
 
6304
                        {
 
6305
                                return false;
 
6306
                        }
 
6307
 
 
6308
                        bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
 
6309
                        {
 
6310
                                return false;
 
6311
                        }
 
6312
 
 
6313
                        #endregion
 
6314
                }
 
6315
 
 
6316
                Expression type;
 
6317
                LocalVariable variable;
 
6318
                Expression expr;
 
6319
                Statement statement;
 
6320
                Block body;
 
6321
 
 
6322
                public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
 
6323
                {
 
6324
                        this.type = type;
 
6325
                        this.variable = var;
 
6326
                        this.expr = expr;
 
6327
                        this.statement = stmt;
 
6328
                        this.body = body;
 
6329
                        loc = l;
 
6330
                }
 
6331
 
 
6332
                public Expression Expr {
 
6333
                        get { return expr; }
 
6334
                }
 
6335
 
 
6336
                public Statement Statement {
 
6337
                        get { return statement; }
 
6338
                }
 
6339
 
 
6340
                public Expression TypeExpression {
 
6341
                        get { return type; }
 
6342
                }
 
6343
 
 
6344
                public LocalVariable Variable {
 
6345
                        get { return variable; }
 
6346
                }
 
6347
 
 
6348
                public override bool Resolve (BlockContext ec)
 
6349
                {
 
6350
                        expr = expr.Resolve (ec);
 
6351
                        if (expr == null)
 
6352
                                return false;
 
6353
 
 
6354
                        if (expr.IsNull) {
 
6355
                                ec.Report.Error (186, loc, "Use of null is not valid in this context");
 
6356
                                return false;
 
6357
                        }
 
6358
 
 
6359
                        body.AddStatement (statement);
 
6360
 
 
6361
                        if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
 
6362
                                statement = new ArrayForeach (this, 1);
 
6363
                        } else if (expr.Type is ArrayContainer) {
 
6364
                                statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
 
6365
                        } else {
 
6366
                                if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
 
6367
                                        ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
 
6368
                                                expr.ExprClassName);
 
6369
                                        return false;
 
6370
                                }
 
6371
 
 
6372
                                statement = new CollectionForeach (this, variable, expr);
 
6373
                        }
 
6374
 
 
6375
                        return statement.Resolve (ec);
 
6376
                }
 
6377
 
 
6378
                protected override void DoEmit (EmitContext ec)
 
6379
                {
 
6380
                        variable.CreateBuilder (ec);
 
6381
 
 
6382
                        Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
 
6383
                        ec.LoopBegin = ec.DefineLabel ();
 
6384
                        ec.LoopEnd = ec.DefineLabel ();
 
6385
 
 
6386
                        statement.Emit (ec);
 
6387
 
 
6388
                        ec.LoopBegin = old_begin;
 
6389
                        ec.LoopEnd = old_end;
 
6390
                }
 
6391
 
 
6392
                protected override void CloneTo (CloneContext clonectx, Statement t)
 
6393
                {
 
6394
                        Foreach target = (Foreach) t;
 
6395
 
 
6396
                        target.type = type.Clone (clonectx);
 
6397
                        target.expr = expr.Clone (clonectx);
 
6398
                        target.body = (Block) body.Clone (clonectx);
 
6399
                        target.statement = statement.Clone (clonectx);
 
6400
                }
 
6401
                
 
6402
                public override object Accept (StructuralVisitor visitor)
 
6403
                {
 
6404
                        return visitor.Visit (this);
 
6405
                }
 
6406
        }
 
6407
}