2
// statement.cs: Statement representation for the IL tree.
5
// Miguel de Icaza (miguel@ximian.com)
6
// Martin Baulig (martin@ximian.com)
7
// Marek Safar (marek.safar@gmail.com)
9
// Copyright 2001, 2002, 2003 Ximian, Inc.
10
// Copyright 2003, 2004 Novell, Inc.
11
// Copyright 2011 Xamarin Inc.
15
using System.Collections.Generic;
18
using IKVM.Reflection.Emit;
20
using System.Reflection.Emit;
23
namespace Mono.CSharp {
25
public abstract class Statement {
29
/// Resolves the statement, true means that all sub-statements
32
public virtual bool Resolve (BlockContext bc)
38
/// We already know that the statement is unreachable, but we still
39
/// need to resolve it to catch errors.
41
public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
44
// This conflicts with csc's way of doing this, but IMHO it's
45
// the right thing to do.
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.
53
ec.Report.Warning (162, 2, loc, "Unreachable code detected");
55
ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
56
bool ok = Resolve (ec);
57
ec.KillFlowBranching ();
63
/// Return value indicates whether all code paths emitted return.
65
protected abstract void DoEmit (EmitContext ec);
67
public virtual void Emit (EmitContext ec)
72
if (ec.StatementEpilogue != null) {
78
// This routine must be overrided in derived classes and make copies
79
// of all the data that might be modified if resolved
81
protected abstract void CloneTo (CloneContext clonectx, Statement target);
83
public Statement Clone (CloneContext clonectx)
85
Statement s = (Statement) this.MemberwiseClone ();
86
CloneTo (clonectx, s);
90
public virtual Expression CreateExpressionTree (ResolveContext ec)
92
ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
96
public virtual object Accept (StructuralVisitor visitor)
98
return visitor.Visit (this);
102
public sealed class EmptyStatement : Statement
104
public EmptyStatement (Location loc)
109
public override bool Resolve (BlockContext ec)
114
public override bool ResolveUnreachable (BlockContext ec, bool warn)
119
public override void Emit (EmitContext ec)
123
protected override void DoEmit (EmitContext ec)
125
throw new NotSupportedException ();
128
protected override void CloneTo (CloneContext clonectx, Statement target)
133
public override object Accept (StructuralVisitor visitor)
135
return visitor.Visit (this);
139
public class If : Statement {
141
public Statement TrueStatement;
142
public Statement FalseStatement;
146
public If (Expression bool_expr, Statement true_statement, Location l)
147
: this (bool_expr, true_statement, null, l)
151
public If (Expression bool_expr,
152
Statement true_statement,
153
Statement false_statement,
156
this.expr = bool_expr;
157
TrueStatement = true_statement;
158
FalseStatement = false_statement;
162
public Expression Expr {
168
public override bool Resolve (BlockContext ec)
172
expr = expr.Resolve (ec);
177
// Dead code elimination
179
if (expr is Constant) {
180
bool take = !((Constant) expr).IsDefaultValue;
183
if (!TrueStatement.Resolve (ec))
186
if ((FalseStatement != null) &&
187
!FalseStatement.ResolveUnreachable (ec, true))
189
FalseStatement = null;
191
if (!TrueStatement.ResolveUnreachable (ec, true))
193
TrueStatement = null;
195
if ((FalseStatement != null) &&
196
!FalseStatement.Resolve (ec))
204
ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
206
ok &= TrueStatement.Resolve (ec);
208
is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
210
ec.CurrentBranching.CreateSibling ();
212
if (FalseStatement != null)
213
ok &= FalseStatement.Resolve (ec);
215
ec.EndFlowBranching ();
220
protected override void DoEmit (EmitContext ec)
222
Label false_target = ec.DefineLabel ();
226
// If we're a boolean constant, Resolve() already
227
// eliminated dead code for us.
229
Constant c = expr as Constant;
231
c.EmitSideEffect (ec);
233
if (!c.IsDefaultValue)
234
TrueStatement.Emit (ec);
235
else if (FalseStatement != null)
236
FalseStatement.Emit (ec);
241
expr.EmitBranchable (ec, false_target, false);
243
TrueStatement.Emit (ec);
245
if (FalseStatement != null){
246
bool branch_emitted = false;
248
end = ec.DefineLabel ();
250
ec.Emit (OpCodes.Br, end);
251
branch_emitted = true;
254
ec.MarkLabel (false_target);
255
FalseStatement.Emit (ec);
260
ec.MarkLabel (false_target);
264
protected override void CloneTo (CloneContext clonectx, Statement t)
268
target.expr = expr.Clone (clonectx);
269
target.TrueStatement = TrueStatement.Clone (clonectx);
270
if (FalseStatement != null)
271
target.FalseStatement = FalseStatement.Clone (clonectx);
274
public override object Accept (StructuralVisitor visitor)
276
return visitor.Visit (this);
280
public class Do : Statement {
281
public Expression expr;
282
public Statement EmbeddedStatement;
284
public Do (Statement statement, BooleanExpression bool_expr, Location l)
287
EmbeddedStatement = statement;
291
public override bool Resolve (BlockContext ec)
295
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
297
bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
299
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
300
if (!EmbeddedStatement.Resolve (ec))
302
ec.EndFlowBranching ();
304
if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
305
ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
307
expr = expr.Resolve (ec);
310
else if (expr is Constant){
311
bool infinite = !((Constant) expr).IsDefaultValue;
313
ec.CurrentBranching.CurrentUsageVector.Goto ();
316
ec.EndFlowBranching ();
321
protected override void DoEmit (EmitContext ec)
323
Label loop = ec.DefineLabel ();
324
Label old_begin = ec.LoopBegin;
325
Label old_end = ec.LoopEnd;
327
ec.LoopBegin = ec.DefineLabel ();
328
ec.LoopEnd = ec.DefineLabel ();
331
EmbeddedStatement.Emit (ec);
332
ec.MarkLabel (ec.LoopBegin);
334
// Mark start of while condition
335
ec.Mark (expr.Location);
338
// Dead code elimination
340
if (expr is Constant) {
341
bool res = !((Constant) expr).IsDefaultValue;
343
expr.EmitSideEffect (ec);
345
ec.Emit (OpCodes.Br, loop);
347
expr.EmitBranchable (ec, loop, true);
350
ec.MarkLabel (ec.LoopEnd);
352
ec.LoopBegin = old_begin;
353
ec.LoopEnd = old_end;
356
protected override void CloneTo (CloneContext clonectx, Statement t)
360
target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
361
target.expr = expr.Clone (clonectx);
364
public override object Accept (StructuralVisitor visitor)
366
return visitor.Visit (this);
370
public class While : Statement {
371
public Expression expr;
372
public Statement Statement;
373
bool infinite, empty;
375
public While (BooleanExpression bool_expr, Statement statement, Location l)
377
this.expr = bool_expr;
378
Statement = statement;
382
public override bool Resolve (BlockContext ec)
386
expr = expr.Resolve (ec);
391
// Inform whether we are infinite or not
393
if (expr is Constant){
394
bool value = !((Constant) expr).IsDefaultValue;
397
if (!Statement.ResolveUnreachable (ec, true))
405
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
407
ec.CurrentBranching.CreateSibling ();
409
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
410
if (!Statement.Resolve (ec))
412
ec.EndFlowBranching ();
414
// There's no direct control flow from the end of the embedded statement to the end of the loop
415
ec.CurrentBranching.CurrentUsageVector.Goto ();
417
ec.EndFlowBranching ();
422
protected override void DoEmit (EmitContext ec)
425
expr.EmitSideEffect (ec);
429
Label old_begin = ec.LoopBegin;
430
Label old_end = ec.LoopEnd;
432
ec.LoopBegin = ec.DefineLabel ();
433
ec.LoopEnd = ec.DefineLabel ();
436
// Inform whether we are infinite or not
438
if (expr is Constant) {
439
// expr is 'true', since the 'empty' case above handles the 'false' case
440
ec.MarkLabel (ec.LoopBegin);
442
if (ec.EmitAccurateDebugInfo)
443
ec.Emit (OpCodes.Nop);
445
expr.EmitSideEffect (ec);
447
ec.Emit (OpCodes.Br, ec.LoopBegin);
450
// Inform that we are infinite (ie, `we return'), only
451
// if we do not `break' inside the code.
453
ec.MarkLabel (ec.LoopEnd);
455
Label while_loop = ec.DefineLabel ();
457
ec.Emit (OpCodes.Br, ec.LoopBegin);
458
ec.MarkLabel (while_loop);
462
ec.MarkLabel (ec.LoopBegin);
464
ec.Mark (expr.Location);
465
expr.EmitBranchable (ec, while_loop, true);
467
ec.MarkLabel (ec.LoopEnd);
470
ec.LoopBegin = old_begin;
471
ec.LoopEnd = old_end;
474
protected override void CloneTo (CloneContext clonectx, Statement t)
476
While target = (While) t;
478
target.expr = expr.Clone (clonectx);
479
target.Statement = Statement.Clone (clonectx);
482
public override object Accept (StructuralVisitor visitor)
484
return visitor.Visit (this);
488
public class For : Statement
490
bool infinite, empty;
492
public For (Location l)
497
public Statement Initializer {
501
public Expression Condition {
505
public Statement Iterator {
509
public Statement Statement {
513
public override bool Resolve (BlockContext ec)
517
if (Initializer != null) {
518
if (!Initializer.Resolve (ec))
522
if (Condition != null) {
523
Condition = Condition.Resolve (ec);
524
if (Condition == null)
526
else if (Condition is Constant) {
527
bool value = !((Constant) Condition).IsDefaultValue;
530
if (!Statement.ResolveUnreachable (ec, true))
532
if ((Iterator != null) &&
533
!Iterator.ResolveUnreachable (ec, false))
543
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
545
ec.CurrentBranching.CreateSibling ();
547
bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
549
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
550
if (!Statement.Resolve (ec))
552
ec.EndFlowBranching ();
554
if (Iterator != null){
555
if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
556
if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
559
if (!Iterator.Resolve (ec))
564
// There's no direct control flow from the end of the embedded statement to the end of the loop
565
ec.CurrentBranching.CurrentUsageVector.Goto ();
567
ec.EndFlowBranching ();
572
protected override void DoEmit (EmitContext ec)
574
if (Initializer != null)
575
Initializer.Emit (ec);
578
Condition.EmitSideEffect (ec);
582
Label old_begin = ec.LoopBegin;
583
Label old_end = ec.LoopEnd;
584
Label loop = ec.DefineLabel ();
585
Label test = ec.DefineLabel ();
587
ec.LoopBegin = ec.DefineLabel ();
588
ec.LoopEnd = ec.DefineLabel ();
590
ec.Emit (OpCodes.Br, test);
594
ec.MarkLabel (ec.LoopBegin);
599
// If test is null, there is no test, and we are just
602
if (Condition != null) {
603
ec.Mark (Condition.Location);
606
// The Resolve code already catches the case for
607
// Test == Constant (false) so we know that
610
if (Condition is Constant) {
611
Condition.EmitSideEffect (ec);
612
ec.Emit (OpCodes.Br, loop);
614
Condition.EmitBranchable (ec, loop, true);
618
ec.Emit (OpCodes.Br, loop);
619
ec.MarkLabel (ec.LoopEnd);
621
ec.LoopBegin = old_begin;
622
ec.LoopEnd = old_end;
625
protected override void CloneTo (CloneContext clonectx, Statement t)
627
For target = (For) t;
629
if (Initializer != null)
630
target.Initializer = Initializer.Clone (clonectx);
631
if (Condition != null)
632
target.Condition = Condition.Clone (clonectx);
633
if (Iterator != null)
634
target.Iterator = Iterator.Clone (clonectx);
635
target.Statement = Statement.Clone (clonectx);
638
public override object Accept (StructuralVisitor visitor)
640
return visitor.Visit (this);
644
public class StatementExpression : Statement
646
ExpressionStatement expr;
648
public StatementExpression (ExpressionStatement expr)
654
public StatementExpression (ExpressionStatement expr, Location loc)
660
public ExpressionStatement Expr {
666
protected override void CloneTo (CloneContext clonectx, Statement t)
668
StatementExpression target = (StatementExpression) t;
669
target.expr = (ExpressionStatement) expr.Clone (clonectx);
672
protected override void DoEmit (EmitContext ec)
674
expr.EmitStatement (ec);
677
public override bool Resolve (BlockContext ec)
679
expr = expr.ResolveStatement (ec);
683
public override object Accept (StructuralVisitor visitor)
685
return visitor.Visit (this);
689
public class StatementErrorExpression : Statement
691
readonly Expression expr;
693
public StatementErrorExpression (Expression expr)
698
public Expression Expr {
704
protected override void DoEmit (EmitContext ec)
706
throw new NotSupportedException ();
709
protected override void CloneTo (CloneContext clonectx, Statement target)
711
throw new NotImplementedException ();
714
public override object Accept (StructuralVisitor visitor)
716
return visitor.Visit (this);
721
// Simple version of statement list not requiring a block
723
public class StatementList : Statement
725
List<Statement> statements;
727
public StatementList (Statement first, Statement second)
729
statements = new List<Statement> () { first, second };
733
public IList<Statement> Statements {
740
public void Add (Statement statement)
742
statements.Add (statement);
745
public override bool Resolve (BlockContext ec)
747
foreach (var s in statements)
753
protected override void DoEmit (EmitContext ec)
755
foreach (var s in statements)
759
protected override void CloneTo (CloneContext clonectx, Statement target)
761
StatementList t = (StatementList) target;
763
t.statements = new List<Statement> (statements.Count);
764
foreach (Statement s in statements)
765
t.statements.Add (s.Clone (clonectx));
768
public override object Accept (StructuralVisitor visitor)
770
return visitor.Visit (this);
774
// A 'return' or a 'yield break'
775
public abstract class ExitStatement : Statement
777
protected bool unwind_protect;
778
protected abstract bool DoResolve (BlockContext ec);
780
public virtual void Error_FinallyClause (Report Report)
782
Report.Error (157, loc, "Control cannot leave the body of a finally clause");
785
public sealed override bool Resolve (BlockContext ec)
787
var res = DoResolve (ec);
788
unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
789
ec.CurrentBranching.CurrentUsageVector.Goto ();
795
/// Implements the return statement
797
public class Return : ExitStatement
801
public Return (Expression expr, Location l)
809
public Expression Expr {
820
protected override bool DoResolve (BlockContext ec)
823
if (ec.ReturnType.Kind == MemberKind.Void)
827
// Return must not be followed by an expression when
828
// the method return type is Task
830
if (ec.CurrentAnonymousMethod is AsyncInitializer) {
831
var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
832
if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
834
// Extra trick not to emit ret/leave inside awaiter body
836
expr = EmptyExpression.Null;
841
if (ec.CurrentIterator != null) {
842
Error_ReturnFromIterator (ec);
844
ec.Report.Error (126, loc,
845
"An object of a type convertible to `{0}' is required for the return statement",
846
ec.ReturnType.GetSignatureForError ());
852
expr = expr.Resolve (ec);
853
TypeSpec block_return_type = ec.ReturnType;
855
AnonymousExpression am = ec.CurrentAnonymousMethod;
857
if (block_return_type.Kind == MemberKind.Void) {
858
ec.Report.Error (127, loc,
859
"`{0}': A return keyword must not be followed by any expression when method returns void",
860
ec.GetSignatureForError ());
866
Error_ReturnFromIterator (ec);
870
var async_block = am as AsyncInitializer;
871
if (async_block != null) {
873
var storey = (AsyncTaskStorey) am.Storey;
874
var async_type = storey.ReturnType;
876
if (async_type == null && async_block.ReturnTypeInference != null) {
877
async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
881
// TODO: Better error message
882
if (async_type.Kind == MemberKind.Void) {
883
ec.Report.Error (127, loc,
884
"`{0}': A return keyword must not be followed by any expression when method returns void",
885
ec.GetSignatureForError ());
890
if (!async_type.IsGenericTask) {
891
if (this is ContextualReturn)
894
ec.Report.Error (1997, loc,
895
"`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
896
ec.GetSignatureForError ());
901
// The return type is actually Task<T> type argument
903
if (expr.Type == async_type) {
904
ec.Report.Error (4016, loc,
905
"`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
906
ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
908
block_return_type = async_type.TypeArguments[0];
912
var l = am as AnonymousMethodBody;
913
if (l != null && l.ReturnTypeInference != null && expr != null) {
914
l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
923
if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
924
expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
927
if (am != null && block_return_type == ec.ReturnType) {
928
ec.Report.Error (1662, loc,
929
"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",
930
am.ContainerType, am.GetSignatureForError ());
939
protected override void DoEmit (EmitContext ec)
944
var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
945
if (async_body != null) {
946
var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
948
// It's null for await without async
949
if (async_return != null) {
950
async_return.EmitAssign (ec);
955
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
961
if (unwind_protect || ec.EmitAccurateDebugInfo)
962
ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
965
if (unwind_protect) {
966
ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
967
} else if (ec.EmitAccurateDebugInfo) {
968
ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
970
ec.Emit (OpCodes.Ret);
974
void Error_ReturnFromIterator (ResolveContext rc)
976
rc.Report.Error (1622, loc,
977
"Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
980
protected override void CloneTo (CloneContext clonectx, Statement t)
982
Return target = (Return) t;
983
// It's null for simple return;
985
target.expr = expr.Clone (clonectx);
988
public override object Accept (StructuralVisitor visitor)
990
return visitor.Visit (this);
994
public class Goto : Statement {
996
LabeledStatement label;
999
public override bool Resolve (BlockContext ec)
1001
unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
1002
ec.CurrentBranching.CurrentUsageVector.Goto ();
1006
public Goto (string label, Location l)
1012
public string Target {
1013
get { return target; }
1016
public void SetResolvedTarget (LabeledStatement label)
1019
label.AddReference ();
1022
protected override void CloneTo (CloneContext clonectx, Statement target)
1027
protected override void DoEmit (EmitContext ec)
1030
throw new InternalErrorException ("goto emitted before target resolved");
1031
Label l = label.LabelTarget (ec);
1032
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1035
public override object Accept (StructuralVisitor visitor)
1037
return visitor.Visit (this);
1041
public class LabeledStatement : Statement {
1048
FlowBranching.UsageVector vectors;
1050
public LabeledStatement (string name, Block block, Location l)
1057
public Label LabelTarget (EmitContext ec)
1062
label = ec.DefineLabel ();
1067
public Block Block {
1073
public string Name {
1074
get { return name; }
1077
public bool IsDefined {
1078
get { return defined; }
1081
public bool HasBeenReferenced {
1082
get { return referenced; }
1085
public FlowBranching.UsageVector JumpOrigins {
1086
get { return vectors; }
1089
public void AddUsageVector (FlowBranching.UsageVector vector)
1091
vector = vector.Clone ();
1092
vector.Next = vectors;
1096
protected override void CloneTo (CloneContext clonectx, Statement target)
1101
public override bool Resolve (BlockContext ec)
1103
// this flow-branching will be terminated when the surrounding block ends
1104
ec.StartFlowBranching (this);
1108
protected override void DoEmit (EmitContext ec)
1110
if (!HasBeenReferenced)
1111
ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1114
ec.MarkLabel (label);
1117
public void AddReference ()
1122
public override object Accept (StructuralVisitor visitor)
1124
return visitor.Visit (this);
1130
/// `goto default' statement
1132
public class GotoDefault : Statement {
1134
public GotoDefault (Location l)
1139
protected override void CloneTo (CloneContext clonectx, Statement target)
1144
public override bool Resolve (BlockContext ec)
1146
ec.CurrentBranching.CurrentUsageVector.Goto ();
1148
if (ec.Switch == null) {
1149
ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1153
if (!ec.Switch.GotDefault) {
1154
FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1161
protected override void DoEmit (EmitContext ec)
1163
ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
1166
public override object Accept (StructuralVisitor visitor)
1168
return visitor.Visit (this);
1173
/// `goto case' statement
1175
public class GotoCase : Statement {
1179
public GotoCase (Expression e, Location l)
1185
public Expression Expr {
1191
public override bool Resolve (BlockContext ec)
1193
if (ec.Switch == null){
1194
ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1198
ec.CurrentBranching.CurrentUsageVector.Goto ();
1200
expr = expr.Resolve (ec);
1204
Constant c = expr as Constant;
1206
ec.Report.Error (150, expr.Location, "A constant value is expected");
1211
if (ec.Switch.IsNullable && c is NullLiteral) {
1214
TypeSpec type = ec.Switch.SwitchType;
1215
res = c.TryReduce (ec, type);
1217
c.Error_ValueCannotBeConverted (ec, type, true);
1221
if (!Convert.ImplicitStandardConversionExists (c, type))
1222
ec.Report.Warning (469, 2, loc,
1223
"The `goto case' value is not implicitly convertible to type `{0}'",
1224
TypeManager.CSharpName (type));
1228
sl = ec.Switch.ResolveGotoCase (ec, res);
1232
protected override void DoEmit (EmitContext ec)
1234
ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1237
protected override void CloneTo (CloneContext clonectx, Statement t)
1239
GotoCase target = (GotoCase) t;
1241
target.expr = expr.Clone (clonectx);
1244
public override object Accept (StructuralVisitor visitor)
1246
return visitor.Visit (this);
1250
public class Throw : Statement {
1253
public Throw (Expression expr, Location l)
1259
public Expression Expr {
1265
public override bool Resolve (BlockContext ec)
1268
ec.CurrentBranching.CurrentUsageVector.Goto ();
1269
return ec.CurrentBranching.CheckRethrow (loc);
1272
expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1273
ec.CurrentBranching.CurrentUsageVector.Goto ();
1278
var et = ec.BuiltinTypes.Exception;
1279
if (Convert.ImplicitConversionExists (ec, expr, et))
1280
expr = Convert.ImplicitConversion (ec, expr, et, loc);
1282
ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1287
protected override void DoEmit (EmitContext ec)
1290
ec.Emit (OpCodes.Rethrow);
1294
ec.Emit (OpCodes.Throw);
1298
protected override void CloneTo (CloneContext clonectx, Statement t)
1300
Throw target = (Throw) t;
1303
target.expr = expr.Clone (clonectx);
1306
public override object Accept (StructuralVisitor visitor)
1308
return visitor.Visit (this);
1312
public class Break : Statement {
1314
public Break (Location l)
1319
bool unwind_protect;
1321
public override bool Resolve (BlockContext ec)
1323
unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1324
ec.CurrentBranching.CurrentUsageVector.Goto ();
1328
protected override void DoEmit (EmitContext ec)
1330
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1333
protected override void CloneTo (CloneContext clonectx, Statement t)
1338
public override object Accept (StructuralVisitor visitor)
1340
return visitor.Visit (this);
1344
public class Continue : Statement {
1346
public Continue (Location l)
1351
bool unwind_protect;
1353
public override bool Resolve (BlockContext ec)
1355
unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1356
ec.CurrentBranching.CurrentUsageVector.Goto ();
1360
protected override void DoEmit (EmitContext ec)
1362
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1365
protected override void CloneTo (CloneContext clonectx, Statement t)
1370
public override object Accept (StructuralVisitor visitor)
1372
return visitor.Visit (this);
1376
public interface ILocalVariable
1378
void Emit (EmitContext ec);
1379
void EmitAssign (EmitContext ec);
1380
void EmitAddressOf (EmitContext ec);
1383
public interface INamedBlockVariable
1385
Block Block { get; }
1386
Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1387
bool IsDeclared { get; }
1388
bool IsParameter { get; }
1389
Location Location { get; }
1392
public class BlockVariableDeclaration : Statement
1394
public class Declarator
1397
Expression initializer;
1399
public Declarator (LocalVariable li, Expression initializer)
1401
if (li.Type != null)
1402
throw new ArgumentException ("Expected null variable type");
1405
this.initializer = initializer;
1408
public Declarator (Declarator clone, Expression initializer)
1411
this.initializer = initializer;
1416
public LocalVariable Variable {
1422
public Expression Initializer {
1427
initializer = value;
1434
Expression initializer;
1435
protected FullNamedExpression type_expr;
1436
protected LocalVariable li;
1437
protected List<Declarator> declarators;
1440
public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1442
this.type_expr = type;
1444
this.loc = type_expr.Location;
1447
protected BlockVariableDeclaration (LocalVariable li)
1454
public List<Declarator> Declarators {
1460
public Expression Initializer {
1465
initializer = value;
1469
public FullNamedExpression TypeExpression {
1475
public LocalVariable Variable {
1483
public void AddDeclarator (Declarator decl)
1485
if (declarators == null)
1486
declarators = new List<Declarator> ();
1488
declarators.Add (decl);
1491
static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1493
if (bc.Report.Errors != 0)
1496
var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1498
Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1499
new MemberName (li.Name, li.Location), null);
1501
container.AddField (f);
1504
li.HoistedVariant = new HoistedEvaluatorVariable (f);
1508
public override bool Resolve (BlockContext bc)
1510
return Resolve (bc, true);
1513
public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1515
if (type == null && !li.IsCompilerGenerated) {
1516
var vexpr = type_expr as VarExpr;
1519
// C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1520
// same name exists or as a keyword when no type was found
1522
if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1523
if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1524
bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1527
bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1531
if (li.IsConstant) {
1532
bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1536
if (Initializer == null) {
1537
bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1541
if (declarators != null) {
1542
bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1546
Initializer = Initializer.Resolve (bc);
1547
if (Initializer != null) {
1548
((VarExpr) type_expr).InferType (bc, Initializer);
1549
type = type_expr.Type;
1551
// Set error type to indicate the var was placed correctly but could
1554
// var a = missing ();
1556
type = InternalType.ErrorType;
1561
type = type_expr.ResolveAsType (bc);
1565
if (li.IsConstant && !type.IsConstantCompatible) {
1566
Const.Error_InvalidConstantType (type, loc, bc.Report);
1571
FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1576
bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1578
CreateEvaluatorVariable (bc, li);
1580
li.PrepareForFlowAnalysis (bc);
1583
if (initializer != null) {
1584
initializer = ResolveInitializer (bc, li, initializer);
1585
// li.Variable.DefinitelyAssigned
1588
if (declarators != null) {
1589
foreach (var d in declarators) {
1590
d.Variable.Type = li.Type;
1592
CreateEvaluatorVariable (bc, d.Variable);
1594
d.Variable.PrepareForFlowAnalysis (bc);
1597
if (d.Initializer != null && resolveDeclaratorInitializers) {
1598
d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1599
// d.Variable.DefinitelyAssigned
1607
protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1609
var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1610
return a.ResolveStatement (bc);
1613
protected override void DoEmit (EmitContext ec)
1615
li.CreateBuilder (ec);
1617
if (Initializer != null)
1618
((ExpressionStatement) Initializer).EmitStatement (ec);
1620
if (declarators != null) {
1621
foreach (var d in declarators) {
1622
d.Variable.CreateBuilder (ec);
1623
if (d.Initializer != null)
1624
((ExpressionStatement) d.Initializer).EmitStatement (ec);
1629
protected override void CloneTo (CloneContext clonectx, Statement target)
1631
BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1633
if (type_expr != null)
1634
t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1636
if (initializer != null)
1637
t.initializer = initializer.Clone (clonectx);
1639
if (declarators != null) {
1640
t.declarators = null;
1641
foreach (var d in declarators)
1642
t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1646
public override object Accept (StructuralVisitor visitor)
1648
return visitor.Visit (this);
1652
public class BlockConstantDeclaration : BlockVariableDeclaration
1654
public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1659
public override void Emit (EmitContext ec)
1661
// Nothing to emit, not even sequence point
1664
protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1666
initializer = initializer.Resolve (bc);
1667
if (initializer == null)
1670
var c = initializer as Constant;
1672
initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1676
c = c.ConvertImplicitly (li.Type);
1678
if (TypeSpec.IsReferenceType (li.Type))
1679
initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1681
initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
1686
li.ConstantValue = c;
1690
public override object Accept (StructuralVisitor visitor)
1692
return visitor.Visit (this);
1697
// The information about a user-perceived local variable
1699
public class LocalVariable : INamedBlockVariable, ILocalVariable
1706
AddressTaken = 1 << 2,
1707
CompilerGenerated = 1 << 3,
1709
ForeachVariable = 1 << 5,
1710
FixedVariable = 1 << 6,
1711
UsingVariable = 1 << 7,
1712
// DefinitelyAssigned = 1 << 8,
1715
ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1719
readonly string name;
1720
readonly Location loc;
1721
readonly Block block;
1723
Constant const_value;
1725
public VariableInfo VariableInfo;
1726
HoistedVariable hoisted_variant;
1728
LocalBuilder builder;
1730
public LocalVariable (Block block, string name, Location loc)
1737
public LocalVariable (Block block, string name, Flags flags, Location loc)
1738
: this (block, name, loc)
1744
// Used by variable declarators
1746
public LocalVariable (LocalVariable li, string name, Location loc)
1747
: this (li.block, name, li.flags, loc)
1753
public bool AddressTaken {
1755
return (flags & Flags.AddressTaken) != 0;
1759
public Block Block {
1765
public Constant ConstantValue {
1770
const_value = value;
1775
// Hoisted local variable variant
1777
public HoistedVariable HoistedVariant {
1779
return hoisted_variant;
1782
hoisted_variant = value;
1786
public bool IsDeclared {
1788
return type != null;
1792
public bool IsCompilerGenerated {
1794
return (flags & Flags.CompilerGenerated) != 0;
1798
public bool IsConstant {
1800
return (flags & Flags.Constant) != 0;
1804
public bool IsLocked {
1806
return (flags & Flags.IsLocked) != 0;
1809
flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1813
public bool IsThis {
1815
return (flags & Flags.IsThis) != 0;
1819
public bool IsFixed {
1821
return (flags & Flags.FixedVariable) != 0;
1825
bool INamedBlockVariable.IsParameter {
1831
public bool IsReadonly {
1833
return (flags & Flags.ReadonlyMask) != 0;
1837
public Location Location {
1843
public string Name {
1849
public TypeSpec Type {
1860
public void CreateBuilder (EmitContext ec)
1862
if ((flags & Flags.Used) == 0) {
1863
if (VariableInfo == null) {
1864
// Missing flow analysis or wrong variable flags
1865
throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1868
if (VariableInfo.IsEverAssigned)
1869
ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1871
ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1874
if (HoistedVariant != null)
1877
if (builder != null) {
1878
if ((flags & Flags.CompilerGenerated) != 0)
1881
// To avoid Used warning duplicates
1882
throw new InternalErrorException ("Already created variable `{0}'", name);
1886
// All fixed variabled are pinned, a slot has to be alocated
1888
builder = ec.DeclareLocal (Type, IsFixed);
1889
if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1890
ec.DefineLocalVariable (name, builder);
1893
public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1895
LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1900
public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1902
if (IsConstant && const_value != null)
1903
return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1905
return new LocalVariableReference (this, loc);
1908
public void Emit (EmitContext ec)
1910
// TODO: Need something better for temporary variables
1911
if ((flags & Flags.CompilerGenerated) != 0)
1914
ec.Emit (OpCodes.Ldloc, builder);
1917
public void EmitAssign (EmitContext ec)
1919
// TODO: Need something better for temporary variables
1920
if ((flags & Flags.CompilerGenerated) != 0)
1923
ec.Emit (OpCodes.Stloc, builder);
1926
public void EmitAddressOf (EmitContext ec)
1928
ec.Emit (OpCodes.Ldloca, builder);
1931
public static string GetCompilerGeneratedName (Block block)
1933
// HACK: Debugger depends on the name semantics
1934
return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1937
public string GetReadOnlyContext ()
1939
switch (flags & Flags.ReadonlyMask) {
1940
case Flags.FixedVariable:
1941
return "fixed variable";
1942
case Flags.ForeachVariable:
1943
return "foreach iteration variable";
1944
case Flags.UsingVariable:
1945
return "using variable";
1948
throw new InternalErrorException ("Variable is not readonly");
1951
public bool IsThisAssigned (BlockContext ec, Block block)
1953
if (VariableInfo == null)
1954
throw new Exception ();
1956
if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1959
return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
1962
public bool IsAssigned (BlockContext ec)
1964
if (VariableInfo == null)
1965
throw new Exception ();
1967
return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1970
public void PrepareForFlowAnalysis (BlockContext bc)
1973
// No need for definitely assigned check for these guys
1975
if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1978
VariableInfo = new VariableInfo (this, bc.FlowOffset);
1979
bc.FlowOffset += VariableInfo.Length;
1983
// Mark the variables as referenced in the user code
1985
public void SetIsUsed ()
1987
flags |= Flags.Used;
1990
public void SetHasAddressTaken ()
1992
flags |= (Flags.AddressTaken | Flags.Used);
1995
public override string ToString ()
1997
return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2002
/// Block represents a C# block.
2006
/// This class is used in a number of places: either to represent
2007
/// explicit blocks that the programmer places or implicit blocks.
2009
/// Implicit blocks are used as labels or to introduce variable
2012
/// Top-level blocks derive from Block, and they are called ToplevelBlock
2013
/// they contain extra information that is not necessary on normal blocks.
2015
public class Block : Statement {
2022
HasCapturedVariable = 64,
2023
HasCapturedThis = 1 << 7,
2024
IsExpressionTree = 1 << 8,
2025
CompilerGenerated = 1 << 9,
2026
HasAsyncModifier = 1 << 10,
2028
YieldBlock = 1 << 12,
2029
AwaitBlock = 1 << 13
2032
public Block Parent;
2033
public Location StartLocation;
2034
public Location EndLocation;
2036
public ExplicitBlock Explicit;
2037
public ParametersBlock ParametersBlock;
2039
protected Flags flags;
2042
// The statements in this block
2044
protected List<Statement> statements;
2046
protected List<Statement> scope_initializers;
2048
int? resolving_init_idx;
2054
public int ID = id++;
2056
static int clone_id_counter;
2060
// int assignable_slots;
2061
bool unreachable_shown;
2064
public Block (Block parent, Location start, Location end)
2065
: this (parent, 0, start, end)
2069
public Block (Block parent, Flags flags, Location start, Location end)
2071
if (parent != null) {
2072
// the appropriate constructors will fixup these fields
2073
ParametersBlock = parent.ParametersBlock;
2074
Explicit = parent.Explicit;
2077
this.Parent = parent;
2079
this.StartLocation = start;
2080
this.EndLocation = end;
2082
statements = new List<Statement> (4);
2084
this.original = this;
2089
public bool HasUnreachableClosingBrace {
2091
return (flags & Flags.HasRet) != 0;
2094
flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2098
public Block Original {
2107
public bool IsCompilerGenerated {
2108
get { return (flags & Flags.CompilerGenerated) != 0; }
2109
set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2112
public bool Unchecked {
2113
get { return (flags & Flags.Unchecked) != 0; }
2114
set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2117
public bool Unsafe {
2118
get { return (flags & Flags.Unsafe) != 0; }
2119
set { flags |= Flags.Unsafe; }
2122
public List<Statement> Statements {
2123
get { return statements; }
2128
public Block CreateSwitchBlock (Location start)
2130
// FIXME: Only explicit block should be created
2131
var new_block = new Block (this, start, start);
2132
new_block.IsCompilerGenerated = true;
2136
public void SetEndLocation (Location loc)
2141
public void AddLabel (LabeledStatement target)
2143
ParametersBlock.TopBlock.AddLabel (target.Name, target);
2146
public void AddLocalName (LocalVariable li)
2148
AddLocalName (li.Name, li);
2151
public void AddLocalName (string name, INamedBlockVariable li)
2153
ParametersBlock.TopBlock.AddLocalName (name, li, false);
2156
public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2158
if (reason == null) {
2159
Error_AlreadyDeclared (name, variable);
2163
ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2164
"A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2165
"to `{0}', which is already used in a `{1}' scope to denote something else",
2169
public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2171
var pi = variable as ParametersBlock.ParameterInfo;
2173
pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2175
ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2176
"A local variable named `{0}' is already defined in this scope", name);
2180
public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2182
ParametersBlock.TopBlock.Report.Error (412, loc,
2183
"The type parameter name `{0}' is the same as local variable or parameter name",
2188
// It should be used by expressions which require to
2189
// register a statement during resolve process.
2191
public void AddScopeStatement (Statement s)
2193
if (scope_initializers == null)
2194
scope_initializers = new List<Statement> ();
2197
// Simple recursive helper, when resolve scope initializer another
2198
// new scope initializer can be added, this ensures it's initialized
2199
// before existing one. For now this can happen with expression trees
2200
// in base ctor initializer only
2202
if (resolving_init_idx.HasValue) {
2203
scope_initializers.Insert (resolving_init_idx.Value, s);
2204
++resolving_init_idx;
2206
scope_initializers.Add (s);
2210
public void AddStatement (Statement s)
2215
public int AssignableSlots {
2217
// FIXME: HACK, we don't know the block available variables count now, so set this high enough
2219
// return assignable_slots;
2223
public LabeledStatement LookupLabel (string name)
2225
return ParametersBlock.TopBlock.GetLabel (name, this);
2228
public override bool Resolve (BlockContext ec)
2230
if ((flags & Flags.Resolved) != 0)
2233
Block prev_block = ec.CurrentBlock;
2236
ec.CurrentBlock = this;
2237
ec.StartFlowBranching (this);
2240
// Compiler generated scope statements
2242
if (scope_initializers != null) {
2243
for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2244
scope_initializers[resolving_init_idx.Value].Resolve (ec);
2247
resolving_init_idx = null;
2251
// This flag is used to notate nested statements as unreachable from the beginning of this block.
2252
// For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2253
// from the beginning of the function. The outer Resolve() that detected the unreachability is
2254
// responsible for handling the situation.
2256
int statement_count = statements.Count;
2257
for (int ix = 0; ix < statement_count; ix++){
2258
Statement s = statements [ix];
2261
// Warn if we detect unreachable code.
2264
if (s is EmptyStatement)
2267
if (!unreachable_shown && !(s is LabeledStatement)) {
2268
ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2269
unreachable_shown = true;
2272
Block c_block = s as Block;
2273
if (c_block != null)
2274
c_block.unreachable = c_block.unreachable_shown = true;
2278
// Note that we're not using ResolveUnreachable() for unreachable
2279
// statements here. ResolveUnreachable() creates a temporary
2280
// flow branching and kills it afterwards. This leads to problems
2281
// if you have two unreachable statements where the first one
2282
// assigns a variable and the second one tries to access it.
2285
if (!s.Resolve (ec)) {
2287
if (ec.IsInProbingMode)
2290
statements [ix] = new EmptyStatement (s.loc);
2294
if (unreachable && !(s is LabeledStatement) && !(s is Block))
2295
statements [ix] = new EmptyStatement (s.loc);
2297
unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2298
if (unreachable && s is LabeledStatement)
2299
throw new InternalErrorException ("should not happen");
2302
while (ec.CurrentBranching is FlowBranchingLabeled)
2303
ec.EndFlowBranching ();
2305
bool flow_unreachable = ec.EndFlowBranching ();
2307
ec.CurrentBlock = prev_block;
2309
if (flow_unreachable)
2310
flags |= Flags.HasRet;
2312
// If we're a non-static `struct' constructor which doesn't have an
2313
// initializer, then we must initialize all of the struct's fields.
2314
if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2317
flags |= Flags.Resolved;
2321
public override bool ResolveUnreachable (BlockContext ec, bool warn)
2323
unreachable_shown = true;
2327
ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2329
var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2330
fb.CurrentUsageVector.IsUnreachable = true;
2331
bool ok = Resolve (ec);
2332
ec.KillFlowBranching ();
2337
protected override void DoEmit (EmitContext ec)
2339
for (int ix = 0; ix < statements.Count; ix++){
2340
statements [ix].Emit (ec);
2344
public override void Emit (EmitContext ec)
2346
if (scope_initializers != null)
2347
EmitScopeInitializers (ec);
2352
protected void EmitScopeInitializers (EmitContext ec)
2354
foreach (Statement s in scope_initializers)
2359
public override string ToString ()
2361
return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2365
protected override void CloneTo (CloneContext clonectx, Statement t)
2367
Block target = (Block) t;
2369
target.clone_id = clone_id_counter++;
2372
clonectx.AddBlockMap (this, target);
2373
if (original != this)
2374
clonectx.AddBlockMap (original, target);
2376
target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2377
target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2380
target.Parent = clonectx.RemapBlockCopy (Parent);
2382
target.statements = new List<Statement> (statements.Count);
2383
foreach (Statement s in statements)
2384
target.statements.Add (s.Clone (clonectx));
2387
public override object Accept (StructuralVisitor visitor)
2389
return visitor.Visit (this);
2393
public class ExplicitBlock : Block
2395
protected AnonymousMethodStorey am_storey;
2397
public ExplicitBlock (Block parent, Location start, Location end)
2398
: this (parent, (Flags) 0, start, end)
2402
public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2403
: base (parent, flags, start, end)
2405
this.Explicit = this;
2410
public AnonymousMethodStorey AnonymousMethodStorey {
2416
public bool HasAwait {
2418
return (flags & Flags.AwaitBlock) != 0;
2422
public bool HasCapturedThis {
2424
flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
2427
return (flags & Flags.HasCapturedThis) != 0;
2432
// Used to indicate that the block has reference to parent
2433
// block and cannot be made static when defining anonymous method
2435
public bool HasCapturedVariable {
2437
flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2440
return (flags & Flags.HasCapturedVariable) != 0;
2444
public bool HasYield {
2446
return (flags & Flags.YieldBlock) != 0;
2453
// Creates anonymous method storey in current block
2455
public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2458
// Return same story for iterator and async blocks unless we are
2459
// in nested anonymous method
2461
if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2462
return ec.CurrentAnonymousMethod.Storey;
2464
if (am_storey == null) {
2465
MemberBase mc = ec.MemberContext as MemberBase;
2468
// Creates anonymous method storey for this block
2470
am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2476
public override void Emit (EmitContext ec)
2478
if (am_storey != null) {
2479
DefineStoreyContainer (ec, am_storey);
2480
am_storey.EmitStoreyInstantiation (ec, this);
2483
if (scope_initializers != null)
2484
EmitScopeInitializers (ec);
2486
if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2487
ec.Emit (OpCodes.Nop);
2498
if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2499
ec.Emit (OpCodes.Nop);
2503
protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2505
if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2506
storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2507
storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2511
// Creates anonymous method storey
2513
storey.CreateContainer ();
2514
storey.DefineContainer ();
2516
if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
2519
// Only first storey in path will hold this reference. All children blocks will
2520
// reference it indirectly using $ref field
2522
for (Block b = Original.Explicit.Parent; b != null; b = b.Parent) {
2523
var s = b.Explicit.AnonymousMethodStorey;
2525
storey.HoistedThis = s.HoistedThis;
2531
// We are the first storey on path and this has to be hoisted
2533
if (storey.HoistedThis == null) {
2534
foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
2536
// ThisReferencesFromChildrenBlock holds all reference even if they
2537
// are not on this path. It saves some memory otherwise it'd have to
2538
// be in every explicit block. We run this check to see if the reference
2539
// is valid for this storey
2541
Block block_on_path = ref_block;
2542
for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
2544
if (block_on_path == null)
2547
if (storey.HoistedThis == null)
2548
storey.AddCapturedThisField (ec);
2550
for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2551
if (b.AnonymousMethodStorey != null) {
2552
b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2553
b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
2556
// Stop propagation inside same top block
2558
if (b.ParametersBlock == ParametersBlock.Original)
2561
b = b.ParametersBlock;
2564
var pb = b as ParametersBlock;
2565
if (pb != null && pb.StateMachine != null) {
2566
if (pb.StateMachine == storey)
2569
pb.StateMachine.AddParentStoreyReference (ec, storey);
2572
b.HasCapturedVariable = true;
2578
var ref_blocks = storey.ReferencesFromChildrenBlock;
2579
if (ref_blocks != null) {
2580
foreach (ExplicitBlock ref_block in ref_blocks) {
2581
for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2582
if (b.AnonymousMethodStorey != null) {
2583
b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2586
// Stop propagation inside same top block
2588
if (b.ParametersBlock == ParametersBlock.Original)
2591
b = b.ParametersBlock;
2594
var pb = b as ParametersBlock;
2595
if (pb != null && pb.StateMachine != null) {
2596
if (pb.StateMachine == storey)
2599
pb.StateMachine.AddParentStoreyReference (ec, storey);
2602
b.HasCapturedVariable = true;
2608
storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2611
public void RegisterAsyncAwait ()
2614
while ((block.flags & Flags.AwaitBlock) == 0) {
2615
block.flags |= Flags.AwaitBlock;
2617
if (block is ParametersBlock)
2620
block = block.Parent.Explicit;
2624
public void RegisterIteratorYield ()
2627
while ((block.flags & Flags.YieldBlock) == 0) {
2628
block.flags |= Flags.YieldBlock;
2630
if (block.Parent == null)
2633
block = block.Parent.Explicit;
2637
public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2639
tryBlock.statements = statements;
2640
statements = new List<Statement> (1);
2641
statements.Add (tf);
2646
// ParametersBlock was introduced to support anonymous methods
2647
// and lambda expressions
2649
public class ParametersBlock : ExplicitBlock
2651
public class ParameterInfo : INamedBlockVariable
2653
readonly ParametersBlock block;
2655
public VariableInfo VariableInfo;
2658
public ParameterInfo (ParametersBlock block, int index)
2666
public ParametersBlock Block {
2672
Block INamedBlockVariable.Block {
2678
public bool IsDeclared {
2684
public bool IsParameter {
2690
public bool IsLocked {
2699
public Location Location {
2701
return Parameter.Location;
2705
public Parameter Parameter {
2707
return block.Parameters [index];
2711
public TypeSpec ParameterType {
2713
return Parameter.Type;
2719
public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2721
return new ParameterReference (this, loc);
2726
// Block is converted into an expression
2728
sealed class BlockScopeExpression : Expression
2731
readonly ParametersBlock block;
2733
public BlockScopeExpression (Expression child, ParametersBlock block)
2739
public override bool ContainsEmitWithAwait ()
2741
return child.ContainsEmitWithAwait ();
2744
public override Expression CreateExpressionTree (ResolveContext ec)
2746
throw new NotSupportedException ();
2749
protected override Expression DoResolve (ResolveContext ec)
2754
child = child.Resolve (ec);
2758
eclass = child.eclass;
2763
public override void Emit (EmitContext ec)
2765
block.EmitScopeInitializers (ec);
2770
protected ParametersCompiled parameters;
2771
protected ParameterInfo[] parameter_info;
2773
protected bool unreachable;
2774
protected ToplevelBlock top_block;
2775
protected StateMachine state_machine;
2777
public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2778
: base (parent, 0, start, start)
2780
if (parameters == null)
2781
throw new ArgumentNullException ("parameters");
2783
this.parameters = parameters;
2784
ParametersBlock = this;
2786
flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2788
this.top_block = parent.ParametersBlock.top_block;
2789
ProcessParameters ();
2792
protected ParametersBlock (ParametersCompiled parameters, Location start)
2793
: base (null, 0, start, start)
2795
if (parameters == null)
2796
throw new ArgumentNullException ("parameters");
2798
this.parameters = parameters;
2799
ParametersBlock = this;
2803
// It's supposed to be used by method body implementation of anonymous methods
2805
protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2806
: base (null, 0, source.StartLocation, source.EndLocation)
2808
this.parameters = parameters;
2809
this.statements = source.statements;
2810
this.scope_initializers = source.scope_initializers;
2812
this.resolved = true;
2813
this.unreachable = source.unreachable;
2814
this.am_storey = source.am_storey;
2815
this.state_machine = source.state_machine;
2817
ParametersBlock = this;
2820
// Overwrite original for comparison purposes when linking cross references
2821
// between anonymous methods
2828
public bool IsAsync {
2830
return (flags & Flags.HasAsyncModifier) != 0;
2833
flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2838
// Block has been converted to expression tree
2840
public bool IsExpressionTree {
2842
return (flags & Flags.IsExpressionTree) != 0;
2847
// The parameters for the block.
2849
public ParametersCompiled Parameters {
2855
public StateMachine StateMachine {
2857
return state_machine;
2861
public ToplevelBlock TopBlock {
2867
public bool Resolved {
2869
return (flags & Flags.Resolved) != 0;
2873
public int TemporaryLocalsCount { get; set; }
2878
// Check whether all `out' parameters have been assigned.
2880
public void CheckOutParameters (FlowBranching.UsageVector vector)
2882
if (vector.IsUnreachable)
2885
int n = parameter_info == null ? 0 : parameter_info.Length;
2887
for (int i = 0; i < n; i++) {
2888
VariableInfo var = parameter_info[i].VariableInfo;
2893
if (vector.IsAssigned (var, false))
2896
var p = parameter_info[i].Parameter;
2897
TopBlock.Report.Error (177, p.Location,
2898
"The out parameter `{0}' must be assigned to before control leaves the current method",
2903
public override Expression CreateExpressionTree (ResolveContext ec)
2905
if (statements.Count == 1) {
2906
Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2907
if (scope_initializers != null)
2908
expr = new BlockScopeExpression (expr, this);
2913
return base.CreateExpressionTree (ec);
2916
public override void Emit (EmitContext ec)
2918
if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2919
DefineStoreyContainer (ec, state_machine);
2920
state_machine.EmitStoreyInstantiation (ec, this);
2926
public void EmitEmbedded (EmitContext ec)
2928
if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2929
DefineStoreyContainer (ec, state_machine);
2930
state_machine.EmitStoreyInstantiation (ec, this);
2936
public ParameterInfo GetParameterInfo (Parameter p)
2938
for (int i = 0; i < parameters.Count; ++i) {
2939
if (parameters[i] == p)
2940
return parameter_info[i];
2943
throw new ArgumentException ("Invalid parameter");
2946
public ParameterReference GetParameterReference (int index, Location loc)
2948
return new ParameterReference (parameter_info[index], loc);
2951
public Statement PerformClone ()
2953
CloneContext clonectx = new CloneContext ();
2954
return Clone (clonectx);
2957
protected void ProcessParameters ()
2959
if (parameters.Count == 0)
2962
parameter_info = new ParameterInfo[parameters.Count];
2963
for (int i = 0; i < parameter_info.Length; ++i) {
2964
var p = parameters.FixedParameters[i];
2968
// TODO: Should use Parameter only and more block there
2969
parameter_info[i] = new ParameterInfo (this, i);
2971
AddLocalName (p.Name, parameter_info[i]);
2975
public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2982
if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2983
flags |= Flags.IsExpressionTree;
2988
using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2989
FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2994
unreachable = top_level.End ();
2996
} catch (Exception e) {
2997
if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
3000
if (rc.CurrentBlock != null) {
3001
rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3003
rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3006
if (rc.Module.Compiler.Settings.DebugFlags > 0)
3010
if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3011
if (rc.CurrentAnonymousMethod == null) {
3012
// FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3013
if (md is StateMachineMethod) {
3016
rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3021
// If an asynchronous body of F is either an expression classified as nothing, or a
3022
// statement block where no return statements have expressions, the inferred return type is Task
3025
var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3026
if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3027
am.ReturnTypeInference = null;
3028
am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3033
rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3034
rc.CurrentAnonymousMethod.GetSignatureForError ());
3042
void ResolveMeta (BlockContext ec)
3044
int orig_count = parameters.Count;
3046
for (int i = 0; i < orig_count; ++i) {
3047
Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
3049
if ((mod & Parameter.Modifier.OUT) == 0)
3052
VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
3053
parameter_info[i].VariableInfo = vi;
3054
ec.FlowOffset += vi.Length;
3058
public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3060
var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3061
var stateMachine = new IteratorStorey (iterator);
3063
state_machine = stateMachine;
3064
iterator.SetStateMachine (stateMachine);
3066
var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3067
tlb.Original = this;
3068
tlb.IsCompilerGenerated = true;
3069
tlb.state_machine = stateMachine;
3070
tlb.AddStatement (new Return (iterator, iterator.Location));
3074
public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, Location loc)
3076
for (int i = 0; i < parameters.Count; i++) {
3077
Parameter p = parameters[i];
3078
Parameter.Modifier mod = p.ModFlags;
3079
if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3080
host.Compiler.Report.Error (1988, p.Location,
3081
"Async methods cannot have ref or out parameters");
3085
if (p is ArglistParameter) {
3086
host.Compiler.Report.Error (4006, p.Location,
3087
"__arglist is not allowed in parameter list of async methods");
3091
if (parameters.Types[i].IsPointer) {
3092
host.Compiler.Report.Error (4005, p.Location,
3093
"Async methods cannot have unsafe parameters");
3099
host.Compiler.Report.Warning (1998, 1, loc,
3100
"Async block lacks `await' operator and will run synchronously");
3103
var block_type = host.Module.Compiler.BuiltinTypes.Void;
3104
var initializer = new AsyncInitializer (this, host, block_type);
3105
initializer.Type = block_type;
3107
var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3109
state_machine = stateMachine;
3110
initializer.SetStateMachine (stateMachine);
3112
var b = this is ToplevelBlock ?
3113
new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3114
new ParametersBlock (Parent, parameters, Location.Null) {
3119
b.IsCompilerGenerated = true;
3120
b.state_machine = stateMachine;
3121
b.AddStatement (new StatementExpression (initializer));
3129
public class ToplevelBlock : ParametersBlock
3131
LocalVariable this_variable;
3132
CompilerContext compiler;
3133
Dictionary<string, object> names;
3134
Dictionary<string, object> labels;
3136
List<ExplicitBlock> this_references;
3138
public ToplevelBlock (CompilerContext ctx, Location loc)
3139
: this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3143
public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3144
: base (parameters, start)
3146
this.compiler = ctx;
3148
flags |= Flags.HasRet;
3150
ProcessParameters ();
3154
// Recreates a top level block from parameters block. Used for
3155
// compiler generated methods where the original block comes from
3156
// explicit child block. This works for already resolved blocks
3157
// only to ensure we resolve them in the correct flow order
3159
public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3160
: base (source, parameters)
3162
this.compiler = source.TopBlock.compiler;
3164
flags |= Flags.HasRet;
3167
public bool IsIterator {
3173
public Report Report {
3175
return compiler.Report;
3180
// Used by anonymous blocks to track references of `this' variable
3182
public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3184
return this_references;
3189
// Returns the "this" instance variable of this block.
3190
// See AddThisVariable() for more information.
3192
public LocalVariable ThisVariable {
3194
return this_variable;
3198
public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3201
names = new Dictionary<string, object> ();
3204
if (!names.TryGetValue (name, out value)) {
3205
names.Add (name, li);
3209
INamedBlockVariable existing = value as INamedBlockVariable;
3210
List<INamedBlockVariable> existing_list;
3211
if (existing != null) {
3212
existing_list = new List<INamedBlockVariable> ();
3213
existing_list.Add (existing);
3214
names[name] = existing_list;
3216
existing_list = (List<INamedBlockVariable>) value;
3220
// A collision checking between local names
3222
for (int i = 0; i < existing_list.Count; ++i) {
3223
existing = existing_list[i];
3224
Block b = existing.Block.Explicit;
3226
// Collision at same level
3227
if (li.Block.Explicit == b) {
3228
li.Block.Error_AlreadyDeclared (name, li);
3232
// Collision with parent
3233
Block parent = li.Block.Explicit;
3234
while ((parent = parent.Parent) != null) {
3236
li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3237
i = existing_list.Count;
3242
if (!ignoreChildrenBlocks) {
3243
// Collision with children
3244
while ((b = b.Parent) != null) {
3245
if (li.Block.Explicit == b) {
3246
li.Block.Error_AlreadyDeclared (name, li, "child");
3247
i = existing_list.Count;
3254
existing_list.Add (li);
3257
public void AddLabel (string name, LabeledStatement label)
3260
labels = new Dictionary<string, object> ();
3263
if (!labels.TryGetValue (name, out value)) {
3264
labels.Add (name, label);
3268
LabeledStatement existing = value as LabeledStatement;
3269
List<LabeledStatement> existing_list;
3270
if (existing != null) {
3271
existing_list = new List<LabeledStatement> ();
3272
existing_list.Add (existing);
3273
labels[name] = existing_list;
3275
existing_list = (List<LabeledStatement>) value;
3279
// A collision checking between labels
3281
for (int i = 0; i < existing_list.Count; ++i) {
3282
existing = existing_list[i];
3283
Block b = existing.Block;
3285
// Collision at same level
3286
if (label.Block == b) {
3287
Report.SymbolRelatedToPreviousError (existing.loc, name);
3288
Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3292
// Collision with parent
3294
while ((b = b.Parent) != null) {
3295
if (existing.Block == b) {
3296
Report.Error (158, label.loc,
3297
"The label `{0}' shadows another label by the same name in a contained scope", name);
3298
i = existing_list.Count;
3303
// Collision with with children
3305
while ((b = b.Parent) != null) {
3306
if (label.Block == b) {
3307
Report.Error (158, label.loc,
3308
"The label `{0}' shadows another label by the same name in a contained scope", name);
3309
i = existing_list.Count;
3315
existing_list.Add (label);
3318
public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3320
if (this_references == null)
3321
this_references = new List<ExplicitBlock> ();
3323
if (!this_references.Contains (block))
3324
this_references.Add (block);
3327
public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3329
this_references.Remove (block);
3333
// Creates an arguments set from all parameters, useful for method proxy calls
3335
public Arguments GetAllParametersArguments ()
3337
int count = parameters.Count;
3338
Arguments args = new Arguments (count);
3339
for (int i = 0; i < count; ++i) {
3340
var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3341
args.Add (new Argument (arg_expr));
3348
// Lookup inside a block, the returned value can represent 3 states
3350
// true+variable: A local name was found and it's valid
3351
// false+variable: A local name was found in a child block only
3352
// false+null: No local name was found
3354
public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3360
if (!names.TryGetValue (name, out value))
3363
variable = value as INamedBlockVariable;
3365
if (variable != null) {
3367
if (variable.Block == b.Original)
3371
} while (b != null);
3379
} while (b != null);
3381
List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3382
for (int i = 0; i < list.Count; ++i) {
3385
if (variable.Block == b.Original)
3389
} while (b != null);
3397
} while (b != null);
3407
public LabeledStatement GetLabel (string name, Block block)
3413
if (!labels.TryGetValue (name, out value)) {
3417
var label = value as LabeledStatement;
3419
if (label != null) {
3420
if (label.Block == b.Original)
3423
// TODO: Temporary workaround for the switch block implicit label block
3424
if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3427
List<LabeledStatement> list = (List<LabeledStatement>) value;
3428
for (int i = 0; i < list.Count; ++i) {
3430
if (label.Block == b.Original)
3433
// TODO: Temporary workaround for the switch block implicit label block
3434
if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3443
// This is used by non-static `struct' constructors which do not have an
3444
// initializer - in this case, the constructor must initialize all of the
3445
// struct's fields. To do this, we add a "this" variable and use the flow
3446
// analysis code to ensure that it's been fully initialized before control
3447
// leaves the constructor.
3449
public void AddThisVariable (BlockContext bc)
3451
if (this_variable != null)
3452
throw new InternalErrorException (StartLocation.ToString ());
3454
this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3455
this_variable.Type = bc.CurrentType;
3456
this_variable.PrepareForFlowAnalysis (bc);
3459
public bool IsThisAssigned (BlockContext ec)
3461
return this_variable == null || this_variable.IsThisAssigned (ec, this);
3464
public override void Emit (EmitContext ec)
3466
if (Report.Errors > 0)
3472
if (IsCompilerGenerated) {
3473
using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3481
// If `HasReturnLabel' is set, then we already emitted a
3482
// jump to the end of the method, so we must emit a `ret'
3485
// Unfortunately, System.Reflection.Emit automatically emits
3486
// a leave to the end of a finally block. This is a problem
3487
// if no code is following the try/finally block since we may
3488
// jump to a point after the end of the method.
3489
// As a workaround, we're always creating a return label in
3492
if (ec.HasReturnLabel || !unreachable) {
3493
if (ec.HasReturnLabel)
3494
ec.MarkLabel (ec.ReturnLabel);
3496
if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3497
ec.Mark (EndLocation);
3499
if (ec.ReturnType.Kind != MemberKind.Void)
3500
ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3502
ec.Emit (OpCodes.Ret);
3506
} catch (Exception e){
3507
Console.WriteLine ("Exception caught by the compiler while emitting:");
3508
Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3510
Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3517
public class SwitchLabel {
3520
readonly Location loc;
3525
// if expr == null, then it is the default case.
3527
public SwitchLabel (Expression expr, Location l)
3533
public bool IsDefault {
3535
return label == null;
3539
public Expression Label {
3545
public Location Location {
3551
public Constant Converted {
3560
public Label GetILLabel (EmitContext ec)
3562
if (il_label == null){
3563
il_label = ec.DefineLabel ();
3566
return il_label.Value;
3570
// Resolves the expression, reduces it to a literal if possible
3571
// and then converts it to the requested type.
3573
public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3575
Expression e = label.Resolve (ec);
3580
Constant c = e as Constant;
3582
ec.Report.Error (150, loc, "A constant value is expected");
3586
if (allow_nullable && c is NullLiteral) {
3591
converted = c.ImplicitConversionRequired (ec, required_type, loc);
3592
return converted != null;
3595
public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3598
if (converted == null)
3601
label = converted.GetValueAsLiteral ();
3603
ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3604
ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3607
public SwitchLabel Clone (CloneContext clonectx)
3612
return new SwitchLabel (label.Clone (clonectx), loc);
3616
public class SwitchSection {
3617
public readonly List<SwitchLabel> Labels;
3618
public readonly Block Block;
3620
public SwitchSection (List<SwitchLabel> labels, Block block)
3626
public SwitchSection Clone (CloneContext clonectx)
3628
var cloned_labels = new List<SwitchLabel> ();
3630
foreach (SwitchLabel sl in Labels)
3631
cloned_labels.Add (sl.Clone (clonectx));
3633
return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3637
public class Switch : Statement
3639
// structure used to hold blocks of keys while calculating table switch
3640
sealed class LabelsRange : IComparable<LabelsRange>
3642
public readonly long min;
3644
public readonly List<long> label_values;
3646
public LabelsRange (long value)
3649
label_values = new List<long> ();
3650
label_values.Add (value);
3653
public LabelsRange (long min, long max, ICollection<long> values)
3657
this.label_values = new List<long> (values);
3662
return max - min + 1;
3666
public bool AddValue (long value)
3668
var gap = value - min + 1;
3669
// Ensure the range has > 50% occupancy
3670
if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3674
label_values.Add (value);
3678
public int CompareTo (LabelsRange other)
3680
int nLength = label_values.Count;
3681
int nLengthOther = other.label_values.Count;
3682
if (nLengthOther == nLength)
3683
return (int) (other.min - min);
3685
return nLength - nLengthOther;
3689
sealed class LabelMarker : Statement
3692
readonly List<SwitchLabel> labels;
3694
public LabelMarker (Switch s, List<SwitchLabel> labels)
3697
this.labels = labels;
3700
protected override void CloneTo (CloneContext clonectx, Statement target)
3704
protected override void DoEmit (EmitContext ec)
3706
foreach (var l in labels) {
3708
ec.MarkLabel (s.DefaultLabel);
3710
ec.MarkLabel (l.GetILLabel (ec));
3715
public List<SwitchSection> Sections;
3716
public Expression Expr;
3719
// Mapping of all labels to their SwitchLabels
3721
Dictionary<long, SwitchLabel> labels;
3722
Dictionary<string, SwitchLabel> string_labels;
3725
/// The governing switch type
3727
public TypeSpec SwitchType;
3732
Label default_target;
3734
Expression new_expr;
3737
SwitchSection constant_section;
3738
SwitchSection default_section;
3739
SwitchLabel null_section;
3741
Statement simple_stmt;
3742
VariableReference value;
3743
ExpressionStatement string_dictionary;
3744
FieldExpr switch_cache_field;
3745
ExplicitBlock block;
3748
// Nullable Types support
3750
Nullable.Unwrap unwrap;
3752
public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3760
public ExplicitBlock Block {
3766
public Label DefaultLabel {
3768
return default_target;
3772
public bool GotDefault {
3774
return default_section != null;
3778
public bool IsNullable {
3780
return unwrap != null;
3785
// Determines the governing type for a switch. The returned
3786
// expression might be the expression from the switch, or an
3787
// expression that includes any potential conversions to
3789
Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3791
switch (expr.Type.BuiltinType) {
3792
case BuiltinTypeSpec.Type.Byte:
3793
case BuiltinTypeSpec.Type.SByte:
3794
case BuiltinTypeSpec.Type.UShort:
3795
case BuiltinTypeSpec.Type.Short:
3796
case BuiltinTypeSpec.Type.UInt:
3797
case BuiltinTypeSpec.Type.Int:
3798
case BuiltinTypeSpec.Type.ULong:
3799
case BuiltinTypeSpec.Type.Long:
3800
case BuiltinTypeSpec.Type.Char:
3801
case BuiltinTypeSpec.Type.String:
3802
case BuiltinTypeSpec.Type.Bool:
3806
if (expr.Type.IsEnum)
3810
// Try to find a *user* defined implicit conversion.
3812
// If there is no implicit conversion, or if there are multiple
3813
// conversions, we have to report an error
3815
Expression converted = null;
3816
foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3819
e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3824
// Ignore over-worked ImplicitUserConversions that do
3825
// an implicit conversion in addition to the user conversion.
3827
if (!(e is UserCast))
3830
if (converted != null){
3831
ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3840
public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3842
// LAMESPEC: For some reason it does not contain bool which looks like csc bug
3858
// Performs the basic sanity checks on the switch statement
3859
// (looks for duplicate keys and non-constant expressions).
3861
// It also returns a hashtable with the keys that we will later
3862
// use to compute the switch tables
3864
bool CheckSwitch (ResolveContext ec)
3867
if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3868
string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3870
labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3872
foreach (SwitchSection ss in Sections){
3873
foreach (SwitchLabel sl in ss.Labels){
3875
if (default_section != null){
3876
sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3879
default_section = ss;
3883
if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3889
if (string_labels != null) {
3890
string s = sl.Converted.GetValue () as string;
3894
string_labels.Add (s, sl);
3896
if (sl.Converted is NullLiteral) {
3899
labels.Add (sl.Converted.GetValueAsLong (), sl);
3902
} catch (ArgumentException) {
3903
if (string_labels != null)
3904
sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3906
sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3916
// This method emits code for a lookup-based switch statement (non-string)
3917
// Basically it groups the cases into blocks that are at least half full,
3918
// and then spits out individual lookup opcodes for each block.
3919
// It emits the longest blocks first, and short blocks are just
3920
// handled with direct compares.
3922
void EmitTableSwitch (EmitContext ec, Expression val)
3924
Label lbl_default = default_target;
3926
if (labels != null && labels.Count > 0) {
3927
List<LabelsRange> ranges;
3928
if (string_labels != null) {
3929
// We have done all hard work for string already
3930
// setup single range only
3931
ranges = new List<LabelsRange> (1);
3932
ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3934
var element_keys = new long[labels.Count];
3935
labels.Keys.CopyTo (element_keys, 0);
3936
Array.Sort (element_keys);
3939
// Build possible ranges of switch labes to reduce number
3942
ranges = new List<LabelsRange> (element_keys.Length);
3943
var range = new LabelsRange (element_keys[0]);
3945
for (int i = 1; i < element_keys.Length; ++i) {
3946
var l = element_keys[i];
3947
if (range.AddValue (l))
3950
range = new LabelsRange (l);
3954
// sort the blocks so we can tackle the largest ones first
3958
TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3960
for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3961
LabelsRange kb = ranges[range_index];
3962
lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3964
// Optimize small ranges using simple equality check
3965
if (kb.Range <= 2) {
3966
foreach (var key in kb.label_values) {
3967
SwitchLabel sl = labels[key];
3968
if (sl.Converted.IsDefaultValue) {
3969
val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3972
sl.Converted.Emit (ec);
3973
ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3977
// TODO: if all the keys in the block are the same and there are
3978
// no gaps/defaults then just use a range-check.
3979
if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3980
// TODO: optimize constant/I4 cases
3982
// check block range (could be > 2^31)
3984
ec.EmitLong (kb.min);
3985
ec.Emit (OpCodes.Blt, lbl_default);
3988
ec.EmitLong (kb.max);
3989
ec.Emit (OpCodes.Bgt, lbl_default);
3994
ec.EmitLong (kb.min);
3995
ec.Emit (OpCodes.Sub);
3998
ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4002
int first = (int) kb.min;
4005
ec.Emit (OpCodes.Sub);
4006
} else if (first < 0) {
4007
ec.EmitInt (-first);
4008
ec.Emit (OpCodes.Add);
4012
// first, build the list of labels for the switch
4014
long cJumps = kb.Range;
4015
Label[] switch_labels = new Label[cJumps];
4016
for (int iJump = 0; iJump < cJumps; iJump++) {
4017
var key = kb.label_values[iKey];
4018
if (key == kb.min + iJump) {
4019
switch_labels[iJump] = labels[key].GetILLabel (ec);
4022
switch_labels[iJump] = lbl_default;
4026
// emit the switch opcode
4027
ec.Emit (OpCodes.Switch, switch_labels);
4030
// mark the default for this block
4031
if (range_index != 0)
4032
ec.MarkLabel (lbl_default);
4035
// the last default just goes to the end
4036
if (ranges.Count > 0)
4037
ec.Emit (OpCodes.Br, lbl_default);
4040
// now emit the code for the sections
4041
bool found_default = false;
4043
foreach (SwitchSection ss in Sections) {
4044
foreach (SwitchLabel sl in ss.Labels) {
4046
ec.MarkLabel (lbl_default);
4047
found_default = true;
4048
if (null_section == null)
4049
ec.MarkLabel (null_target);
4050
} else if (sl.Converted.IsNull) {
4051
ec.MarkLabel (null_target);
4054
ec.MarkLabel (sl.GetILLabel (ec));
4060
if (!found_default) {
4061
ec.MarkLabel (lbl_default);
4062
if (null_section == null) {
4063
ec.MarkLabel (null_target);
4068
SwitchLabel FindLabel (Constant value)
4070
SwitchLabel sl = null;
4072
if (string_labels != null) {
4073
string s = value.GetValue () as string;
4075
if (null_section != null)
4077
else if (default_section != null)
4078
sl = default_section.Labels[0];
4080
string_labels.TryGetValue (s, out sl);
4083
if (value is NullLiteral) {
4086
labels.TryGetValue (value.GetValueAsLong (), out sl);
4093
SwitchSection FindSection (SwitchLabel label)
4095
foreach (SwitchSection ss in Sections){
4096
foreach (SwitchLabel sl in ss.Labels){
4105
public override bool Resolve (BlockContext ec)
4107
Expr = Expr.Resolve (ec);
4111
new_expr = SwitchGoverningType (ec, Expr);
4113
if (new_expr == null && Expr.Type.IsNullableType) {
4114
unwrap = Nullable.Unwrap.Create (Expr, false);
4118
new_expr = SwitchGoverningType (ec, unwrap);
4121
if (new_expr == null){
4122
ec.Report.Error (151, loc,
4123
"A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4124
TypeManager.CSharpName (Expr.Type));
4129
SwitchType = new_expr.Type;
4131
if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4132
ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4136
if (!CheckSwitch (ec))
4139
Switch old_switch = ec.Switch;
4141
ec.Switch.SwitchType = SwitchType;
4143
ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4145
var constant = new_expr as Constant;
4146
if (constant != null) {
4148
SwitchLabel label = FindLabel (constant);
4150
constant_section = FindSection (label);
4152
if (constant_section == null)
4153
constant_section = default_section;
4156
// Store switch expression for comparission purposes
4158
value = new_expr as VariableReference;
4160
value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4165
foreach (SwitchSection ss in Sections){
4167
ec.CurrentBranching.CreateSibling (
4168
null, FlowBranching.SiblingType.SwitchSection);
4172
if (is_constant && (ss != constant_section)) {
4173
// If we're a constant switch, we're only emitting
4174
// one single section - mark all the others as
4176
ec.CurrentBranching.CurrentUsageVector.Goto ();
4177
if (!ss.Block.ResolveUnreachable (ec, true)) {
4181
if (!ss.Block.Resolve (ec))
4186
if (default_section == null)
4187
ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
4189
ec.EndFlowBranching ();
4190
ec.Switch = old_switch;
4196
if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4197
if (string_labels.Count < 7)
4198
ResolveSimpleSwitch (ec);
4200
ResolveStringSwitchMap (ec);
4201
} else if (labels.Count < 3 && !IsNullable) {
4202
ResolveSimpleSwitch (ec);
4209
public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4211
var sl = FindLabel (value);
4214
FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4221
// Prepares switch using simple if/else comparison for small label count (4 + optional default)
4223
void ResolveSimpleSwitch (BlockContext bc)
4225
simple_stmt = default_section != null ? default_section.Block : null;
4227
for (int i = Sections.Count - 1; i >= 0; --i) {
4228
var s = Sections[i];
4230
if (s == default_section) {
4231
s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4235
s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4237
Expression cond = null;
4238
for (int ci = 0; ci < s.Labels.Count; ++ci) {
4239
var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted, loc);
4242
cond = new Binary (Binary.Operator.LogicalOr, cond, e, loc);
4249
// Compiler generated, hide from symbol file
4251
simple_stmt = new If (cond, s.Block, simple_stmt, Location.Null);
4254
// It's null for empty switch
4255
if (simple_stmt != null)
4256
simple_stmt.Resolve (bc);
4260
// Converts string switch into string hashtable
4262
void ResolveStringSwitchMap (ResolveContext ec)
4264
FullNamedExpression string_dictionary_type;
4265
if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4266
string_dictionary_type = new TypeExpression (
4267
ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4268
new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4270
} else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4271
string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4273
ec.Module.PredefinedTypes.Dictionary.Resolve ();
4277
var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4278
Field field = new Field (ctype, string_dictionary_type,
4279
Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4280
new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4281
if (!field.Define ())
4283
ctype.AddField (field);
4285
var init = new List<Expression> ();
4287
labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4288
string value = null;
4289
foreach (SwitchSection section in Sections) {
4290
bool contains_label = false;
4291
foreach (SwitchLabel sl in section.Labels) {
4292
if (sl.IsDefault || sl.Converted.IsNull)
4295
if (!contains_label) {
4296
labels.Add (counter, sl);
4297
contains_label = true;
4300
value = (string) sl.Converted.GetValue ();
4301
var init_args = new List<Expression> (2);
4302
init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4304
sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4305
init_args.Add (sl.Converted);
4307
init.Add (new CollectionElementInitializer (init_args, loc));
4311
// Don't add empty sections
4317
Arguments args = new Arguments (1);
4318
args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4319
Expression initializer = new NewInitialize (string_dictionary_type, args,
4320
new CollectionOrObjectInitializers (init, loc), loc);
4322
switch_cache_field = new FieldExpr (field, loc);
4323
string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4326
void DoEmitStringSwitch (EmitContext ec)
4328
Label l_initialized = ec.DefineLabel ();
4331
// Skip initialization when value is null
4333
value.EmitBranchable (ec, null_target, false);
4336
// Check if string dictionary is initialized and initialize
4338
switch_cache_field.EmitBranchable (ec, l_initialized, true);
4339
using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4340
string_dictionary.EmitStatement (ec);
4342
ec.MarkLabel (l_initialized);
4344
LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4346
ResolveContext rc = new ResolveContext (ec.MemberContext);
4348
if (switch_cache_field.Type.IsGeneric) {
4349
Arguments get_value_args = new Arguments (2);
4350
get_value_args.Add (new Argument (value));
4351
get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4352
Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4353
if (get_item == null)
4357
// A value was not found, go to default case
4359
get_item.EmitBranchable (ec, default_target, false);
4361
Arguments get_value_args = new Arguments (1);
4362
get_value_args.Add (new Argument (value));
4364
Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4365
if (get_item == null)
4368
LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4369
get_item_object.EmitAssign (ec, get_item, true, false);
4370
ec.Emit (OpCodes.Brfalse, default_target);
4372
ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4373
new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4375
get_item_int.EmitStatement (ec);
4376
get_item_object.Release (ec);
4379
EmitTableSwitch (ec, string_switch_variable);
4380
string_switch_variable.Release (ec);
4383
protected override void DoEmit (EmitContext ec)
4385
// Workaround broken flow-analysis
4386
block.HasUnreachableClosingBrace = true;
4389
// Needed to emit anonymous storey initialization
4390
// Otherwise it does not contain any statements for now
4394
default_target = ec.DefineLabel ();
4395
null_target = ec.DefineLabel ();
4398
unwrap.EmitCheck (ec);
4399
ec.Emit (OpCodes.Brfalse, null_target);
4400
value.EmitAssign (ec, new_expr, false, false);
4401
} else if (new_expr != value && !is_constant) {
4402
value.EmitAssign (ec, new_expr, false, false);
4406
// Setup the codegen context
4408
Label old_end = ec.LoopEnd;
4409
Switch old_switch = ec.Switch;
4411
ec.LoopEnd = ec.DefineLabel ();
4416
if (constant_section != null)
4417
constant_section.Block.Emit (ec);
4418
} else if (string_dictionary != null) {
4419
DoEmitStringSwitch (ec);
4420
} else if (simple_stmt != null) {
4421
simple_stmt.Emit (ec);
4423
EmitTableSwitch (ec, value);
4426
// Restore context state.
4427
ec.MarkLabel (ec.LoopEnd);
4430
// Restore the previous context
4432
ec.LoopEnd = old_end;
4433
ec.Switch = old_switch;
4436
protected override void CloneTo (CloneContext clonectx, Statement t)
4438
Switch target = (Switch) t;
4440
target.Expr = Expr.Clone (clonectx);
4441
target.Sections = new List<SwitchSection> ();
4442
foreach (SwitchSection ss in Sections){
4443
target.Sections.Add (ss.Clone (clonectx));
4447
public override object Accept (StructuralVisitor visitor)
4449
return visitor.Visit (this);
4453
// A place where execution can restart in an iterator
4454
public abstract class ResumableStatement : Statement
4457
protected Label resume_point;
4459
public Label PrepareForEmit (EmitContext ec)
4463
resume_point = ec.DefineLabel ();
4465
return resume_point;
4468
public virtual Label PrepareForDispose (EmitContext ec, Label end)
4473
public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4478
public abstract class TryFinallyBlock : ExceptionStatement
4480
protected Statement stmt;
4481
Label dispose_try_block;
4482
bool prepared_for_dispose, emitted_dispose;
4483
Method finally_host;
4485
protected TryFinallyBlock (Statement stmt, Location loc)
4493
public Statement Statement {
4501
protected abstract void EmitTryBody (EmitContext ec);
4502
public abstract void EmitFinallyBody (EmitContext ec);
4504
public override Label PrepareForDispose (EmitContext ec, Label end)
4506
if (!prepared_for_dispose) {
4507
prepared_for_dispose = true;
4508
dispose_try_block = ec.DefineLabel ();
4510
return dispose_try_block;
4513
protected sealed override void DoEmit (EmitContext ec)
4515
EmitTryBodyPrepare (ec);
4518
ec.BeginFinallyBlock ();
4520
Label start_finally = ec.DefineLabel ();
4521
if (resume_points != null) {
4522
var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4524
ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4525
ec.Emit (OpCodes.Brfalse_S, start_finally);
4526
ec.Emit (OpCodes.Endfinally);
4529
ec.MarkLabel (start_finally);
4531
if (finally_host != null) {
4532
finally_host.Define ();
4533
finally_host.Emit ();
4535
// Now it's safe to add, to close it properly and emit sequence points
4536
finally_host.Parent.AddMember (finally_host);
4538
var ce = new CallEmitter ();
4539
ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4540
ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4542
EmitFinallyBody (ec);
4545
ec.EndExceptionBlock ();
4548
public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4550
if (emitted_dispose)
4553
emitted_dispose = true;
4555
Label end_of_try = ec.DefineLabel ();
4557
// Ensure that the only way we can get into this code is through a dispatcher
4558
if (have_dispatcher)
4559
ec.Emit (OpCodes.Br, end);
4561
ec.BeginExceptionBlock ();
4563
ec.MarkLabel (dispose_try_block);
4565
Label[] labels = null;
4566
for (int i = 0; i < resume_points.Count; ++i) {
4567
ResumableStatement s = resume_points[i];
4568
Label ret = s.PrepareForDispose (ec, end_of_try);
4569
if (ret.Equals (end_of_try) && labels == null)
4571
if (labels == null) {
4572
labels = new Label[resume_points.Count];
4573
for (int j = 0; j < i; ++j)
4574
labels[j] = end_of_try;
4579
if (labels != null) {
4581
for (j = 1; j < labels.Length; ++j)
4582
if (!labels[0].Equals (labels[j]))
4584
bool emit_dispatcher = j < labels.Length;
4586
if (emit_dispatcher) {
4587
ec.Emit (OpCodes.Ldloc, pc);
4588
ec.EmitInt (first_resume_pc);
4589
ec.Emit (OpCodes.Sub);
4590
ec.Emit (OpCodes.Switch, labels);
4593
foreach (ResumableStatement s in resume_points)
4594
s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4597
ec.MarkLabel (end_of_try);
4599
ec.BeginFinallyBlock ();
4601
if (finally_host != null) {
4602
var ce = new CallEmitter ();
4603
ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4604
ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4606
EmitFinallyBody (ec);
4609
ec.EndExceptionBlock ();
4612
public override bool Resolve (BlockContext bc)
4615
// Finally block inside iterator is called from MoveNext and
4616
// Dispose methods that means we need to lift the block into
4617
// newly created host method to emit the body only once. The
4618
// original block then simply calls the newly generated method.
4620
if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4621
var b = stmt as Block;
4622
if (b != null && b.Explicit.HasYield) {
4623
finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4627
return base.Resolve (bc);
4632
// Base class for blocks using exception handling
4634
public abstract class ExceptionStatement : ResumableStatement
4639
protected List<ResumableStatement> resume_points;
4640
protected int first_resume_pc;
4642
protected ExceptionStatement (Location loc)
4647
protected virtual void EmitTryBodyPrepare (EmitContext ec)
4649
StateMachineInitializer state_machine = null;
4650
if (resume_points != null) {
4651
state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4653
ec.EmitInt ((int) IteratorStorey.State.Running);
4654
ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4657
ec.BeginExceptionBlock ();
4659
if (resume_points != null) {
4660
ec.MarkLabel (resume_point);
4662
// For normal control flow, we want to fall-through the Switch
4663
// So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4664
ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4665
ec.EmitInt (first_resume_pc);
4666
ec.Emit (OpCodes.Sub);
4668
Label[] labels = new Label[resume_points.Count];
4669
for (int i = 0; i < resume_points.Count; ++i)
4670
labels[i] = resume_points[i].PrepareForEmit (ec);
4671
ec.Emit (OpCodes.Switch, labels);
4675
public void SomeCodeFollows ()
4678
code_follows = true;
4682
public override bool Resolve (BlockContext ec)
4685
// System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4686
// So, ensure there's some IL code after this statement.
4687
if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4688
ec.NeedReturnLabel ();
4693
public void AddResumePoint (ResumableStatement stmt, int pc)
4695
if (resume_points == null) {
4696
resume_points = new List<ResumableStatement> ();
4697
first_resume_pc = pc;
4700
if (pc != first_resume_pc + resume_points.Count)
4701
throw new InternalErrorException ("missed an intervening AddResumePoint?");
4703
resume_points.Add (stmt);
4708
public class Lock : TryFinallyBlock
4711
TemporaryVariableReference expr_copy;
4712
TemporaryVariableReference lock_taken;
4714
public Lock (Expression expr, Statement stmt, Location loc)
4720
public Expression Expr {
4726
public override bool Resolve (BlockContext ec)
4728
expr = expr.Resolve (ec);
4732
if (!TypeSpec.IsReferenceType (expr.Type)) {
4733
ec.Report.Error (185, loc,
4734
"`{0}' is not a reference type as required by the lock statement",
4735
expr.Type.GetSignatureForError ());
4738
if (expr.Type.IsGenericParameter) {
4739
expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4742
VariableReference lv = expr as VariableReference;
4745
locked = lv.IsLockedByStatement;
4746
lv.IsLockedByStatement = true;
4752
using (ec.Set (ResolveContext.Options.LockScope)) {
4753
ec.StartFlowBranching (this);
4754
Statement.Resolve (ec);
4755
ec.EndFlowBranching ();
4759
lv.IsLockedByStatement = locked;
4765
// Have to keep original lock value around to unlock same location
4766
// in the case the original has changed or is null
4768
expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4769
expr_copy.Resolve (ec);
4772
// Ensure Monitor methods are available
4774
if (ResolvePredefinedMethods (ec) > 1) {
4775
lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4776
lock_taken.Resolve (ec);
4782
protected override void EmitTryBodyPrepare (EmitContext ec)
4784
expr_copy.EmitAssign (ec, expr);
4786
if (lock_taken != null) {
4788
// Initialize ref variable
4790
lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4793
// Monitor.Enter (expr_copy)
4795
expr_copy.Emit (ec);
4796
ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4799
base.EmitTryBodyPrepare (ec);
4802
protected override void EmitTryBody (EmitContext ec)
4805
// Monitor.Enter (expr_copy, ref lock_taken)
4807
if (lock_taken != null) {
4808
expr_copy.Emit (ec);
4809
lock_taken.LocalInfo.CreateBuilder (ec);
4810
lock_taken.AddressOf (ec, AddressOp.Load);
4811
ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4814
Statement.Emit (ec);
4817
public override void EmitFinallyBody (EmitContext ec)
4820
// if (lock_taken) Monitor.Exit (expr_copy)
4822
Label skip = ec.DefineLabel ();
4824
if (lock_taken != null) {
4825
lock_taken.Emit (ec);
4826
ec.Emit (OpCodes.Brfalse_S, skip);
4829
expr_copy.Emit (ec);
4830
var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4832
ec.Emit (OpCodes.Call, m);
4834
ec.MarkLabel (skip);
4837
int ResolvePredefinedMethods (ResolveContext rc)
4839
// Try 4.0 Monitor.Enter (object, ref bool) overload first
4840
var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4844
m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4848
rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4852
protected override void CloneTo (CloneContext clonectx, Statement t)
4854
Lock target = (Lock) t;
4856
target.expr = expr.Clone (clonectx);
4857
target.stmt = Statement.Clone (clonectx);
4860
public override object Accept (StructuralVisitor visitor)
4862
return visitor.Visit (this);
4867
public class Unchecked : Statement {
4870
public Unchecked (Block b, Location loc)
4877
public override bool Resolve (BlockContext ec)
4879
using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4880
return Block.Resolve (ec);
4883
protected override void DoEmit (EmitContext ec)
4885
using (ec.With (EmitContext.Options.CheckedScope, false))
4889
protected override void CloneTo (CloneContext clonectx, Statement t)
4891
Unchecked target = (Unchecked) t;
4893
target.Block = clonectx.LookupBlock (Block);
4896
public override object Accept (StructuralVisitor visitor)
4898
return visitor.Visit (this);
4902
public class Checked : Statement {
4905
public Checked (Block b, Location loc)
4908
b.Unchecked = false;
4912
public override bool Resolve (BlockContext ec)
4914
using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4915
return Block.Resolve (ec);
4918
protected override void DoEmit (EmitContext ec)
4920
using (ec.With (EmitContext.Options.CheckedScope, true))
4924
protected override void CloneTo (CloneContext clonectx, Statement t)
4926
Checked target = (Checked) t;
4928
target.Block = clonectx.LookupBlock (Block);
4931
public override object Accept (StructuralVisitor visitor)
4933
return visitor.Visit (this);
4937
public class Unsafe : Statement {
4940
public Unsafe (Block b, Location loc)
4943
Block.Unsafe = true;
4947
public override bool Resolve (BlockContext ec)
4949
if (ec.CurrentIterator != null)
4950
ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4952
using (ec.Set (ResolveContext.Options.UnsafeScope))
4953
return Block.Resolve (ec);
4956
protected override void DoEmit (EmitContext ec)
4961
protected override void CloneTo (CloneContext clonectx, Statement t)
4963
Unsafe target = (Unsafe) t;
4965
target.Block = clonectx.LookupBlock (Block);
4968
public override object Accept (StructuralVisitor visitor)
4970
return visitor.Visit (this);
4977
public class Fixed : Statement
4979
abstract class Emitter : ShimExpression
4981
protected LocalVariable vi;
4983
protected Emitter (Expression expr, LocalVariable li)
4989
public abstract void EmitExit (EmitContext ec);
4992
class ExpressionEmitter : Emitter {
4993
public ExpressionEmitter (Expression converted, LocalVariable li) :
4994
base (converted, li)
4998
protected override Expression DoResolve (ResolveContext rc)
5000
throw new NotImplementedException ();
5003
public override void Emit (EmitContext ec) {
5005
// Store pointer in pinned location
5011
public override void EmitExit (EmitContext ec)
5014
ec.Emit (OpCodes.Conv_U);
5019
class StringEmitter : Emitter
5021
LocalVariable pinned_string;
5023
public StringEmitter (Expression expr, LocalVariable li, Location loc)
5028
protected override Expression DoResolve (ResolveContext rc)
5030
pinned_string = new LocalVariable (vi.Block, "$pinned",
5031
LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5033
pinned_string.Type = rc.BuiltinTypes.String;
5035
eclass = ExprClass.Variable;
5036
type = rc.BuiltinTypes.Int;
5040
public override void Emit (EmitContext ec)
5042
pinned_string.CreateBuilder (ec);
5045
pinned_string.EmitAssign (ec);
5047
// TODO: Should use Binary::Add
5048
pinned_string.Emit (ec);
5049
ec.Emit (OpCodes.Conv_I);
5051
var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5055
PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5056
//pe.InstanceExpression = pinned_string;
5057
pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5059
ec.Emit (OpCodes.Add);
5063
public override void EmitExit (EmitContext ec)
5066
pinned_string.EmitAssign (ec);
5071
public class VariableDeclaration : BlockVariableDeclaration
5073
public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5078
protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5080
if (!Variable.Type.IsPointer && li == Variable) {
5081
bc.Report.Error (209, TypeExpression.Location,
5082
"The type of locals declared in a fixed statement must be a pointer type");
5087
// The rules for the possible declarators are pretty wise,
5088
// but the production on the grammar is more concise.
5090
// So we have to enforce these rules here.
5092
// We do not resolve before doing the case 1 test,
5093
// because the grammar is explicit in that the token &
5094
// is present, so we need to test for this particular case.
5097
if (initializer is Cast) {
5098
bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5102
initializer = initializer.Resolve (bc);
5104
if (initializer == null)
5110
if (initializer.Type.IsArray) {
5111
TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5114
// Provided that array_type is unmanaged,
5116
if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5120
// and T* is implicitly convertible to the
5121
// pointer type given in the fixed statement.
5123
ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5125
Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5126
if (converted == null)
5130
// fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5132
converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5133
new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
5134
new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
5135
new NullLiteral (loc),
5138
converted = converted.Resolve (bc);
5140
return new ExpressionEmitter (converted, li);
5146
if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5147
return new StringEmitter (initializer, li, loc).Resolve (bc);
5150
// Case 3: fixed buffer
5151
if (initializer is FixedBufferPtr) {
5152
return new ExpressionEmitter (initializer, li);
5156
// Case 4: & object.
5158
bool already_fixed = true;
5159
Unary u = initializer as Unary;
5160
if (u != null && u.Oper == Unary.Operator.AddressOf) {
5161
IVariableReference vr = u.Expr as IVariableReference;
5162
if (vr == null || !vr.IsFixed) {
5163
already_fixed = false;
5167
if (already_fixed) {
5168
bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5171
initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5172
return new ExpressionEmitter (initializer, li);
5177
VariableDeclaration decl;
5178
Statement statement;
5181
public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5190
public Statement Statement {
5196
public BlockVariableDeclaration Variables {
5204
public override bool Resolve (BlockContext ec)
5206
using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5207
if (!decl.Resolve (ec))
5211
ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5212
bool ok = statement.Resolve (ec);
5213
bool flow_unreachable = ec.EndFlowBranching ();
5214
has_ret = flow_unreachable;
5219
protected override void DoEmit (EmitContext ec)
5221
decl.Variable.CreateBuilder (ec);
5222
decl.Initializer.Emit (ec);
5223
if (decl.Declarators != null) {
5224
foreach (var d in decl.Declarators) {
5225
d.Variable.CreateBuilder (ec);
5226
d.Initializer.Emit (ec);
5230
statement.Emit (ec);
5236
// Clear the pinned variable
5238
((Emitter) decl.Initializer).EmitExit (ec);
5239
if (decl.Declarators != null) {
5240
foreach (var d in decl.Declarators) {
5241
((Emitter)d.Initializer).EmitExit (ec);
5246
protected override void CloneTo (CloneContext clonectx, Statement t)
5248
Fixed target = (Fixed) t;
5250
target.decl = (VariableDeclaration) decl.Clone (clonectx);
5251
target.statement = statement.Clone (clonectx);
5254
public override object Accept (StructuralVisitor visitor)
5256
return visitor.Visit (this);
5260
public class Catch : Statement
5264
FullNamedExpression type_expr;
5265
CompilerAssign assign;
5268
public Catch (Block block, Location loc)
5276
public Block Block {
5282
public TypeSpec CatchType {
5288
public bool IsGeneral {
5290
return type_expr == null;
5294
public FullNamedExpression TypeExpression {
5303
public LocalVariable Variable {
5314
protected override void DoEmit (EmitContext ec)
5317
ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5319
ec.BeginCatchBlock (CatchType);
5322
li.CreateBuilder (ec);
5325
// Special case hoisted catch variable, we have to use a temporary variable
5326
// to pass via anonymous storey initialization with the value still on top
5329
if (li.HoistedVariant != null) {
5330
LocalTemporary lt = new LocalTemporary (li.Type);
5333
// switch to assigning from the temporary variable and not from top of the stack
5334
assign.UpdateSource (lt);
5337
ec.Emit (OpCodes.Pop);
5343
public override bool Resolve (BlockContext ec)
5345
using (ec.With (ResolveContext.Options.CatchScope, true)) {
5346
if (type_expr != null) {
5347
type = type_expr.ResolveAsType (ec);
5351
if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5352
ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5353
} else if (li != null) {
5355
li.PrepareForFlowAnalysis (ec);
5357
// source variable is at the top of the stack
5358
Expression source = new EmptyExpression (li.Type);
5359
if (li.Type.IsGenericParameter)
5360
source = new UnboxCast (source, li.Type);
5363
// Uses Location.Null to hide from symbol file
5365
assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5366
Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5370
return Block.Resolve (ec);
5374
protected override void CloneTo (CloneContext clonectx, Statement t)
5376
Catch target = (Catch) t;
5378
if (type_expr != null)
5379
target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5381
target.block = clonectx.LookupBlock (block);
5385
public class TryFinally : TryFinallyBlock
5389
public Statement Stmt {
5390
get { return this.stmt; }
5394
get { return this.fini; }
5397
public TryFinally (Statement stmt, Block fini, Location loc)
5403
public Block Finallyblock {
5409
public override bool Resolve (BlockContext ec)
5413
ec.StartFlowBranching (this);
5415
if (!stmt.Resolve (ec))
5419
ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5421
using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5422
if (!fini.Resolve (ec))
5426
ec.EndFlowBranching ();
5428
ok &= base.Resolve (ec);
5433
protected override void EmitTryBody (EmitContext ec)
5438
public override void EmitFinallyBody (EmitContext ec)
5443
protected override void CloneTo (CloneContext clonectx, Statement t)
5445
TryFinally target = (TryFinally) t;
5447
target.stmt = (Statement) stmt.Clone (clonectx);
5449
target.fini = clonectx.LookupBlock (fini);
5452
public override object Accept (StructuralVisitor visitor)
5454
return visitor.Visit (this);
5458
public class TryCatch : ExceptionStatement
5461
List<Catch> clauses;
5462
readonly bool inside_try_finally;
5464
public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5468
this.clauses = catch_clauses;
5469
this.inside_try_finally = inside_try_finally;
5472
public List<Catch> Clauses {
5478
public bool IsTryCatchFinally {
5480
return inside_try_finally;
5484
public override bool Resolve (BlockContext ec)
5488
ec.StartFlowBranching (this);
5490
if (!Block.Resolve (ec))
5493
for (int i = 0; i < clauses.Count; ++i) {
5495
ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5497
if (!c.Resolve (ec)) {
5502
TypeSpec resolved_type = c.CatchType;
5503
for (int ii = 0; ii < clauses.Count; ++ii) {
5507
if (clauses[ii].IsGeneral) {
5508
if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5511
if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5514
if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5517
ec.Report.Warning (1058, 1, c.loc,
5518
"A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5526
var ct = clauses[ii].CatchType;
5530
if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5531
ec.Report.Error (160, c.loc,
5532
"A previous catch clause already catches all exceptions of this or a super type `{0}'",
5533
ct.GetSignatureForError ());
5539
ec.EndFlowBranching ();
5541
return base.Resolve (ec) && ok;
5544
protected sealed override void DoEmit (EmitContext ec)
5546
if (!inside_try_finally)
5547
EmitTryBodyPrepare (ec);
5551
foreach (Catch c in clauses)
5554
if (!inside_try_finally)
5555
ec.EndExceptionBlock ();
5558
protected override void CloneTo (CloneContext clonectx, Statement t)
5560
TryCatch target = (TryCatch) t;
5562
target.Block = clonectx.LookupBlock (Block);
5563
if (clauses != null){
5564
target.clauses = new List<Catch> ();
5565
foreach (Catch c in clauses)
5566
target.clauses.Add ((Catch) c.Clone (clonectx));
5570
public override object Accept (StructuralVisitor visitor)
5572
return visitor.Visit (this);
5576
public class Using : TryFinallyBlock
5578
public class VariableDeclaration : BlockVariableDeclaration
5580
Statement dispose_call;
5582
public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5587
public VariableDeclaration (LocalVariable li, Location loc)
5593
public VariableDeclaration (Expression expr)
5596
loc = expr.Location;
5602
public bool IsNested { get; private set; }
5606
public void EmitDispose (EmitContext ec)
5608
dispose_call.Emit (ec);
5611
public override bool Resolve (BlockContext bc)
5616
return base.Resolve (bc, false);
5619
public Expression ResolveExpression (BlockContext bc)
5621
var e = Initializer.Resolve (bc);
5625
li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5626
Initializer = ResolveInitializer (bc, Variable, e);
5630
protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5632
if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5633
initializer = initializer.Resolve (bc);
5634
if (initializer == null)
5637
// Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5638
Arguments args = new Arguments (1);
5639
args.Add (new Argument (initializer));
5640
initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5641
if (initializer == null)
5644
var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5645
dispose_call = CreateDisposeCall (bc, var);
5646
dispose_call.Resolve (bc);
5648
return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5651
if (li == Variable) {
5652
CheckIDiposableConversion (bc, li, initializer);
5653
dispose_call = CreateDisposeCall (bc, li);
5654
dispose_call.Resolve (bc);
5657
return base.ResolveInitializer (bc, li, initializer);
5660
protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5664
if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5665
if (type.IsNullableType) {
5666
// it's handled in CreateDisposeCall
5670
bc.Report.SymbolRelatedToPreviousError (type);
5671
var loc = type_expr == null ? initializer.Location : type_expr.Location;
5672
bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5673
type.GetSignatureForError ());
5679
protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5681
var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5683
var loc = lv.Location;
5685
var idt = bc.BuiltinTypes.IDisposable;
5686
var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5688
var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5689
dispose_mg.InstanceExpression = type.IsNullableType ?
5690
new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5694
// Hide it from symbol file via null location
5696
Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5698
// Add conditional call when disposing possible null variable
5699
if (!type.IsStruct || type.IsNullableType)
5700
dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, dispose.loc);
5705
public void ResolveDeclaratorInitializer (BlockContext bc)
5707
Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5710
public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5712
for (int i = declarators.Count - 1; i >= 0; --i) {
5713
var d = declarators [i];
5714
var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5715
vd.Initializer = d.Initializer;
5717
vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5718
vd.dispose_call.Resolve (bc);
5720
stmt = new Using (vd, stmt, d.Variable.Location);
5727
public override object Accept (StructuralVisitor visitor)
5729
return visitor.Visit (this);
5733
VariableDeclaration decl;
5735
public Using (VariableDeclaration decl, Statement stmt, Location loc)
5741
public Using (Expression expr, Statement stmt, Location loc)
5744
this.decl = new VariableDeclaration (expr);
5749
public Expression Expr {
5751
return decl.Variable == null ? decl.Initializer : null;
5755
public BlockVariableDeclaration Variables {
5763
public override void Emit (EmitContext ec)
5766
// Don't emit sequence point it will be set on variable declaration
5771
protected override void EmitTryBodyPrepare (EmitContext ec)
5774
base.EmitTryBodyPrepare (ec);
5777
protected override void EmitTryBody (EmitContext ec)
5782
public override void EmitFinallyBody (EmitContext ec)
5784
decl.EmitDispose (ec);
5787
public override bool Resolve (BlockContext ec)
5789
VariableReference vr;
5790
bool vr_locked = false;
5792
using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5793
if (decl.Variable == null) {
5794
vr = decl.ResolveExpression (ec) as VariableReference;
5796
vr_locked = vr.IsLockedByStatement;
5797
vr.IsLockedByStatement = true;
5800
if (decl.IsNested) {
5801
decl.ResolveDeclaratorInitializer (ec);
5803
if (!decl.Resolve (ec))
5806
if (decl.Declarators != null) {
5807
stmt = decl.RewriteUsingDeclarators (ec, stmt);
5815
ec.StartFlowBranching (this);
5819
ec.EndFlowBranching ();
5822
vr.IsLockedByStatement = vr_locked;
5829
protected override void CloneTo (CloneContext clonectx, Statement t)
5831
Using target = (Using) t;
5833
target.decl = (VariableDeclaration) decl.Clone (clonectx);
5834
target.stmt = stmt.Clone (clonectx);
5837
public override object Accept (StructuralVisitor visitor)
5839
return visitor.Visit (this);
5844
/// Implementation of the foreach C# statement
5846
public class Foreach : Statement
5848
abstract class IteratorStatement : Statement
5850
protected readonly Foreach for_each;
5852
protected IteratorStatement (Foreach @foreach)
5854
this.for_each = @foreach;
5855
this.loc = @foreach.expr.Location;
5858
protected override void CloneTo (CloneContext clonectx, Statement target)
5860
throw new NotImplementedException ();
5863
public override void Emit (EmitContext ec)
5865
if (ec.EmitAccurateDebugInfo) {
5866
ec.Emit (OpCodes.Nop);
5873
sealed class ArrayForeach : IteratorStatement
5875
TemporaryVariableReference[] lengths;
5876
Expression [] length_exprs;
5877
StatementExpression[] counter;
5878
TemporaryVariableReference[] variables;
5880
TemporaryVariableReference copy;
5882
public ArrayForeach (Foreach @foreach, int rank)
5885
counter = new StatementExpression[rank];
5886
variables = new TemporaryVariableReference[rank];
5887
length_exprs = new Expression [rank];
5890
// Only use temporary length variables when dealing with
5891
// multi-dimensional arrays
5894
lengths = new TemporaryVariableReference [rank];
5897
public override bool Resolve (BlockContext ec)
5899
Block variables_block = for_each.variable.Block;
5900
copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5903
int rank = length_exprs.Length;
5904
Arguments list = new Arguments (rank);
5905
for (int i = 0; i < rank; i++) {
5906
var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5908
counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5909
counter[i].Resolve (ec);
5912
length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5914
lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5915
lengths[i].Resolve (ec);
5917
Arguments args = new Arguments (1);
5918
args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5919
length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5922
list.Add (new Argument (v));
5925
var access = new ElementAccess (copy, list, loc).Resolve (ec);
5930
if (for_each.type is VarExpr) {
5931
// Infer implicitly typed local variable from foreach array type
5932
var_type = access.Type;
5934
var_type = for_each.type.ResolveAsType (ec);
5936
if (var_type == null)
5939
access = Convert.ExplicitConversion (ec, access, var_type, loc);
5944
for_each.variable.Type = var_type;
5946
var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5947
if (variable_ref == null)
5950
for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.variable.Location));
5954
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5955
ec.CurrentBranching.CreateSibling ();
5957
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5958
if (!for_each.body.Resolve (ec))
5960
ec.EndFlowBranching ();
5962
// There's no direct control flow from the end of the embedded statement to the end of the loop
5963
ec.CurrentBranching.CurrentUsageVector.Goto ();
5965
ec.EndFlowBranching ();
5970
protected override void DoEmit (EmitContext ec)
5972
copy.EmitAssign (ec, for_each.expr);
5974
int rank = length_exprs.Length;
5975
Label[] test = new Label [rank];
5976
Label[] loop = new Label [rank];
5978
for (int i = 0; i < rank; i++) {
5979
test [i] = ec.DefineLabel ();
5980
loop [i] = ec.DefineLabel ();
5982
if (lengths != null)
5983
lengths [i].EmitAssign (ec, length_exprs [i]);
5986
IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5987
for (int i = 0; i < rank; i++) {
5988
variables [i].EmitAssign (ec, zero);
5990
ec.Emit (OpCodes.Br, test [i]);
5991
ec.MarkLabel (loop [i]);
5994
for_each.body.Emit (ec);
5996
ec.MarkLabel (ec.LoopBegin);
5997
ec.Mark (for_each.expr.Location);
5999
for (int i = rank - 1; i >= 0; i--){
6000
counter [i].Emit (ec);
6002
ec.MarkLabel (test [i]);
6003
variables [i].Emit (ec);
6005
if (lengths != null)
6006
lengths [i].Emit (ec);
6008
length_exprs [i].Emit (ec);
6010
ec.Emit (OpCodes.Blt, loop [i]);
6013
ec.MarkLabel (ec.LoopEnd);
6017
sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
6019
class RuntimeDispose : Using.VariableDeclaration
6021
public RuntimeDispose (LocalVariable lv, Location loc)
6026
protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6028
// Defered to runtime check
6031
protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6033
var idt = bc.BuiltinTypes.IDisposable;
6036
// Fabricates code like
6038
// if ((temp = vr as IDisposable) != null) temp.Dispose ();
6041
var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6043
var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6044
dispose_variable.CreateReferenceExpression (bc, loc),
6045
new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6046
loc), new NullLiteral (loc), loc);
6048
var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6050
var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6051
dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6053
Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6054
return new If (idisaposable_test, dispose, loc);
6058
LocalVariable variable;
6060
Statement statement;
6061
ExpressionStatement init;
6062
TemporaryVariableReference enumerator_variable;
6063
bool ambiguous_getenumerator_name;
6065
public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6068
this.variable = var;
6072
void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6074
rc.Report.SymbolRelatedToPreviousError (enumerator);
6075
rc.Report.Error (202, loc,
6076
"foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6077
enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6080
MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6083
// Option 1: Try to match by name GetEnumerator first
6085
var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6086
"GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6088
var mg = mexpr as MethodGroupExpr;
6090
mg.InstanceExpression = expr;
6091
Arguments args = new Arguments (0);
6092
mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
6094
// For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6095
if (ambiguous_getenumerator_name)
6098
if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6104
// Option 2: Try to match using IEnumerable interfaces with preference of generic version
6107
PredefinedMember<MethodSpec> iface_candidate = null;
6108
var ptypes = rc.Module.PredefinedTypes;
6109
var gen_ienumerable = ptypes.IEnumerableGeneric;
6110
if (!gen_ienumerable.Define ())
6111
gen_ienumerable = null;
6114
var ifaces = t.Interfaces;
6115
if (ifaces != null) {
6116
foreach (var iface in ifaces) {
6117
if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6118
if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6119
rc.Report.SymbolRelatedToPreviousError (expr.Type);
6120
rc.Report.Error (1640, loc,
6121
"foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6122
expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6127
// TODO: Cache this somehow
6128
iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6129
MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6134
if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6135
iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6140
if (t.IsGenericParameter)
6145
} while (t != null);
6147
if (iface_candidate == null) {
6148
if (expr.Type != InternalType.ErrorType) {
6149
rc.Report.Error (1579, loc,
6150
"foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6151
expr.Type.GetSignatureForError (), "GetEnumerator");
6157
var method = iface_candidate.Resolve (loc);
6161
mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6162
mg.InstanceExpression = expr;
6166
MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6168
var ms = MemberCache.FindMember (enumerator.ReturnType,
6169
MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6170
BindingRestriction.InstanceOnly) as MethodSpec;
6172
if (ms == null || !ms.IsPublic) {
6173
Error_WrongEnumerator (rc, enumerator);
6177
return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6180
PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6182
var ps = MemberCache.FindMember (enumerator.ReturnType,
6183
MemberFilter.Property ("Current", null),
6184
BindingRestriction.InstanceOnly) as PropertySpec;
6186
if (ps == null || !ps.IsPublic) {
6187
Error_WrongEnumerator (rc, enumerator);
6194
public override bool Resolve (BlockContext ec)
6196
bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6199
expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6200
} else if (expr.Type.IsNullableType) {
6201
expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6204
var get_enumerator_mg = ResolveGetEnumerator (ec);
6205
if (get_enumerator_mg == null) {
6209
var get_enumerator = get_enumerator_mg.BestCandidate;
6210
enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6211
enumerator_variable.Resolve (ec);
6213
// Prepare bool MoveNext ()
6214
var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6215
if (move_next_mg == null) {
6219
move_next_mg.InstanceExpression = enumerator_variable;
6221
// Prepare ~T~ Current { get; }
6222
var current_prop = ResolveCurrent (ec, get_enumerator);
6223
if (current_prop == null) {
6227
var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6228
if (current_pe == null)
6231
VarExpr ve = for_each.type as VarExpr;
6235
// Source type is dynamic, set element type to dynamic too
6236
variable.Type = ec.BuiltinTypes.Dynamic;
6238
// Infer implicitly typed local variable from foreach enumerable type
6239
variable.Type = current_pe.Type;
6243
// Explicit cast of dynamic collection elements has to be done at runtime
6244
current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6247
variable.Type = for_each.type.ResolveAsType (ec);
6249
if (variable.Type == null)
6252
current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6253
if (current_pe == null)
6257
var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6258
if (variable_ref == null)
6261
for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), variable.Location));
6263
var init = new Invocation (get_enumerator_mg, null);
6265
statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6266
for_each.body, Location.Null);
6268
var enum_type = enumerator_variable.Type;
6271
// Add Dispose method call when enumerator can be IDisposable
6273
if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6274
if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6276
// Runtime Dispose check
6278
var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6279
vd.Initializer = init;
6280
statement = new Using (vd, statement, Location.Null);
6283
// No Dispose call needed
6285
this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6286
this.init.Resolve (ec);
6290
// Static Dispose check
6292
var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6293
vd.Initializer = init;
6294
statement = new Using (vd, statement, Location.Null);
6297
return statement.Resolve (ec);
6300
protected override void DoEmit (EmitContext ec)
6302
enumerator_variable.LocalInfo.CreateBuilder (ec);
6305
init.EmitStatement (ec);
6307
statement.Emit (ec);
6310
#region IErrorHandler Members
6312
bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6314
ec.Report.SymbolRelatedToPreviousError (best);
6315
ec.Report.Warning (278, 2, expr.Location,
6316
"`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6317
expr.Type.GetSignatureForError (), "enumerable",
6318
best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6320
ambiguous_getenumerator_name = true;
6324
bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6329
bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6334
bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6343
LocalVariable variable;
6345
Statement statement;
6348
public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6351
this.variable = var;
6353
this.statement = stmt;
6358
public Expression Expr {
6359
get { return expr; }
6362
public Statement Statement {
6363
get { return statement; }
6366
public Expression TypeExpression {
6367
get { return type; }
6370
public LocalVariable Variable {
6371
get { return variable; }
6374
public override bool Resolve (BlockContext ec)
6376
expr = expr.Resolve (ec);
6381
ec.Report.Error (186, loc, "Use of null is not valid in this context");
6385
body.AddStatement (statement);
6387
if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6388
statement = new ArrayForeach (this, 1);
6389
} else if (expr.Type is ArrayContainer) {
6390
statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6392
if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6393
ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6394
expr.ExprClassName);
6398
statement = new CollectionForeach (this, variable, expr);
6401
return statement.Resolve (ec);
6404
protected override void DoEmit (EmitContext ec)
6406
variable.CreateBuilder (ec);
6408
Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6409
ec.LoopBegin = ec.DefineLabel ();
6410
ec.LoopEnd = ec.DefineLabel ();
6412
statement.Emit (ec);
6414
ec.LoopBegin = old_begin;
6415
ec.LoopEnd = old_end;
6418
protected override void CloneTo (CloneContext clonectx, Statement t)
6420
Foreach target = (Foreach) t;
6422
target.type = type.Clone (clonectx);
6423
target.expr = expr.Clone (clonectx);
6424
target.body = (Block) body.Clone (clonectx);
6425
target.statement = statement.Clone (clonectx);
6428
public override object Accept (StructuralVisitor visitor)
6430
return visitor.Visit (this);