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.
52
bool unreachable = false;
53
if (warn && !ec.UnreachableReported) {
54
ec.UnreachableReported = true;
56
ec.Report.Warning (162, 2, loc, "Unreachable code detected");
59
ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
60
bool ok = Resolve (ec);
61
ec.KillFlowBranching ();
64
ec.UnreachableReported = false;
71
/// Return value indicates whether all code paths emitted return.
73
protected abstract void DoEmit (EmitContext ec);
75
public virtual void Emit (EmitContext ec)
80
if (ec.StatementEpilogue != null) {
86
// This routine must be overrided in derived classes and make copies
87
// of all the data that might be modified if resolved
89
protected abstract void CloneTo (CloneContext clonectx, Statement target);
91
public Statement Clone (CloneContext clonectx)
93
Statement s = (Statement) this.MemberwiseClone ();
94
CloneTo (clonectx, s);
98
public virtual Expression CreateExpressionTree (ResolveContext ec)
100
ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
104
public virtual object Accept (StructuralVisitor visitor)
106
return visitor.Visit (this);
110
public sealed class EmptyStatement : Statement
112
public EmptyStatement (Location loc)
117
public override bool Resolve (BlockContext ec)
122
public override bool ResolveUnreachable (BlockContext ec, bool warn)
127
public override void Emit (EmitContext ec)
131
protected override void DoEmit (EmitContext ec)
133
throw new NotSupportedException ();
136
protected override void CloneTo (CloneContext clonectx, Statement target)
141
public override object Accept (StructuralVisitor visitor)
143
return visitor.Visit (this);
147
public class If : Statement {
149
public Statement TrueStatement;
150
public Statement FalseStatement;
154
public If (Expression bool_expr, Statement true_statement, Location l)
155
: this (bool_expr, true_statement, null, l)
159
public If (Expression bool_expr,
160
Statement true_statement,
161
Statement false_statement,
164
this.expr = bool_expr;
165
TrueStatement = true_statement;
166
FalseStatement = false_statement;
170
public Expression Expr {
176
public override bool Resolve (BlockContext ec)
180
expr = expr.Resolve (ec);
185
// Dead code elimination
187
if (expr is Constant) {
188
bool take = !((Constant) expr).IsDefaultValue;
191
if (!TrueStatement.Resolve (ec))
194
if ((FalseStatement != null) &&
195
!FalseStatement.ResolveUnreachable (ec, true))
197
FalseStatement = null;
199
if (!TrueStatement.ResolveUnreachable (ec, true))
201
TrueStatement = null;
203
if ((FalseStatement != null) &&
204
!FalseStatement.Resolve (ec))
212
ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
214
ok &= TrueStatement.Resolve (ec);
216
is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
218
ec.CurrentBranching.CreateSibling ();
220
if (FalseStatement != null)
221
ok &= FalseStatement.Resolve (ec);
223
ec.EndFlowBranching ();
228
protected override void DoEmit (EmitContext ec)
230
Label false_target = ec.DefineLabel ();
234
// If we're a boolean constant, Resolve() already
235
// eliminated dead code for us.
237
Constant c = expr as Constant;
239
c.EmitSideEffect (ec);
241
if (!c.IsDefaultValue)
242
TrueStatement.Emit (ec);
243
else if (FalseStatement != null)
244
FalseStatement.Emit (ec);
249
expr.EmitBranchable (ec, false_target, false);
251
TrueStatement.Emit (ec);
253
if (FalseStatement != null){
254
bool branch_emitted = false;
256
end = ec.DefineLabel ();
258
ec.Emit (OpCodes.Br, end);
259
branch_emitted = true;
262
ec.MarkLabel (false_target);
263
FalseStatement.Emit (ec);
268
ec.MarkLabel (false_target);
272
protected override void CloneTo (CloneContext clonectx, Statement t)
276
target.expr = expr.Clone (clonectx);
277
target.TrueStatement = TrueStatement.Clone (clonectx);
278
if (FalseStatement != null)
279
target.FalseStatement = FalseStatement.Clone (clonectx);
282
public override object Accept (StructuralVisitor visitor)
284
return visitor.Visit (this);
288
public class Do : Statement {
289
public Expression expr;
290
public Statement EmbeddedStatement;
292
public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
295
EmbeddedStatement = statement;
297
WhileLocation = whileLocation;
300
public Location WhileLocation {
304
public override bool Resolve (BlockContext ec)
308
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
310
bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
312
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
313
if (!EmbeddedStatement.Resolve (ec))
315
ec.EndFlowBranching ();
317
if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
318
ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
320
expr = expr.Resolve (ec);
323
else if (expr is Constant){
324
bool infinite = !((Constant) expr).IsDefaultValue;
326
ec.CurrentBranching.CurrentUsageVector.Goto ();
329
ec.EndFlowBranching ();
334
protected override void DoEmit (EmitContext ec)
336
Label loop = ec.DefineLabel ();
337
Label old_begin = ec.LoopBegin;
338
Label old_end = ec.LoopEnd;
340
ec.LoopBegin = ec.DefineLabel ();
341
ec.LoopEnd = ec.DefineLabel ();
344
EmbeddedStatement.Emit (ec);
345
ec.MarkLabel (ec.LoopBegin);
347
// Mark start of while condition
348
ec.Mark (WhileLocation);
351
// Dead code elimination
353
if (expr is Constant) {
354
bool res = !((Constant) expr).IsDefaultValue;
356
expr.EmitSideEffect (ec);
358
ec.Emit (OpCodes.Br, loop);
360
expr.EmitBranchable (ec, loop, true);
363
ec.MarkLabel (ec.LoopEnd);
365
ec.LoopBegin = old_begin;
366
ec.LoopEnd = old_end;
369
protected override void CloneTo (CloneContext clonectx, Statement t)
373
target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
374
target.expr = expr.Clone (clonectx);
377
public override object Accept (StructuralVisitor visitor)
379
return visitor.Visit (this);
383
public class While : Statement {
384
public Expression expr;
385
public Statement Statement;
386
bool infinite, empty;
388
public While (BooleanExpression bool_expr, Statement statement, Location l)
390
this.expr = bool_expr;
391
Statement = statement;
395
public override bool Resolve (BlockContext ec)
399
expr = expr.Resolve (ec);
404
// Inform whether we are infinite or not
406
if (expr is Constant){
407
bool value = !((Constant) expr).IsDefaultValue;
410
if (!Statement.ResolveUnreachable (ec, true))
418
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
420
ec.CurrentBranching.CreateSibling ();
422
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
423
if (!Statement.Resolve (ec))
425
ec.EndFlowBranching ();
427
// There's no direct control flow from the end of the embedded statement to the end of the loop
428
ec.CurrentBranching.CurrentUsageVector.Goto ();
430
ec.EndFlowBranching ();
435
protected override void DoEmit (EmitContext ec)
438
expr.EmitSideEffect (ec);
442
Label old_begin = ec.LoopBegin;
443
Label old_end = ec.LoopEnd;
445
ec.LoopBegin = ec.DefineLabel ();
446
ec.LoopEnd = ec.DefineLabel ();
449
// Inform whether we are infinite or not
451
if (expr is Constant) {
452
// expr is 'true', since the 'empty' case above handles the 'false' case
453
ec.MarkLabel (ec.LoopBegin);
455
if (ec.EmitAccurateDebugInfo)
456
ec.Emit (OpCodes.Nop);
458
expr.EmitSideEffect (ec);
460
ec.Emit (OpCodes.Br, ec.LoopBegin);
463
// Inform that we are infinite (ie, `we return'), only
464
// if we do not `break' inside the code.
466
ec.MarkLabel (ec.LoopEnd);
468
Label while_loop = ec.DefineLabel ();
470
ec.Emit (OpCodes.Br, ec.LoopBegin);
471
ec.MarkLabel (while_loop);
475
ec.MarkLabel (ec.LoopBegin);
478
expr.EmitBranchable (ec, while_loop, true);
480
ec.MarkLabel (ec.LoopEnd);
483
ec.LoopBegin = old_begin;
484
ec.LoopEnd = old_end;
487
protected override void CloneTo (CloneContext clonectx, Statement t)
489
While target = (While) t;
491
target.expr = expr.Clone (clonectx);
492
target.Statement = Statement.Clone (clonectx);
495
public override object Accept (StructuralVisitor visitor)
497
return visitor.Visit (this);
501
public class For : Statement
503
bool infinite, empty;
505
public For (Location l)
510
public Statement Initializer {
514
public Expression Condition {
518
public Statement Iterator {
522
public Statement Statement {
526
public override bool Resolve (BlockContext ec)
530
if (Initializer != null) {
531
if (!Initializer.Resolve (ec))
535
if (Condition != null) {
536
Condition = Condition.Resolve (ec);
537
if (Condition == null)
539
else if (Condition is Constant) {
540
bool value = !((Constant) Condition).IsDefaultValue;
543
if (!Statement.ResolveUnreachable (ec, true))
545
if ((Iterator != null) &&
546
!Iterator.ResolveUnreachable (ec, false))
556
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
558
ec.CurrentBranching.CreateSibling ();
560
bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
562
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
563
if (!Statement.Resolve (ec))
565
ec.EndFlowBranching ();
567
if (Iterator != null){
568
if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
569
if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
572
if (!Iterator.Resolve (ec))
577
// There's no direct control flow from the end of the embedded statement to the end of the loop
578
ec.CurrentBranching.CurrentUsageVector.Goto ();
580
ec.EndFlowBranching ();
585
protected override void DoEmit (EmitContext ec)
587
if (Initializer != null)
588
Initializer.Emit (ec);
591
Condition.EmitSideEffect (ec);
595
Label old_begin = ec.LoopBegin;
596
Label old_end = ec.LoopEnd;
597
Label loop = ec.DefineLabel ();
598
Label test = ec.DefineLabel ();
600
ec.LoopBegin = ec.DefineLabel ();
601
ec.LoopEnd = ec.DefineLabel ();
603
ec.Emit (OpCodes.Br, test);
607
ec.MarkLabel (ec.LoopBegin);
612
// If test is null, there is no test, and we are just
615
if (Condition != null) {
616
ec.Mark (Condition.Location);
619
// The Resolve code already catches the case for
620
// Test == Constant (false) so we know that
623
if (Condition is Constant) {
624
Condition.EmitSideEffect (ec);
625
ec.Emit (OpCodes.Br, loop);
627
Condition.EmitBranchable (ec, loop, true);
631
ec.Emit (OpCodes.Br, loop);
632
ec.MarkLabel (ec.LoopEnd);
634
ec.LoopBegin = old_begin;
635
ec.LoopEnd = old_end;
638
protected override void CloneTo (CloneContext clonectx, Statement t)
640
For target = (For) t;
642
if (Initializer != null)
643
target.Initializer = Initializer.Clone (clonectx);
644
if (Condition != null)
645
target.Condition = Condition.Clone (clonectx);
646
if (Iterator != null)
647
target.Iterator = Iterator.Clone (clonectx);
648
target.Statement = Statement.Clone (clonectx);
651
public override object Accept (StructuralVisitor visitor)
653
return visitor.Visit (this);
657
public class StatementExpression : Statement
659
ExpressionStatement expr;
661
public StatementExpression (ExpressionStatement expr)
664
loc = expr.StartLocation;
667
public StatementExpression (ExpressionStatement expr, Location loc)
673
public ExpressionStatement Expr {
679
protected override void CloneTo (CloneContext clonectx, Statement t)
681
StatementExpression target = (StatementExpression) t;
682
target.expr = (ExpressionStatement) expr.Clone (clonectx);
685
protected override void DoEmit (EmitContext ec)
687
expr.EmitStatement (ec);
690
public override bool Resolve (BlockContext ec)
692
expr = expr.ResolveStatement (ec);
696
public override object Accept (StructuralVisitor visitor)
698
return visitor.Visit (this);
702
public class StatementErrorExpression : Statement
704
readonly Expression expr;
706
public StatementErrorExpression (Expression expr)
711
public Expression Expr {
717
protected override void DoEmit (EmitContext ec)
719
throw new NotSupportedException ();
722
protected override void CloneTo (CloneContext clonectx, Statement target)
724
throw new NotImplementedException ();
727
public override object Accept (StructuralVisitor visitor)
729
return visitor.Visit (this);
734
// Simple version of statement list not requiring a block
736
public class StatementList : Statement
738
List<Statement> statements;
740
public StatementList (Statement first, Statement second)
742
statements = new List<Statement> () { first, second };
746
public IList<Statement> Statements {
753
public void Add (Statement statement)
755
statements.Add (statement);
758
public override bool Resolve (BlockContext ec)
760
foreach (var s in statements)
766
protected override void DoEmit (EmitContext ec)
768
foreach (var s in statements)
772
protected override void CloneTo (CloneContext clonectx, Statement target)
774
StatementList t = (StatementList) target;
776
t.statements = new List<Statement> (statements.Count);
777
foreach (Statement s in statements)
778
t.statements.Add (s.Clone (clonectx));
781
public override object Accept (StructuralVisitor visitor)
783
return visitor.Visit (this);
787
// A 'return' or a 'yield break'
788
public abstract class ExitStatement : Statement
790
protected bool unwind_protect;
791
protected abstract bool DoResolve (BlockContext ec);
793
public virtual void Error_FinallyClause (Report Report)
795
Report.Error (157, loc, "Control cannot leave the body of a finally clause");
798
public sealed override bool Resolve (BlockContext ec)
800
var res = DoResolve (ec);
801
unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
802
ec.CurrentBranching.CurrentUsageVector.Goto ();
808
/// Implements the return statement
810
public class Return : ExitStatement
814
public Return (Expression expr, Location l)
822
public Expression Expr {
833
protected override bool DoResolve (BlockContext ec)
836
if (ec.ReturnType.Kind == MemberKind.Void)
840
// Return must not be followed by an expression when
841
// the method return type is Task
843
if (ec.CurrentAnonymousMethod is AsyncInitializer) {
844
var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
845
if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
847
// Extra trick not to emit ret/leave inside awaiter body
849
expr = EmptyExpression.Null;
854
if (ec.CurrentIterator != null) {
855
Error_ReturnFromIterator (ec);
856
} else if (ec.ReturnType != InternalType.ErrorType) {
857
ec.Report.Error (126, loc,
858
"An object of a type convertible to `{0}' is required for the return statement",
859
ec.ReturnType.GetSignatureForError ());
865
expr = expr.Resolve (ec);
866
TypeSpec block_return_type = ec.ReturnType;
868
AnonymousExpression am = ec.CurrentAnonymousMethod;
870
if (block_return_type.Kind == MemberKind.Void) {
871
ec.Report.Error (127, loc,
872
"`{0}': A return keyword must not be followed by any expression when method returns void",
873
ec.GetSignatureForError ());
879
Error_ReturnFromIterator (ec);
883
var async_block = am as AsyncInitializer;
884
if (async_block != null) {
886
var storey = (AsyncTaskStorey) am.Storey;
887
var async_type = storey.ReturnType;
889
if (async_type == null && async_block.ReturnTypeInference != null) {
890
async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
894
if (async_type.Kind == MemberKind.Void) {
895
ec.Report.Error (127, loc,
896
"`{0}': A return keyword must not be followed by any expression when method returns void",
897
ec.GetSignatureForError ());
902
if (!async_type.IsGenericTask) {
903
if (this is ContextualReturn)
906
ec.Report.Error (1997, loc,
907
"`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
908
ec.GetSignatureForError ());
913
// The return type is actually Task<T> type argument
915
if (expr.Type == async_type) {
916
ec.Report.Error (4016, loc,
917
"`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
918
ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
920
block_return_type = async_type.TypeArguments[0];
924
// Same error code as .NET but better error message
925
if (block_return_type.Kind == MemberKind.Void) {
926
ec.Report.Error (127, loc,
927
"`{0}': A return keyword must not be followed by any expression when delegate returns void",
928
am.GetSignatureForError ());
933
var l = am as AnonymousMethodBody;
934
if (l != null && expr != null) {
935
if (l.ReturnTypeInference != null) {
936
l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
941
// Try to optimize simple lambda. Only when optimizations are enabled not to cause
942
// unexpected debugging experience
944
if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
945
l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
954
if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
955
expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
958
if (am != null && block_return_type == ec.ReturnType) {
959
ec.Report.Error (1662, loc,
960
"Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
961
am.ContainerType, am.GetSignatureForError ());
970
protected override void DoEmit (EmitContext ec)
975
var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
976
if (async_body != null) {
977
var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
979
// It's null for await without async
980
if (async_return != null) {
981
async_return.EmitAssign (ec);
986
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
992
if (unwind_protect || ec.EmitAccurateDebugInfo)
993
ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
996
if (unwind_protect) {
997
ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
998
} else if (ec.EmitAccurateDebugInfo) {
999
ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1001
ec.Emit (OpCodes.Ret);
1005
void Error_ReturnFromIterator (ResolveContext rc)
1007
rc.Report.Error (1622, loc,
1008
"Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1011
protected override void CloneTo (CloneContext clonectx, Statement t)
1013
Return target = (Return) t;
1014
// It's null for simple return;
1016
target.expr = expr.Clone (clonectx);
1019
public override object Accept (StructuralVisitor visitor)
1021
return visitor.Visit (this);
1025
public class Goto : Statement {
1027
LabeledStatement label;
1028
bool unwind_protect;
1030
public override bool Resolve (BlockContext ec)
1032
unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
1033
ec.CurrentBranching.CurrentUsageVector.Goto ();
1037
public Goto (string label, Location l)
1043
public string Target {
1044
get { return target; }
1047
public void SetResolvedTarget (LabeledStatement label)
1050
label.AddReference ();
1053
protected override void CloneTo (CloneContext clonectx, Statement target)
1058
protected override void DoEmit (EmitContext ec)
1061
throw new InternalErrorException ("goto emitted before target resolved");
1062
Label l = label.LabelTarget (ec);
1063
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1066
public override object Accept (StructuralVisitor visitor)
1068
return visitor.Visit (this);
1072
public class LabeledStatement : Statement {
1079
FlowBranching.UsageVector vectors;
1081
public LabeledStatement (string name, Block block, Location l)
1088
public Label LabelTarget (EmitContext ec)
1093
label = ec.DefineLabel ();
1098
public Block Block {
1104
public string Name {
1105
get { return name; }
1108
public bool IsDefined {
1109
get { return defined; }
1112
public bool HasBeenReferenced {
1113
get { return referenced; }
1116
public FlowBranching.UsageVector JumpOrigins {
1117
get { return vectors; }
1120
public void AddUsageVector (FlowBranching.UsageVector vector)
1122
vector = vector.Clone ();
1123
vector.Next = vectors;
1127
protected override void CloneTo (CloneContext clonectx, Statement target)
1132
public override bool Resolve (BlockContext ec)
1134
// this flow-branching will be terminated when the surrounding block ends
1135
ec.StartFlowBranching (this);
1139
protected override void DoEmit (EmitContext ec)
1141
if (!HasBeenReferenced)
1142
ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1145
ec.MarkLabel (label);
1148
public void AddReference ()
1153
public override object Accept (StructuralVisitor visitor)
1155
return visitor.Visit (this);
1161
/// `goto default' statement
1163
public class GotoDefault : Statement {
1165
public GotoDefault (Location l)
1170
protected override void CloneTo (CloneContext clonectx, Statement target)
1175
public override bool Resolve (BlockContext ec)
1177
ec.CurrentBranching.CurrentUsageVector.Goto ();
1179
if (ec.Switch == null) {
1180
ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1184
if (ec.Switch.DefaultLabel == null) {
1185
FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1192
protected override void DoEmit (EmitContext ec)
1194
ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1197
public override object Accept (StructuralVisitor visitor)
1199
return visitor.Visit (this);
1204
/// `goto case' statement
1206
public class GotoCase : Statement {
1210
public GotoCase (Expression e, Location l)
1216
public Expression Expr {
1222
public override bool Resolve (BlockContext ec)
1224
if (ec.Switch == null){
1225
ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1229
ec.CurrentBranching.CurrentUsageVector.Goto ();
1231
expr = expr.Resolve (ec);
1235
Constant c = expr as Constant;
1237
ec.Report.Error (150, expr.Location, "A constant value is expected");
1242
if (ec.Switch.IsNullable && c is NullLiteral) {
1245
TypeSpec type = ec.Switch.SwitchType;
1246
res = c.Reduce (ec, type);
1248
c.Error_ValueCannotBeConverted (ec, type, true);
1252
if (!Convert.ImplicitStandardConversionExists (c, type))
1253
ec.Report.Warning (469, 2, loc,
1254
"The `goto case' value is not implicitly convertible to type `{0}'",
1255
TypeManager.CSharpName (type));
1259
sl = ec.Switch.ResolveGotoCase (ec, res);
1263
protected override void DoEmit (EmitContext ec)
1265
ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1268
protected override void CloneTo (CloneContext clonectx, Statement t)
1270
GotoCase target = (GotoCase) t;
1272
target.expr = expr.Clone (clonectx);
1275
public override object Accept (StructuralVisitor visitor)
1277
return visitor.Visit (this);
1281
public class Throw : Statement {
1284
public Throw (Expression expr, Location l)
1290
public Expression Expr {
1296
public override bool Resolve (BlockContext ec)
1299
ec.CurrentBranching.CurrentUsageVector.Goto ();
1300
return ec.CurrentBranching.CheckRethrow (loc);
1303
expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1304
ec.CurrentBranching.CurrentUsageVector.Goto ();
1309
var et = ec.BuiltinTypes.Exception;
1310
if (Convert.ImplicitConversionExists (ec, expr, et))
1311
expr = Convert.ImplicitConversion (ec, expr, et, loc);
1313
ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1318
protected override void DoEmit (EmitContext ec)
1321
ec.Emit (OpCodes.Rethrow);
1325
ec.Emit (OpCodes.Throw);
1329
protected override void CloneTo (CloneContext clonectx, Statement t)
1331
Throw target = (Throw) t;
1334
target.expr = expr.Clone (clonectx);
1337
public override object Accept (StructuralVisitor visitor)
1339
return visitor.Visit (this);
1343
public class Break : Statement {
1345
public Break (Location l)
1350
bool unwind_protect;
1352
public override bool Resolve (BlockContext ec)
1354
unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1355
ec.CurrentBranching.CurrentUsageVector.Goto ();
1359
protected override void DoEmit (EmitContext ec)
1361
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1364
protected override void CloneTo (CloneContext clonectx, Statement t)
1369
public override object Accept (StructuralVisitor visitor)
1371
return visitor.Visit (this);
1375
public class Continue : Statement {
1377
public Continue (Location l)
1382
bool unwind_protect;
1384
public override bool Resolve (BlockContext ec)
1386
unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1387
ec.CurrentBranching.CurrentUsageVector.Goto ();
1391
protected override void DoEmit (EmitContext ec)
1393
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1396
protected override void CloneTo (CloneContext clonectx, Statement t)
1401
public override object Accept (StructuralVisitor visitor)
1403
return visitor.Visit (this);
1407
public interface ILocalVariable
1409
void Emit (EmitContext ec);
1410
void EmitAssign (EmitContext ec);
1411
void EmitAddressOf (EmitContext ec);
1414
public interface INamedBlockVariable
1416
Block Block { get; }
1417
Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1418
bool IsDeclared { get; }
1419
bool IsParameter { get; }
1420
Location Location { get; }
1423
public class BlockVariableDeclaration : Statement
1425
public class Declarator
1428
Expression initializer;
1430
public Declarator (LocalVariable li, Expression initializer)
1432
if (li.Type != null)
1433
throw new ArgumentException ("Expected null variable type");
1436
this.initializer = initializer;
1439
public Declarator (Declarator clone, Expression initializer)
1442
this.initializer = initializer;
1447
public LocalVariable Variable {
1453
public Expression Initializer {
1458
initializer = value;
1465
Expression initializer;
1466
protected FullNamedExpression type_expr;
1467
protected LocalVariable li;
1468
protected List<Declarator> declarators;
1471
public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1473
this.type_expr = type;
1475
this.loc = type_expr.Location;
1478
protected BlockVariableDeclaration (LocalVariable li)
1485
public List<Declarator> Declarators {
1491
public Expression Initializer {
1496
initializer = value;
1500
public FullNamedExpression TypeExpression {
1506
public LocalVariable Variable {
1514
public void AddDeclarator (Declarator decl)
1516
if (declarators == null)
1517
declarators = new List<Declarator> ();
1519
declarators.Add (decl);
1522
static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1524
if (bc.Report.Errors != 0)
1527
var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1529
Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1530
new MemberName (li.Name, li.Location), null);
1532
container.AddField (f);
1535
li.HoistedVariant = new HoistedEvaluatorVariable (f);
1539
public override bool Resolve (BlockContext bc)
1541
return Resolve (bc, true);
1544
public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1546
if (type == null && !li.IsCompilerGenerated) {
1547
var vexpr = type_expr as VarExpr;
1550
// C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1551
// same name exists or as a keyword when no type was found
1553
if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1554
if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1555
bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1558
bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1562
if (li.IsConstant) {
1563
bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1567
if (Initializer == null) {
1568
bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1572
if (declarators != null) {
1573
bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1577
Initializer = Initializer.Resolve (bc);
1578
if (Initializer != null) {
1579
((VarExpr) type_expr).InferType (bc, Initializer);
1580
type = type_expr.Type;
1582
// Set error type to indicate the var was placed correctly but could
1585
// var a = missing ();
1587
type = InternalType.ErrorType;
1592
type = type_expr.ResolveAsType (bc);
1596
if (li.IsConstant && !type.IsConstantCompatible) {
1597
Const.Error_InvalidConstantType (type, loc, bc.Report);
1602
FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1607
bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1609
CreateEvaluatorVariable (bc, li);
1611
li.PrepareForFlowAnalysis (bc);
1614
if (initializer != null) {
1615
initializer = ResolveInitializer (bc, li, initializer);
1616
// li.Variable.DefinitelyAssigned
1619
if (declarators != null) {
1620
foreach (var d in declarators) {
1621
d.Variable.Type = li.Type;
1623
CreateEvaluatorVariable (bc, d.Variable);
1625
d.Variable.PrepareForFlowAnalysis (bc);
1628
if (d.Initializer != null && resolveDeclaratorInitializers) {
1629
d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1630
// d.Variable.DefinitelyAssigned
1638
protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1640
var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1641
return a.ResolveStatement (bc);
1644
protected override void DoEmit (EmitContext ec)
1646
li.CreateBuilder (ec);
1648
if (Initializer != null)
1649
((ExpressionStatement) Initializer).EmitStatement (ec);
1651
if (declarators != null) {
1652
foreach (var d in declarators) {
1653
d.Variable.CreateBuilder (ec);
1654
if (d.Initializer != null) {
1655
ec.Mark (d.Variable.Location);
1656
((ExpressionStatement) d.Initializer).EmitStatement (ec);
1662
protected override void CloneTo (CloneContext clonectx, Statement target)
1664
BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1666
if (type_expr != null)
1667
t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1669
if (initializer != null)
1670
t.initializer = initializer.Clone (clonectx);
1672
if (declarators != null) {
1673
t.declarators = null;
1674
foreach (var d in declarators)
1675
t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1679
public override object Accept (StructuralVisitor visitor)
1681
return visitor.Visit (this);
1685
public class BlockConstantDeclaration : BlockVariableDeclaration
1687
public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1692
public override void Emit (EmitContext ec)
1694
// Nothing to emit, not even sequence point
1697
protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1699
initializer = initializer.Resolve (bc);
1700
if (initializer == null)
1703
var c = initializer as Constant;
1705
initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1709
c = c.ConvertImplicitly (li.Type);
1711
if (TypeSpec.IsReferenceType (li.Type))
1712
initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1714
initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
1719
li.ConstantValue = c;
1723
public override object Accept (StructuralVisitor visitor)
1725
return visitor.Visit (this);
1730
// The information about a user-perceived local variable
1732
public class LocalVariable : INamedBlockVariable, ILocalVariable
1739
AddressTaken = 1 << 2,
1740
CompilerGenerated = 1 << 3,
1742
ForeachVariable = 1 << 5,
1743
FixedVariable = 1 << 6,
1744
UsingVariable = 1 << 7,
1745
// DefinitelyAssigned = 1 << 8,
1748
ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1752
readonly string name;
1753
readonly Location loc;
1754
readonly Block block;
1756
Constant const_value;
1758
public VariableInfo VariableInfo;
1759
HoistedVariable hoisted_variant;
1761
LocalBuilder builder;
1763
public LocalVariable (Block block, string name, Location loc)
1770
public LocalVariable (Block block, string name, Flags flags, Location loc)
1771
: this (block, name, loc)
1777
// Used by variable declarators
1779
public LocalVariable (LocalVariable li, string name, Location loc)
1780
: this (li.block, name, li.flags, loc)
1786
public bool AddressTaken {
1788
return (flags & Flags.AddressTaken) != 0;
1792
public Block Block {
1798
public Constant ConstantValue {
1803
const_value = value;
1808
// Hoisted local variable variant
1810
public HoistedVariable HoistedVariant {
1812
return hoisted_variant;
1815
hoisted_variant = value;
1819
public bool IsDeclared {
1821
return type != null;
1825
public bool IsCompilerGenerated {
1827
return (flags & Flags.CompilerGenerated) != 0;
1831
public bool IsConstant {
1833
return (flags & Flags.Constant) != 0;
1837
public bool IsLocked {
1839
return (flags & Flags.IsLocked) != 0;
1842
flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1846
public bool IsThis {
1848
return (flags & Flags.IsThis) != 0;
1852
public bool IsFixed {
1854
return (flags & Flags.FixedVariable) != 0;
1858
bool INamedBlockVariable.IsParameter {
1864
public bool IsReadonly {
1866
return (flags & Flags.ReadonlyMask) != 0;
1870
public Location Location {
1876
public string Name {
1882
public TypeSpec Type {
1893
public void CreateBuilder (EmitContext ec)
1895
if ((flags & Flags.Used) == 0) {
1896
if (VariableInfo == null) {
1897
// Missing flow analysis or wrong variable flags
1898
throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1901
if (VariableInfo.IsEverAssigned)
1902
ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1904
ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1907
if (HoistedVariant != null)
1910
if (builder != null) {
1911
if ((flags & Flags.CompilerGenerated) != 0)
1914
// To avoid Used warning duplicates
1915
throw new InternalErrorException ("Already created variable `{0}'", name);
1919
// All fixed variabled are pinned, a slot has to be alocated
1921
builder = ec.DeclareLocal (Type, IsFixed);
1922
if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1923
ec.DefineLocalVariable (name, builder);
1926
public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1928
LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1933
public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1935
if (IsConstant && const_value != null)
1936
return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1938
return new LocalVariableReference (this, loc);
1941
public void Emit (EmitContext ec)
1943
// TODO: Need something better for temporary variables
1944
if ((flags & Flags.CompilerGenerated) != 0)
1947
ec.Emit (OpCodes.Ldloc, builder);
1950
public void EmitAssign (EmitContext ec)
1952
// TODO: Need something better for temporary variables
1953
if ((flags & Flags.CompilerGenerated) != 0)
1956
ec.Emit (OpCodes.Stloc, builder);
1959
public void EmitAddressOf (EmitContext ec)
1961
ec.Emit (OpCodes.Ldloca, builder);
1964
public static string GetCompilerGeneratedName (Block block)
1966
// HACK: Debugger depends on the name semantics
1967
return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1970
public string GetReadOnlyContext ()
1972
switch (flags & Flags.ReadonlyMask) {
1973
case Flags.FixedVariable:
1974
return "fixed variable";
1975
case Flags.ForeachVariable:
1976
return "foreach iteration variable";
1977
case Flags.UsingVariable:
1978
return "using variable";
1981
throw new InternalErrorException ("Variable is not readonly");
1984
public bool IsThisAssigned (BlockContext ec, Block block)
1986
if (VariableInfo == null)
1987
throw new Exception ();
1989
if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1992
return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
1995
public bool IsAssigned (BlockContext ec)
1997
if (VariableInfo == null)
1998
throw new Exception ();
2000
return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
2003
public void PrepareForFlowAnalysis (BlockContext bc)
2006
// No need for definitely assigned check for these guys
2008
if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2011
VariableInfo = new VariableInfo (this, bc.FlowOffset);
2012
bc.FlowOffset += VariableInfo.Length;
2016
// Mark the variables as referenced in the user code
2018
public void SetIsUsed ()
2020
flags |= Flags.Used;
2023
public void SetHasAddressTaken ()
2025
flags |= (Flags.AddressTaken | Flags.Used);
2028
public override string ToString ()
2030
return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2035
/// Block represents a C# block.
2039
/// This class is used in a number of places: either to represent
2040
/// explicit blocks that the programmer places or implicit blocks.
2042
/// Implicit blocks are used as labels or to introduce variable
2045
/// Top-level blocks derive from Block, and they are called ToplevelBlock
2046
/// they contain extra information that is not necessary on normal blocks.
2048
public class Block : Statement {
2055
HasCapturedVariable = 64,
2056
HasCapturedThis = 1 << 7,
2057
IsExpressionTree = 1 << 8,
2058
CompilerGenerated = 1 << 9,
2059
HasAsyncModifier = 1 << 10,
2061
YieldBlock = 1 << 12,
2062
AwaitBlock = 1 << 13
2065
public Block Parent;
2066
public Location StartLocation;
2067
public Location EndLocation;
2069
public ExplicitBlock Explicit;
2070
public ParametersBlock ParametersBlock;
2072
protected Flags flags;
2075
// The statements in this block
2077
protected List<Statement> statements;
2079
protected List<Statement> scope_initializers;
2081
int? resolving_init_idx;
2087
public int ID = id++;
2089
static int clone_id_counter;
2093
// int assignable_slots;
2095
public Block (Block parent, Location start, Location end)
2096
: this (parent, 0, start, end)
2100
public Block (Block parent, Flags flags, Location start, Location end)
2102
if (parent != null) {
2103
// the appropriate constructors will fixup these fields
2104
ParametersBlock = parent.ParametersBlock;
2105
Explicit = parent.Explicit;
2108
this.Parent = parent;
2110
this.StartLocation = start;
2111
this.EndLocation = end;
2113
statements = new List<Statement> (4);
2115
this.original = this;
2120
public bool HasUnreachableClosingBrace {
2122
return (flags & Flags.HasRet) != 0;
2125
flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2129
public Block Original {
2138
public bool IsCompilerGenerated {
2139
get { return (flags & Flags.CompilerGenerated) != 0; }
2140
set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2143
public bool Unchecked {
2144
get { return (flags & Flags.Unchecked) != 0; }
2145
set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2148
public bool Unsafe {
2149
get { return (flags & Flags.Unsafe) != 0; }
2150
set { flags |= Flags.Unsafe; }
2153
public List<Statement> Statements {
2154
get { return statements; }
2159
public void SetEndLocation (Location loc)
2164
public void AddLabel (LabeledStatement target)
2166
ParametersBlock.TopBlock.AddLabel (target.Name, target);
2169
public void AddLocalName (LocalVariable li)
2171
AddLocalName (li.Name, li);
2174
public void AddLocalName (string name, INamedBlockVariable li)
2176
ParametersBlock.TopBlock.AddLocalName (name, li, false);
2179
public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2181
if (reason == null) {
2182
Error_AlreadyDeclared (name, variable);
2186
ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2187
"A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2188
"to `{0}', which is already used in a `{1}' scope to denote something else",
2192
public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2194
var pi = variable as ParametersBlock.ParameterInfo;
2196
pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2198
ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2199
"A local variable named `{0}' is already defined in this scope", name);
2203
public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2205
ParametersBlock.TopBlock.Report.Error (412, loc,
2206
"The type parameter name `{0}' is the same as local variable or parameter name",
2211
// It should be used by expressions which require to
2212
// register a statement during resolve process.
2214
public void AddScopeStatement (Statement s)
2216
if (scope_initializers == null)
2217
scope_initializers = new List<Statement> ();
2220
// Simple recursive helper, when resolve scope initializer another
2221
// new scope initializer can be added, this ensures it's initialized
2222
// before existing one. For now this can happen with expression trees
2223
// in base ctor initializer only
2225
if (resolving_init_idx.HasValue) {
2226
scope_initializers.Insert (resolving_init_idx.Value, s);
2227
++resolving_init_idx;
2229
scope_initializers.Add (s);
2233
public void AddStatement (Statement s)
2238
public int AssignableSlots {
2240
// FIXME: HACK, we don't know the block available variables count now, so set this high enough
2242
// return assignable_slots;
2246
public LabeledStatement LookupLabel (string name)
2248
return ParametersBlock.TopBlock.GetLabel (name, this);
2251
public override bool Resolve (BlockContext ec)
2253
if ((flags & Flags.Resolved) != 0)
2256
Block prev_block = ec.CurrentBlock;
2258
bool unreachable = ec.IsUnreachable;
2259
bool prev_unreachable = unreachable;
2261
ec.CurrentBlock = this;
2262
ec.StartFlowBranching (this);
2265
// Compiler generated scope statements
2267
if (scope_initializers != null) {
2268
for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2269
scope_initializers[resolving_init_idx.Value].Resolve (ec);
2272
resolving_init_idx = null;
2276
// This flag is used to notate nested statements as unreachable from the beginning of this block.
2277
// For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2278
// from the beginning of the function. The outer Resolve() that detected the unreachability is
2279
// responsible for handling the situation.
2281
int statement_count = statements.Count;
2282
for (int ix = 0; ix < statement_count; ix++){
2283
Statement s = statements [ix];
2286
// Warn if we detect unreachable code.
2289
if (s is EmptyStatement)
2292
if (!ec.UnreachableReported && !(s is LabeledStatement) && !(s is SwitchLabel)) {
2293
ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2294
ec.UnreachableReported = true;
2299
// Note that we're not using ResolveUnreachable() for unreachable
2300
// statements here. ResolveUnreachable() creates a temporary
2301
// flow branching and kills it afterwards. This leads to problems
2302
// if you have two unreachable statements where the first one
2303
// assigns a variable and the second one tries to access it.
2306
if (!s.Resolve (ec)) {
2308
if (!ec.IsInProbingMode)
2309
statements [ix] = new EmptyStatement (s.loc);
2314
if (unreachable && !(s is LabeledStatement) && !(s is SwitchLabel) && !(s is Block))
2315
statements [ix] = new EmptyStatement (s.loc);
2317
unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2319
ec.IsUnreachable = true;
2320
} else if (ec.IsUnreachable)
2321
ec.IsUnreachable = false;
2324
if (unreachable != prev_unreachable) {
2325
ec.IsUnreachable = prev_unreachable;
2326
ec.UnreachableReported = false;
2329
while (ec.CurrentBranching is FlowBranchingLabeled)
2330
ec.EndFlowBranching ();
2332
bool flow_unreachable = ec.EndFlowBranching ();
2334
ec.CurrentBlock = prev_block;
2336
if (flow_unreachable)
2337
flags |= Flags.HasRet;
2339
// If we're a non-static `struct' constructor which doesn't have an
2340
// initializer, then we must initialize all of the struct's fields.
2341
if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2344
flags |= Flags.Resolved;
2348
public override bool ResolveUnreachable (BlockContext ec, bool warn)
2350
bool unreachable = false;
2351
if (warn && !ec.UnreachableReported) {
2352
ec.UnreachableReported = true;
2354
ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2357
var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2358
fb.CurrentUsageVector.IsUnreachable = true;
2359
bool ok = Resolve (ec);
2360
ec.KillFlowBranching ();
2363
ec.UnreachableReported = false;
2368
protected override void DoEmit (EmitContext ec)
2370
for (int ix = 0; ix < statements.Count; ix++){
2371
statements [ix].Emit (ec);
2375
public override void Emit (EmitContext ec)
2377
if (scope_initializers != null)
2378
EmitScopeInitializers (ec);
2383
protected void EmitScopeInitializers (EmitContext ec)
2385
foreach (Statement s in scope_initializers)
2390
public override string ToString ()
2392
return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2396
protected override void CloneTo (CloneContext clonectx, Statement t)
2398
Block target = (Block) t;
2400
target.clone_id = clone_id_counter++;
2403
clonectx.AddBlockMap (this, target);
2404
if (original != this)
2405
clonectx.AddBlockMap (original, target);
2407
target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2408
target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2411
target.Parent = clonectx.RemapBlockCopy (Parent);
2413
target.statements = new List<Statement> (statements.Count);
2414
foreach (Statement s in statements)
2415
target.statements.Add (s.Clone (clonectx));
2418
public override object Accept (StructuralVisitor visitor)
2420
return visitor.Visit (this);
2424
public class ExplicitBlock : Block
2426
protected AnonymousMethodStorey am_storey;
2428
public ExplicitBlock (Block parent, Location start, Location end)
2429
: this (parent, (Flags) 0, start, end)
2433
public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2434
: base (parent, flags, start, end)
2436
this.Explicit = this;
2441
public AnonymousMethodStorey AnonymousMethodStorey {
2447
public bool HasAwait {
2449
return (flags & Flags.AwaitBlock) != 0;
2453
public bool HasCapturedThis {
2455
flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
2458
return (flags & Flags.HasCapturedThis) != 0;
2463
// Used to indicate that the block has reference to parent
2464
// block and cannot be made static when defining anonymous method
2466
public bool HasCapturedVariable {
2468
flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2471
return (flags & Flags.HasCapturedVariable) != 0;
2475
public bool HasYield {
2477
return (flags & Flags.YieldBlock) != 0;
2484
// Creates anonymous method storey in current block
2486
public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2489
// Return same story for iterator and async blocks unless we are
2490
// in nested anonymous method
2492
if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2493
return ec.CurrentAnonymousMethod.Storey;
2495
if (am_storey == null) {
2496
MemberBase mc = ec.MemberContext as MemberBase;
2499
// Creates anonymous method storey for this block
2501
am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2507
public override void Emit (EmitContext ec)
2509
if (am_storey != null) {
2510
DefineStoreyContainer (ec, am_storey);
2511
am_storey.EmitStoreyInstantiation (ec, this);
2514
if (scope_initializers != null)
2515
EmitScopeInitializers (ec);
2517
if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2518
ec.Emit (OpCodes.Nop);
2529
if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2530
ec.Emit (OpCodes.Nop);
2534
protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2536
if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2537
storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2538
storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2542
// Creates anonymous method storey
2544
storey.CreateContainer ();
2545
storey.DefineContainer ();
2547
if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
2550
// Only first storey in path will hold this reference. All children blocks will
2551
// reference it indirectly using $ref field
2553
for (Block b = Original.Explicit; b != null; b = b.Parent) {
2554
if (b.Parent != null) {
2555
var s = b.Parent.Explicit.AnonymousMethodStorey;
2557
storey.HoistedThis = s.HoistedThis;
2562
if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
2563
storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
2565
if (storey.HoistedThis != null)
2571
// We are the first storey on path and 'this' has to be hoisted
2573
if (storey.HoistedThis == null) {
2574
foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
2576
// ThisReferencesFromChildrenBlock holds all reference even if they
2577
// are not on this path. It saves some memory otherwise it'd have to
2578
// be in every explicit block. We run this check to see if the reference
2579
// is valid for this storey
2581
Block block_on_path = ref_block;
2582
for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
2584
if (block_on_path == null)
2587
if (storey.HoistedThis == null) {
2588
storey.AddCapturedThisField (ec);
2591
for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2592
if (b.AnonymousMethodStorey != null) {
2593
b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2594
b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
2597
// Stop propagation inside same top block
2599
if (b.ParametersBlock == ParametersBlock.Original)
2602
b = b.ParametersBlock;
2605
var pb = b as ParametersBlock;
2606
if (pb != null && pb.StateMachine != null) {
2607
if (pb.StateMachine == storey)
2610
pb.StateMachine.AddParentStoreyReference (ec, storey);
2613
b.HasCapturedVariable = true;
2619
var ref_blocks = storey.ReferencesFromChildrenBlock;
2620
if (ref_blocks != null) {
2621
foreach (ExplicitBlock ref_block in ref_blocks) {
2622
for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2623
if (b.AnonymousMethodStorey != null) {
2624
b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2627
// Stop propagation inside same top block
2629
if (b.ParametersBlock == ParametersBlock.Original)
2632
b = b.ParametersBlock;
2635
var pb = b as ParametersBlock;
2636
if (pb != null && pb.StateMachine != null) {
2637
if (pb.StateMachine == storey)
2640
pb.StateMachine.AddParentStoreyReference (ec, storey);
2643
b.HasCapturedVariable = true;
2649
storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2652
public void RegisterAsyncAwait ()
2655
while ((block.flags & Flags.AwaitBlock) == 0) {
2656
block.flags |= Flags.AwaitBlock;
2658
if (block is ParametersBlock)
2661
block = block.Parent.Explicit;
2665
public void RegisterIteratorYield ()
2668
while ((block.flags & Flags.YieldBlock) == 0) {
2669
block.flags |= Flags.YieldBlock;
2671
if (block.Parent == null)
2674
block = block.Parent.Explicit;
2678
public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2680
tryBlock.statements = statements;
2681
statements = new List<Statement> (1);
2682
statements.Add (tf);
2687
// ParametersBlock was introduced to support anonymous methods
2688
// and lambda expressions
2690
public class ParametersBlock : ExplicitBlock
2692
public class ParameterInfo : INamedBlockVariable
2694
readonly ParametersBlock block;
2696
public VariableInfo VariableInfo;
2699
public ParameterInfo (ParametersBlock block, int index)
2707
public ParametersBlock Block {
2713
Block INamedBlockVariable.Block {
2719
public bool IsDeclared {
2725
public bool IsParameter {
2731
public bool IsLocked {
2740
public Location Location {
2742
return Parameter.Location;
2746
public Parameter Parameter {
2748
return block.Parameters [index];
2752
public TypeSpec ParameterType {
2754
return Parameter.Type;
2760
public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2762
return new ParameterReference (this, loc);
2767
// Block is converted into an expression
2769
sealed class BlockScopeExpression : Expression
2772
readonly ParametersBlock block;
2774
public BlockScopeExpression (Expression child, ParametersBlock block)
2780
public override bool ContainsEmitWithAwait ()
2782
return child.ContainsEmitWithAwait ();
2785
public override Expression CreateExpressionTree (ResolveContext ec)
2787
throw new NotSupportedException ();
2790
protected override Expression DoResolve (ResolveContext ec)
2795
child = child.Resolve (ec);
2799
eclass = child.eclass;
2804
public override void Emit (EmitContext ec)
2806
block.EmitScopeInitializers (ec);
2811
protected ParametersCompiled parameters;
2812
protected ParameterInfo[] parameter_info;
2814
protected bool unreachable;
2815
protected ToplevelBlock top_block;
2816
protected StateMachine state_machine;
2818
public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2819
: base (parent, 0, start, start)
2821
if (parameters == null)
2822
throw new ArgumentNullException ("parameters");
2824
this.parameters = parameters;
2825
ParametersBlock = this;
2827
flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2829
this.top_block = parent.ParametersBlock.top_block;
2830
ProcessParameters ();
2833
protected ParametersBlock (ParametersCompiled parameters, Location start)
2834
: base (null, 0, start, start)
2836
if (parameters == null)
2837
throw new ArgumentNullException ("parameters");
2839
this.parameters = parameters;
2840
ParametersBlock = this;
2844
// It's supposed to be used by method body implementation of anonymous methods
2846
protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2847
: base (null, 0, source.StartLocation, source.EndLocation)
2849
this.parameters = parameters;
2850
this.statements = source.statements;
2851
this.scope_initializers = source.scope_initializers;
2853
this.resolved = true;
2854
this.unreachable = source.unreachable;
2855
this.am_storey = source.am_storey;
2856
this.state_machine = source.state_machine;
2858
ParametersBlock = this;
2861
// Overwrite original for comparison purposes when linking cross references
2862
// between anonymous methods
2864
Original = source.Original;
2869
public bool IsAsync {
2871
return (flags & Flags.HasAsyncModifier) != 0;
2874
flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2879
// Block has been converted to expression tree
2881
public bool IsExpressionTree {
2883
return (flags & Flags.IsExpressionTree) != 0;
2888
// The parameters for the block.
2890
public ParametersCompiled Parameters {
2896
public StateMachine StateMachine {
2898
return state_machine;
2902
public ToplevelBlock TopBlock {
2908
public bool Resolved {
2910
return (flags & Flags.Resolved) != 0;
2914
public int TemporaryLocalsCount { get; set; }
2919
// Check whether all `out' parameters have been assigned.
2921
public void CheckOutParameters (FlowBranching.UsageVector vector)
2923
if (vector.IsUnreachable)
2926
int n = parameter_info == null ? 0 : parameter_info.Length;
2928
for (int i = 0; i < n; i++) {
2929
VariableInfo var = parameter_info[i].VariableInfo;
2934
if (vector.IsAssigned (var, false))
2937
var p = parameter_info[i].Parameter;
2938
TopBlock.Report.Error (177, p.Location,
2939
"The out parameter `{0}' must be assigned to before control leaves the current method",
2944
public override Expression CreateExpressionTree (ResolveContext ec)
2946
if (statements.Count == 1) {
2947
Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2948
if (scope_initializers != null)
2949
expr = new BlockScopeExpression (expr, this);
2954
return base.CreateExpressionTree (ec);
2957
public override void Emit (EmitContext ec)
2959
if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2960
DefineStoreyContainer (ec, state_machine);
2961
state_machine.EmitStoreyInstantiation (ec, this);
2967
public void EmitEmbedded (EmitContext ec)
2969
if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2970
DefineStoreyContainer (ec, state_machine);
2971
state_machine.EmitStoreyInstantiation (ec, this);
2977
public ParameterInfo GetParameterInfo (Parameter p)
2979
for (int i = 0; i < parameters.Count; ++i) {
2980
if (parameters[i] == p)
2981
return parameter_info[i];
2984
throw new ArgumentException ("Invalid parameter");
2987
public ParameterReference GetParameterReference (int index, Location loc)
2989
return new ParameterReference (parameter_info[index], loc);
2992
public Statement PerformClone ()
2994
CloneContext clonectx = new CloneContext ();
2995
return Clone (clonectx);
2998
protected void ProcessParameters ()
3000
if (parameters.Count == 0)
3003
parameter_info = new ParameterInfo[parameters.Count];
3004
for (int i = 0; i < parameter_info.Length; ++i) {
3005
var p = parameters.FixedParameters[i];
3009
// TODO: Should use Parameter only and more block there
3010
parameter_info[i] = new ParameterInfo (this, i);
3012
AddLocalName (p.Name, parameter_info[i]);
3016
public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
3023
if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3024
flags |= Flags.IsExpressionTree;
3029
using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
3030
FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
3035
unreachable = top_level.End ();
3037
} catch (Exception e) {
3038
if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
3041
if (rc.CurrentBlock != null) {
3042
rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3044
rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3047
if (rc.Module.Compiler.Settings.DebugFlags > 0)
3051
if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3052
if (rc.CurrentAnonymousMethod == null) {
3053
// FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3054
if (md is StateMachineMethod) {
3057
rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3062
// If an asynchronous body of F is either an expression classified as nothing, or a
3063
// statement block where no return statements have expressions, the inferred return type is Task
3066
var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3067
if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3068
am.ReturnTypeInference = null;
3069
am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3074
rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3075
rc.CurrentAnonymousMethod.GetSignatureForError ());
3083
void ResolveMeta (BlockContext ec)
3085
int orig_count = parameters.Count;
3087
for (int i = 0; i < orig_count; ++i) {
3088
Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
3090
if ((mod & Parameter.Modifier.OUT) == 0)
3093
VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
3094
parameter_info[i].VariableInfo = vi;
3095
ec.FlowOffset += vi.Length;
3099
public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3101
var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3102
var stateMachine = new IteratorStorey (iterator);
3104
state_machine = stateMachine;
3105
iterator.SetStateMachine (stateMachine);
3107
var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3108
tlb.Original = this;
3109
tlb.IsCompilerGenerated = true;
3110
tlb.state_machine = stateMachine;
3111
tlb.AddStatement (new Return (iterator, iterator.Location));
3115
public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, Location loc)
3117
for (int i = 0; i < parameters.Count; i++) {
3118
Parameter p = parameters[i];
3119
Parameter.Modifier mod = p.ModFlags;
3120
if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3121
host.Compiler.Report.Error (1988, p.Location,
3122
"Async methods cannot have ref or out parameters");
3126
if (p is ArglistParameter) {
3127
host.Compiler.Report.Error (4006, p.Location,
3128
"__arglist is not allowed in parameter list of async methods");
3132
if (parameters.Types[i].IsPointer) {
3133
host.Compiler.Report.Error (4005, p.Location,
3134
"Async methods cannot have unsafe parameters");
3140
host.Compiler.Report.Warning (1998, 1, loc,
3141
"Async block lacks `await' operator and will run synchronously");
3144
var block_type = host.Module.Compiler.BuiltinTypes.Void;
3145
var initializer = new AsyncInitializer (this, host, block_type);
3146
initializer.Type = block_type;
3148
var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3150
state_machine = stateMachine;
3151
initializer.SetStateMachine (stateMachine);
3153
var b = this is ToplevelBlock ?
3154
new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3155
new ParametersBlock (Parent, parameters, Location.Null) {
3160
b.IsCompilerGenerated = true;
3161
b.state_machine = stateMachine;
3162
b.AddStatement (new StatementExpression (initializer));
3170
public class ToplevelBlock : ParametersBlock
3172
LocalVariable this_variable;
3173
CompilerContext compiler;
3174
Dictionary<string, object> names;
3175
Dictionary<string, object> labels;
3177
List<ExplicitBlock> this_references;
3179
public ToplevelBlock (CompilerContext ctx, Location loc)
3180
: this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3184
public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3185
: base (parameters, start)
3187
this.compiler = ctx;
3189
flags |= Flags.HasRet;
3191
ProcessParameters ();
3195
// Recreates a top level block from parameters block. Used for
3196
// compiler generated methods where the original block comes from
3197
// explicit child block. This works for already resolved blocks
3198
// only to ensure we resolve them in the correct flow order
3200
public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3201
: base (source, parameters)
3203
this.compiler = source.TopBlock.compiler;
3205
flags |= Flags.HasRet;
3208
public bool IsIterator {
3214
public Report Report {
3216
return compiler.Report;
3221
// Used by anonymous blocks to track references of `this' variable
3223
public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3225
return this_references;
3230
// Returns the "this" instance variable of this block.
3231
// See AddThisVariable() for more information.
3233
public LocalVariable ThisVariable {
3235
return this_variable;
3239
public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3242
names = new Dictionary<string, object> ();
3245
if (!names.TryGetValue (name, out value)) {
3246
names.Add (name, li);
3250
INamedBlockVariable existing = value as INamedBlockVariable;
3251
List<INamedBlockVariable> existing_list;
3252
if (existing != null) {
3253
existing_list = new List<INamedBlockVariable> ();
3254
existing_list.Add (existing);
3255
names[name] = existing_list;
3257
existing_list = (List<INamedBlockVariable>) value;
3261
// A collision checking between local names
3263
var variable_block = li.Block.Explicit;
3264
for (int i = 0; i < existing_list.Count; ++i) {
3265
existing = existing_list[i];
3266
Block b = existing.Block.Explicit;
3268
// Collision at same level
3269
if (variable_block == b) {
3270
li.Block.Error_AlreadyDeclared (name, li);
3274
// Collision with parent
3275
Block parent = variable_block;
3276
while ((parent = parent.Parent) != null) {
3278
li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3279
i = existing_list.Count;
3284
if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
3285
// Collision with children
3286
while ((b = b.Parent) != null) {
3287
if (variable_block == b) {
3288
li.Block.Error_AlreadyDeclared (name, li, "child");
3289
i = existing_list.Count;
3296
existing_list.Add (li);
3299
public void AddLabel (string name, LabeledStatement label)
3302
labels = new Dictionary<string, object> ();
3305
if (!labels.TryGetValue (name, out value)) {
3306
labels.Add (name, label);
3310
LabeledStatement existing = value as LabeledStatement;
3311
List<LabeledStatement> existing_list;
3312
if (existing != null) {
3313
existing_list = new List<LabeledStatement> ();
3314
existing_list.Add (existing);
3315
labels[name] = existing_list;
3317
existing_list = (List<LabeledStatement>) value;
3321
// A collision checking between labels
3323
for (int i = 0; i < existing_list.Count; ++i) {
3324
existing = existing_list[i];
3325
Block b = existing.Block;
3327
// Collision at same level
3328
if (label.Block == b) {
3329
Report.SymbolRelatedToPreviousError (existing.loc, name);
3330
Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3334
// Collision with parent
3336
while ((b = b.Parent) != null) {
3337
if (existing.Block == b) {
3338
Report.Error (158, label.loc,
3339
"The label `{0}' shadows another label by the same name in a contained scope", name);
3340
i = existing_list.Count;
3345
// Collision with with children
3347
while ((b = b.Parent) != null) {
3348
if (label.Block == b) {
3349
Report.Error (158, label.loc,
3350
"The label `{0}' shadows another label by the same name in a contained scope", name);
3351
i = existing_list.Count;
3357
existing_list.Add (label);
3360
public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3362
if (this_references == null)
3363
this_references = new List<ExplicitBlock> ();
3365
if (!this_references.Contains (block))
3366
this_references.Add (block);
3369
public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3371
this_references.Remove (block);
3375
// Creates an arguments set from all parameters, useful for method proxy calls
3377
public Arguments GetAllParametersArguments ()
3379
int count = parameters.Count;
3380
Arguments args = new Arguments (count);
3381
for (int i = 0; i < count; ++i) {
3382
var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3383
args.Add (new Argument (arg_expr));
3390
// Lookup inside a block, the returned value can represent 3 states
3392
// true+variable: A local name was found and it's valid
3393
// false+variable: A local name was found in a child block only
3394
// false+null: No local name was found
3396
public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3402
if (!names.TryGetValue (name, out value))
3405
variable = value as INamedBlockVariable;
3407
if (variable != null) {
3409
if (variable.Block == b.Original)
3413
} while (b != null);
3421
} while (b != null);
3423
List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3424
for (int i = 0; i < list.Count; ++i) {
3427
if (variable.Block == b.Original)
3431
} while (b != null);
3439
} while (b != null);
3449
public LabeledStatement GetLabel (string name, Block block)
3455
if (!labels.TryGetValue (name, out value)) {
3459
var label = value as LabeledStatement;
3461
if (label != null) {
3462
if (label.Block == b.Original)
3465
List<LabeledStatement> list = (List<LabeledStatement>) value;
3466
for (int i = 0; i < list.Count; ++i) {
3468
if (label.Block == b.Original)
3477
// This is used by non-static `struct' constructors which do not have an
3478
// initializer - in this case, the constructor must initialize all of the
3479
// struct's fields. To do this, we add a "this" variable and use the flow
3480
// analysis code to ensure that it's been fully initialized before control
3481
// leaves the constructor.
3483
public void AddThisVariable (BlockContext bc)
3485
if (this_variable != null)
3486
throw new InternalErrorException (StartLocation.ToString ());
3488
this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3489
this_variable.Type = bc.CurrentType;
3490
this_variable.PrepareForFlowAnalysis (bc);
3493
public bool IsThisAssigned (BlockContext ec)
3495
return this_variable == null || this_variable.IsThisAssigned (ec, this);
3498
public override void Emit (EmitContext ec)
3500
if (Report.Errors > 0)
3504
if (IsCompilerGenerated) {
3505
using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3513
// If `HasReturnLabel' is set, then we already emitted a
3514
// jump to the end of the method, so we must emit a `ret'
3517
// Unfortunately, System.Reflection.Emit automatically emits
3518
// a leave to the end of a finally block. This is a problem
3519
// if no code is following the try/finally block since we may
3520
// jump to a point after the end of the method.
3521
// As a workaround, we're always creating a return label in
3524
if (ec.HasReturnLabel || !unreachable) {
3525
if (ec.HasReturnLabel)
3526
ec.MarkLabel (ec.ReturnLabel);
3528
if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3529
ec.Mark (EndLocation);
3531
if (ec.ReturnType.Kind != MemberKind.Void)
3532
ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3534
ec.Emit (OpCodes.Ret);
3537
} catch (Exception e) {
3538
throw new InternalErrorException (e, StartLocation);
3543
public class SwitchLabel : Statement
3551
// if expr == null, then it is the default case.
3553
public SwitchLabel (Expression expr, Location l)
3559
public bool IsDefault {
3561
return label == null;
3565
public Expression Label {
3571
public Location Location {
3577
public Constant Converted {
3586
public bool SectionStart { get; set; }
3588
public Label GetILLabel (EmitContext ec)
3590
if (il_label == null){
3591
il_label = ec.DefineLabel ();
3594
return il_label.Value;
3597
protected override void DoEmit (EmitContext ec)
3599
ec.MarkLabel (GetILLabel (ec));
3602
public override bool Resolve (BlockContext bc)
3604
bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
3606
return base.Resolve (bc);
3610
// Resolves the expression, reduces it to a literal if possible
3611
// and then converts it to the requested type.
3613
public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3615
Expression e = label.Resolve (ec);
3620
Constant c = e as Constant;
3622
ec.Report.Error (150, loc, "A constant value is expected");
3626
if (allow_nullable && c is NullLiteral) {
3631
converted = c.ImplicitConversionRequired (ec, required_type, loc);
3632
return converted != null;
3635
public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3638
if (converted == null)
3641
label = converted.GetValueAsLiteral ();
3643
ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3644
ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3647
protected override void CloneTo (CloneContext clonectx, Statement target)
3649
var t = (SwitchLabel) target;
3651
t.label = label.Clone (clonectx);
3654
public override object Accept (StructuralVisitor visitor)
3656
return visitor.Visit (this);
3660
public class Switch : Statement
3662
// structure used to hold blocks of keys while calculating table switch
3663
sealed class LabelsRange : IComparable<LabelsRange>
3665
public readonly long min;
3667
public readonly List<long> label_values;
3669
public LabelsRange (long value)
3672
label_values = new List<long> ();
3673
label_values.Add (value);
3676
public LabelsRange (long min, long max, ICollection<long> values)
3680
this.label_values = new List<long> (values);
3685
return max - min + 1;
3689
public bool AddValue (long value)
3691
var gap = value - min + 1;
3692
// Ensure the range has > 50% occupancy
3693
if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3697
label_values.Add (value);
3701
public int CompareTo (LabelsRange other)
3703
int nLength = label_values.Count;
3704
int nLengthOther = other.label_values.Count;
3705
if (nLengthOther == nLength)
3706
return (int) (other.min - min);
3708
return nLength - nLengthOther;
3712
sealed class DispatchStatement : Statement
3714
readonly Switch body;
3716
public DispatchStatement (Switch body)
3721
protected override void CloneTo (CloneContext clonectx, Statement target)
3723
throw new NotImplementedException ();
3726
protected override void DoEmit (EmitContext ec)
3728
body.EmitDispatch (ec);
3732
public Expression Expr;
3735
// Mapping of all labels to their SwitchLabels
3737
Dictionary<long, SwitchLabel> labels;
3738
Dictionary<string, SwitchLabel> string_labels;
3739
List<SwitchLabel> case_labels;
3742
/// The governing switch type
3744
public TypeSpec SwitchType;
3746
Expression new_expr;
3748
SwitchLabel case_null;
3749
SwitchLabel case_default;
3751
Label defaultLabel, nullLabel;
3752
VariableReference value;
3753
ExpressionStatement string_dictionary;
3754
FieldExpr switch_cache_field;
3755
ExplicitBlock block;
3758
// Nullable Types support
3760
Nullable.Unwrap unwrap;
3762
public Switch (Expression e, ExplicitBlock block, Location l)
3769
public ExplicitBlock Block {
3775
public SwitchLabel DefaultLabel {
3777
return case_default;
3781
public bool IsNullable {
3783
return unwrap != null;
3788
// Determines the governing type for a switch. The returned
3789
// expression might be the expression from the switch, or an
3790
// expression that includes any potential conversions to
3792
Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3794
switch (expr.Type.BuiltinType) {
3795
case BuiltinTypeSpec.Type.Byte:
3796
case BuiltinTypeSpec.Type.SByte:
3797
case BuiltinTypeSpec.Type.UShort:
3798
case BuiltinTypeSpec.Type.Short:
3799
case BuiltinTypeSpec.Type.UInt:
3800
case BuiltinTypeSpec.Type.Int:
3801
case BuiltinTypeSpec.Type.ULong:
3802
case BuiltinTypeSpec.Type.Long:
3803
case BuiltinTypeSpec.Type.Char:
3804
case BuiltinTypeSpec.Type.String:
3805
case BuiltinTypeSpec.Type.Bool:
3809
if (expr.Type.IsEnum)
3813
// Try to find a *user* defined implicit conversion.
3815
// If there is no implicit conversion, or if there are multiple
3816
// conversions, we have to report an error
3818
Expression converted = null;
3819
foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3822
e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3827
// Ignore over-worked ImplicitUserConversions that do
3828
// an implicit conversion in addition to the user conversion.
3830
if (!(e is UserCast))
3833
if (converted != null){
3834
ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3843
public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3845
// LAMESPEC: For some reason it does not contain bool which looks like csc bug
3861
// Performs the basic sanity checks on the switch statement
3862
// (looks for duplicate keys and non-constant expressions).
3864
// It also returns a hashtable with the keys that we will later
3865
// use to compute the switch tables
3867
bool ResolveLabels (ResolveContext ec, Constant value)
3870
if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
3871
string_labels = new Dictionary<string, SwitchLabel> ();
3873
labels = new Dictionary<long, SwitchLabel> ();
3876
case_labels = new List<SwitchLabel> ();
3877
int default_label_index = -1;
3878
bool constant_label_found = false;
3880
for (int i = 0; i < block.Statements.Count; ++i) {
3881
var s = block.Statements[i];
3883
var sl = s as SwitchLabel;
3888
case_labels.Add (sl);
3891
if (case_default != null) {
3892
sl.Error_AlreadyOccurs (ec, SwitchType, case_default);
3896
default_label_index = i;
3901
if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3907
if (string_labels != null) {
3908
string string_value = sl.Converted.GetValue () as string;
3909
if (string_value == null)
3912
string_labels.Add (string_value, sl);
3914
if (sl.Converted is NullLiteral) {
3917
labels.Add (sl.Converted.GetValueAsLong (), sl);
3920
} catch (ArgumentException) {
3921
if (string_labels != null)
3922
sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3924
sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3929
if (value != null) {
3930
var constant_label = constant_label_found ? null : FindLabel (value);
3931
if (constant_label == null || constant_label != sl)
3932
block.Statements[i] = new EmptyStatement (s.loc);
3934
constant_label_found = true;
3938
if (value != null && constant_label_found && default_label_index >= 0)
3939
block.Statements[default_label_index] = new EmptyStatement (case_default.loc);
3945
// This method emits code for a lookup-based switch statement (non-string)
3946
// Basically it groups the cases into blocks that are at least half full,
3947
// and then spits out individual lookup opcodes for each block.
3948
// It emits the longest blocks first, and short blocks are just
3949
// handled with direct compares.
3951
void EmitTableSwitch (EmitContext ec, Expression val)
3953
if (labels != null && labels.Count > 0) {
3954
List<LabelsRange> ranges;
3955
if (string_labels != null) {
3956
// We have done all hard work for string already
3957
// setup single range only
3958
ranges = new List<LabelsRange> (1);
3959
ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3961
var element_keys = new long[labels.Count];
3962
labels.Keys.CopyTo (element_keys, 0);
3963
Array.Sort (element_keys);
3966
// Build possible ranges of switch labes to reduce number
3969
ranges = new List<LabelsRange> (element_keys.Length);
3970
var range = new LabelsRange (element_keys[0]);
3972
for (int i = 1; i < element_keys.Length; ++i) {
3973
var l = element_keys[i];
3974
if (range.AddValue (l))
3977
range = new LabelsRange (l);
3981
// sort the blocks so we can tackle the largest ones first
3985
Label lbl_default = defaultLabel;
3986
TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3988
for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3989
LabelsRange kb = ranges[range_index];
3990
lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
3992
// Optimize small ranges using simple equality check
3993
if (kb.Range <= 2) {
3994
foreach (var key in kb.label_values) {
3995
SwitchLabel sl = labels[key];
3996
if (sl == case_default || sl == case_null)
3999
if (sl.Converted.IsZeroInteger) {
4000
val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4003
sl.Converted.Emit (ec);
4004
ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4008
// TODO: if all the keys in the block are the same and there are
4009
// no gaps/defaults then just use a range-check.
4010
if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4011
// TODO: optimize constant/I4 cases
4013
// check block range (could be > 2^31)
4015
ec.EmitLong (kb.min);
4016
ec.Emit (OpCodes.Blt, lbl_default);
4019
ec.EmitLong (kb.max);
4020
ec.Emit (OpCodes.Bgt, lbl_default);
4025
ec.EmitLong (kb.min);
4026
ec.Emit (OpCodes.Sub);
4029
ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4033
int first = (int) kb.min;
4036
ec.Emit (OpCodes.Sub);
4037
} else if (first < 0) {
4038
ec.EmitInt (-first);
4039
ec.Emit (OpCodes.Add);
4043
// first, build the list of labels for the switch
4045
long cJumps = kb.Range;
4046
Label[] switch_labels = new Label[cJumps];
4047
for (int iJump = 0; iJump < cJumps; iJump++) {
4048
var key = kb.label_values[iKey];
4049
if (key == kb.min + iJump) {
4050
switch_labels[iJump] = labels[key].GetILLabel (ec);
4053
switch_labels[iJump] = lbl_default;
4057
// emit the switch opcode
4058
ec.Emit (OpCodes.Switch, switch_labels);
4061
// mark the default for this block
4062
if (range_index != 0)
4063
ec.MarkLabel (lbl_default);
4066
// the last default just goes to the end
4067
if (ranges.Count > 0)
4068
ec.Emit (OpCodes.Br, lbl_default);
4072
SwitchLabel FindLabel (Constant value)
4074
SwitchLabel sl = null;
4076
if (string_labels != null) {
4077
string s = value.GetValue () as string;
4079
if (case_null != null)
4081
else if (case_default != null)
4084
string_labels.TryGetValue (s, out sl);
4087
if (value is NullLiteral) {
4090
labels.TryGetValue (value.GetValueAsLong (), out sl);
4097
public override bool Resolve (BlockContext ec)
4099
Expr = Expr.Resolve (ec);
4103
new_expr = SwitchGoverningType (ec, Expr);
4105
if (new_expr == null && Expr.Type.IsNullableType) {
4106
unwrap = Nullable.Unwrap.Create (Expr, false);
4110
new_expr = SwitchGoverningType (ec, unwrap);
4113
if (new_expr == null){
4114
ec.Report.Error (151, loc,
4115
"A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4116
TypeManager.CSharpName (Expr.Type));
4121
SwitchType = new_expr.Type;
4123
if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4124
ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4128
if (block.Statements.Count == 0)
4131
var constant = new_expr as Constant;
4133
if (!ResolveLabels (ec, constant))
4137
// Don't need extra variable for constant switch or switch with
4138
// only default case
4140
if (constant == null && (case_labels.Count - (case_default != null ? 1 : 0)) != 0) {
4142
// Store switch expression for comparison purposes
4144
value = new_expr as VariableReference;
4145
if (value == null) {
4146
// Create temporary variable inside switch scope
4147
var current_block = ec.CurrentBlock;
4148
ec.CurrentBlock = Block;
4149
value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4151
ec.CurrentBlock = current_block;
4155
Switch old_switch = ec.Switch;
4157
ec.Switch.SwitchType = SwitchType;
4159
ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4161
ec.CurrentBranching.CurrentUsageVector.Goto ();
4163
var ok = block.Resolve (ec);
4165
if (case_default == null)
4166
ec.CurrentBranching.CurrentUsageVector.ResetBarrier ();
4168
ec.EndFlowBranching ();
4169
ec.Switch = old_switch;
4174
if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
4175
ResolveStringSwitchMap (ec);
4179
// Needed to emit anonymous storey initialization before
4180
// any generated switch dispatch
4182
block.AddScopeStatement (new DispatchStatement (this));
4187
public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4189
var sl = FindLabel (value);
4192
FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4199
// Converts string switch into string hashtable
4201
void ResolveStringSwitchMap (ResolveContext ec)
4203
FullNamedExpression string_dictionary_type;
4204
if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4205
string_dictionary_type = new TypeExpression (
4206
ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4207
new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4209
} else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4210
string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4212
ec.Module.PredefinedTypes.Dictionary.Resolve ();
4216
var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4217
Field field = new Field (ctype, string_dictionary_type,
4218
Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4219
new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4220
if (!field.Define ())
4222
ctype.AddField (field);
4224
var init = new List<Expression> ();
4226
labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4227
string value = null;
4229
foreach (SwitchLabel sl in case_labels) {
4231
if (sl.SectionStart)
4232
labels.Add (++counter, sl);
4234
if (sl == case_default || sl == case_null)
4237
value = (string) sl.Converted.GetValue ();
4238
var init_args = new List<Expression> (2);
4239
init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4241
sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4242
init_args.Add (sl.Converted);
4244
init.Add (new CollectionElementInitializer (init_args, loc));
4247
Arguments args = new Arguments (1);
4248
args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4249
Expression initializer = new NewInitialize (string_dictionary_type, args,
4250
new CollectionOrObjectInitializers (init, loc), loc);
4252
switch_cache_field = new FieldExpr (field, loc);
4253
string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4256
void DoEmitStringSwitch (EmitContext ec)
4258
Label l_initialized = ec.DefineLabel ();
4261
// Skip initialization when value is null
4263
value.EmitBranchable (ec, nullLabel, false);
4266
// Check if string dictionary is initialized and initialize
4268
switch_cache_field.EmitBranchable (ec, l_initialized, true);
4269
using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4270
string_dictionary.EmitStatement (ec);
4272
ec.MarkLabel (l_initialized);
4274
LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4276
ResolveContext rc = new ResolveContext (ec.MemberContext);
4278
if (switch_cache_field.Type.IsGeneric) {
4279
Arguments get_value_args = new Arguments (2);
4280
get_value_args.Add (new Argument (value));
4281
get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4282
Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4283
if (get_item == null)
4287
// A value was not found, go to default case
4289
get_item.EmitBranchable (ec, defaultLabel, false);
4291
Arguments get_value_args = new Arguments (1);
4292
get_value_args.Add (new Argument (value));
4294
Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4295
if (get_item == null)
4298
LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4299
get_item_object.EmitAssign (ec, get_item, true, false);
4300
ec.Emit (OpCodes.Brfalse, defaultLabel);
4302
ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4303
new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4305
get_item_int.EmitStatement (ec);
4306
get_item_object.Release (ec);
4309
EmitTableSwitch (ec, string_switch_variable);
4310
string_switch_variable.Release (ec);
4314
// Emits switch using simple if/else comparison for small label count (4 + optional default)
4316
void EmitShortSwitch (EmitContext ec)
4318
MethodSpec equal_method = null;
4319
if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4320
equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
4323
if (equal_method != null) {
4324
value.EmitBranchable (ec, nullLabel, false);
4327
for (int i = 0; i < case_labels.Count; ++i) {
4328
var label = case_labels [i];
4329
if (label == case_default || label == case_null)
4332
var constant = label.Converted;
4334
if (equal_method != null) {
4338
var call = new CallEmitter ();
4339
call.EmitPredefined (ec, equal_method, new Arguments (0));
4340
ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
4344
if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
4345
value.EmitBranchable (ec, label.GetILLabel (ec), false);
4351
ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
4354
ec.Emit (OpCodes.Br, defaultLabel);
4357
void EmitDispatch (EmitContext ec)
4359
if (value == null) {
4361
// Constant switch, we already done the work
4367
// Mark sequence point explicitly to switch
4369
ec.Mark (block.StartLocation);
4370
block.IsCompilerGenerated = true;
4372
if (string_dictionary != null) {
4373
DoEmitStringSwitch (ec);
4374
} else if (case_labels.Count < 4 || string_labels != null) {
4375
EmitShortSwitch (ec);
4377
EmitTableSwitch (ec, value);
4381
protected override void DoEmit (EmitContext ec)
4383
// Workaround broken flow-analysis
4384
block.HasUnreachableClosingBrace = true;
4387
// Setup the codegen context
4389
Label old_end = ec.LoopEnd;
4390
Switch old_switch = ec.Switch;
4392
ec.LoopEnd = ec.DefineLabel ();
4395
defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
4396
nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
4398
if (value != null) {
4401
unwrap.EmitCheck (ec);
4402
ec.Emit (OpCodes.Brfalse, nullLabel);
4403
value.EmitAssign (ec, new_expr, false, false);
4404
} else if (new_expr != value) {
4405
value.EmitAssign (ec, new_expr, false, false);
4411
// Restore context state.
4412
ec.MarkLabel (ec.LoopEnd);
4415
// Restore the previous context
4417
ec.LoopEnd = old_end;
4418
ec.Switch = old_switch;
4421
protected override void CloneTo (CloneContext clonectx, Statement t)
4423
Switch target = (Switch) t;
4425
target.Expr = Expr.Clone (clonectx);
4426
target.block = (ExplicitBlock) block.Clone (clonectx);
4429
public override object Accept (StructuralVisitor visitor)
4431
return visitor.Visit (this);
4435
// A place where execution can restart in an iterator
4436
public abstract class ResumableStatement : Statement
4439
protected Label resume_point;
4441
public Label PrepareForEmit (EmitContext ec)
4445
resume_point = ec.DefineLabel ();
4447
return resume_point;
4450
public virtual Label PrepareForDispose (EmitContext ec, Label end)
4455
public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4460
public abstract class TryFinallyBlock : ExceptionStatement
4462
protected Statement stmt;
4463
Label dispose_try_block;
4464
bool prepared_for_dispose, emitted_dispose;
4465
Method finally_host;
4467
protected TryFinallyBlock (Statement stmt, Location loc)
4475
public Statement Statement {
4483
protected abstract void EmitTryBody (EmitContext ec);
4484
public abstract void EmitFinallyBody (EmitContext ec);
4486
public override Label PrepareForDispose (EmitContext ec, Label end)
4488
if (!prepared_for_dispose) {
4489
prepared_for_dispose = true;
4490
dispose_try_block = ec.DefineLabel ();
4492
return dispose_try_block;
4495
protected sealed override void DoEmit (EmitContext ec)
4497
EmitTryBodyPrepare (ec);
4500
ec.BeginFinallyBlock ();
4502
Label start_finally = ec.DefineLabel ();
4503
if (resume_points != null) {
4504
var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4506
ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4507
ec.Emit (OpCodes.Brfalse_S, start_finally);
4508
ec.Emit (OpCodes.Endfinally);
4511
ec.MarkLabel (start_finally);
4513
if (finally_host != null) {
4514
finally_host.Define ();
4515
finally_host.Emit ();
4517
// Now it's safe to add, to close it properly and emit sequence points
4518
finally_host.Parent.AddMember (finally_host);
4520
var ce = new CallEmitter ();
4521
ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4522
ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4524
EmitFinallyBody (ec);
4527
ec.EndExceptionBlock ();
4530
public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4532
if (emitted_dispose)
4535
emitted_dispose = true;
4537
Label end_of_try = ec.DefineLabel ();
4539
// Ensure that the only way we can get into this code is through a dispatcher
4540
if (have_dispatcher)
4541
ec.Emit (OpCodes.Br, end);
4543
ec.BeginExceptionBlock ();
4545
ec.MarkLabel (dispose_try_block);
4547
Label[] labels = null;
4548
for (int i = 0; i < resume_points.Count; ++i) {
4549
ResumableStatement s = resume_points[i];
4550
Label ret = s.PrepareForDispose (ec, end_of_try);
4551
if (ret.Equals (end_of_try) && labels == null)
4553
if (labels == null) {
4554
labels = new Label[resume_points.Count];
4555
for (int j = 0; j < i; ++j)
4556
labels[j] = end_of_try;
4561
if (labels != null) {
4563
for (j = 1; j < labels.Length; ++j)
4564
if (!labels[0].Equals (labels[j]))
4566
bool emit_dispatcher = j < labels.Length;
4568
if (emit_dispatcher) {
4569
ec.Emit (OpCodes.Ldloc, pc);
4570
ec.EmitInt (first_resume_pc);
4571
ec.Emit (OpCodes.Sub);
4572
ec.Emit (OpCodes.Switch, labels);
4575
foreach (ResumableStatement s in resume_points)
4576
s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4579
ec.MarkLabel (end_of_try);
4581
ec.BeginFinallyBlock ();
4583
if (finally_host != null) {
4584
var ce = new CallEmitter ();
4585
ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4586
ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4588
EmitFinallyBody (ec);
4591
ec.EndExceptionBlock ();
4594
public override bool Resolve (BlockContext bc)
4597
// Finally block inside iterator is called from MoveNext and
4598
// Dispose methods that means we need to lift the block into
4599
// newly created host method to emit the body only once. The
4600
// original block then simply calls the newly generated method.
4602
if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4603
var b = stmt as Block;
4604
if (b != null && b.Explicit.HasYield) {
4605
finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4609
return base.Resolve (bc);
4614
// Base class for blocks using exception handling
4616
public abstract class ExceptionStatement : ResumableStatement
4621
protected List<ResumableStatement> resume_points;
4622
protected int first_resume_pc;
4624
protected ExceptionStatement (Location loc)
4629
protected virtual void EmitTryBodyPrepare (EmitContext ec)
4631
StateMachineInitializer state_machine = null;
4632
if (resume_points != null) {
4633
state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4635
ec.EmitInt ((int) IteratorStorey.State.Running);
4636
ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4639
ec.BeginExceptionBlock ();
4641
if (resume_points != null) {
4642
ec.MarkLabel (resume_point);
4644
// For normal control flow, we want to fall-through the Switch
4645
// So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4646
ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4647
ec.EmitInt (first_resume_pc);
4648
ec.Emit (OpCodes.Sub);
4650
Label[] labels = new Label[resume_points.Count];
4651
for (int i = 0; i < resume_points.Count; ++i)
4652
labels[i] = resume_points[i].PrepareForEmit (ec);
4653
ec.Emit (OpCodes.Switch, labels);
4657
public void SomeCodeFollows ()
4660
code_follows = true;
4664
public override bool Resolve (BlockContext ec)
4667
// System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4668
// So, ensure there's some IL code after this statement.
4669
if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4670
ec.NeedReturnLabel ();
4675
public void AddResumePoint (ResumableStatement stmt, int pc)
4677
if (resume_points == null) {
4678
resume_points = new List<ResumableStatement> ();
4679
first_resume_pc = pc;
4682
if (pc != first_resume_pc + resume_points.Count)
4683
throw new InternalErrorException ("missed an intervening AddResumePoint?");
4685
resume_points.Add (stmt);
4690
public class Lock : TryFinallyBlock
4693
TemporaryVariableReference expr_copy;
4694
TemporaryVariableReference lock_taken;
4696
public Lock (Expression expr, Statement stmt, Location loc)
4702
public Expression Expr {
4708
public override bool Resolve (BlockContext ec)
4710
expr = expr.Resolve (ec);
4714
if (!TypeSpec.IsReferenceType (expr.Type)) {
4715
ec.Report.Error (185, loc,
4716
"`{0}' is not a reference type as required by the lock statement",
4717
expr.Type.GetSignatureForError ());
4720
if (expr.Type.IsGenericParameter) {
4721
expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4724
VariableReference lv = expr as VariableReference;
4727
locked = lv.IsLockedByStatement;
4728
lv.IsLockedByStatement = true;
4735
// Have to keep original lock value around to unlock same location
4736
// in the case of original value has changed or is null
4738
expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4739
expr_copy.Resolve (ec);
4742
// Ensure Monitor methods are available
4744
if (ResolvePredefinedMethods (ec) > 1) {
4745
lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4746
lock_taken.Resolve (ec);
4749
using (ec.Set (ResolveContext.Options.LockScope)) {
4750
ec.StartFlowBranching (this);
4751
Statement.Resolve (ec);
4752
ec.EndFlowBranching ();
4756
lv.IsLockedByStatement = locked;
4764
protected override void EmitTryBodyPrepare (EmitContext ec)
4766
expr_copy.EmitAssign (ec, expr);
4768
if (lock_taken != null) {
4770
// Initialize ref variable
4772
lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4775
// Monitor.Enter (expr_copy)
4777
expr_copy.Emit (ec);
4778
ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4781
base.EmitTryBodyPrepare (ec);
4784
protected override void EmitTryBody (EmitContext ec)
4787
// Monitor.Enter (expr_copy, ref lock_taken)
4789
if (lock_taken != null) {
4790
expr_copy.Emit (ec);
4791
lock_taken.LocalInfo.CreateBuilder (ec);
4792
lock_taken.AddressOf (ec, AddressOp.Load);
4793
ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4796
Statement.Emit (ec);
4799
public override void EmitFinallyBody (EmitContext ec)
4802
// if (lock_taken) Monitor.Exit (expr_copy)
4804
Label skip = ec.DefineLabel ();
4806
if (lock_taken != null) {
4807
lock_taken.Emit (ec);
4808
ec.Emit (OpCodes.Brfalse_S, skip);
4811
expr_copy.Emit (ec);
4812
var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4814
ec.Emit (OpCodes.Call, m);
4816
ec.MarkLabel (skip);
4819
int ResolvePredefinedMethods (ResolveContext rc)
4821
// Try 4.0 Monitor.Enter (object, ref bool) overload first
4822
var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4826
m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4830
rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4834
protected override void CloneTo (CloneContext clonectx, Statement t)
4836
Lock target = (Lock) t;
4838
target.expr = expr.Clone (clonectx);
4839
target.stmt = Statement.Clone (clonectx);
4842
public override object Accept (StructuralVisitor visitor)
4844
return visitor.Visit (this);
4849
public class Unchecked : Statement {
4852
public Unchecked (Block b, Location loc)
4859
public override bool Resolve (BlockContext ec)
4861
using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4862
return Block.Resolve (ec);
4865
protected override void DoEmit (EmitContext ec)
4867
using (ec.With (EmitContext.Options.CheckedScope, false))
4871
protected override void CloneTo (CloneContext clonectx, Statement t)
4873
Unchecked target = (Unchecked) t;
4875
target.Block = clonectx.LookupBlock (Block);
4878
public override object Accept (StructuralVisitor visitor)
4880
return visitor.Visit (this);
4884
public class Checked : Statement {
4887
public Checked (Block b, Location loc)
4890
b.Unchecked = false;
4894
public override bool Resolve (BlockContext ec)
4896
using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4897
return Block.Resolve (ec);
4900
protected override void DoEmit (EmitContext ec)
4902
using (ec.With (EmitContext.Options.CheckedScope, true))
4906
protected override void CloneTo (CloneContext clonectx, Statement t)
4908
Checked target = (Checked) t;
4910
target.Block = clonectx.LookupBlock (Block);
4913
public override object Accept (StructuralVisitor visitor)
4915
return visitor.Visit (this);
4919
public class Unsafe : Statement {
4922
public Unsafe (Block b, Location loc)
4925
Block.Unsafe = true;
4929
public override bool Resolve (BlockContext ec)
4931
if (ec.CurrentIterator != null)
4932
ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4934
using (ec.Set (ResolveContext.Options.UnsafeScope))
4935
return Block.Resolve (ec);
4938
protected override void DoEmit (EmitContext ec)
4943
protected override void CloneTo (CloneContext clonectx, Statement t)
4945
Unsafe target = (Unsafe) t;
4947
target.Block = clonectx.LookupBlock (Block);
4950
public override object Accept (StructuralVisitor visitor)
4952
return visitor.Visit (this);
4959
public class Fixed : Statement
4961
abstract class Emitter : ShimExpression
4963
protected LocalVariable vi;
4965
protected Emitter (Expression expr, LocalVariable li)
4971
public abstract void EmitExit (EmitContext ec);
4974
class ExpressionEmitter : Emitter {
4975
public ExpressionEmitter (Expression converted, LocalVariable li) :
4976
base (converted, li)
4980
protected override Expression DoResolve (ResolveContext rc)
4982
throw new NotImplementedException ();
4985
public override void Emit (EmitContext ec) {
4987
// Store pointer in pinned location
4993
public override void EmitExit (EmitContext ec)
4996
ec.Emit (OpCodes.Conv_U);
5001
class StringEmitter : Emitter
5003
LocalVariable pinned_string;
5005
public StringEmitter (Expression expr, LocalVariable li, Location loc)
5010
protected override Expression DoResolve (ResolveContext rc)
5012
pinned_string = new LocalVariable (vi.Block, "$pinned",
5013
LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5015
pinned_string.Type = rc.BuiltinTypes.String;
5017
eclass = ExprClass.Variable;
5018
type = rc.BuiltinTypes.Int;
5022
public override void Emit (EmitContext ec)
5024
pinned_string.CreateBuilder (ec);
5027
pinned_string.EmitAssign (ec);
5029
// TODO: Should use Binary::Add
5030
pinned_string.Emit (ec);
5031
ec.Emit (OpCodes.Conv_I);
5033
var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5037
PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5038
//pe.InstanceExpression = pinned_string;
5039
pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5041
ec.Emit (OpCodes.Add);
5045
public override void EmitExit (EmitContext ec)
5048
pinned_string.EmitAssign (ec);
5053
public class VariableDeclaration : BlockVariableDeclaration
5055
public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5060
protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5062
if (!Variable.Type.IsPointer && li == Variable) {
5063
bc.Report.Error (209, TypeExpression.Location,
5064
"The type of locals declared in a fixed statement must be a pointer type");
5069
// The rules for the possible declarators are pretty wise,
5070
// but the production on the grammar is more concise.
5072
// So we have to enforce these rules here.
5074
// We do not resolve before doing the case 1 test,
5075
// because the grammar is explicit in that the token &
5076
// is present, so we need to test for this particular case.
5079
if (initializer is Cast) {
5080
bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5084
initializer = initializer.Resolve (bc);
5086
if (initializer == null)
5092
if (initializer.Type.IsArray) {
5093
TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5096
// Provided that array_type is unmanaged,
5098
if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5102
// and T* is implicitly convertible to the
5103
// pointer type given in the fixed statement.
5105
ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5107
Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5108
if (converted == null)
5112
// fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5114
converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5115
new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
5116
new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
5117
new NullLiteral (loc),
5120
converted = converted.Resolve (bc);
5122
return new ExpressionEmitter (converted, li);
5128
if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5129
return new StringEmitter (initializer, li, loc).Resolve (bc);
5132
// Case 3: fixed buffer
5133
if (initializer is FixedBufferPtr) {
5134
return new ExpressionEmitter (initializer, li);
5138
// Case 4: & object.
5140
bool already_fixed = true;
5141
Unary u = initializer as Unary;
5142
if (u != null && u.Oper == Unary.Operator.AddressOf) {
5143
IVariableReference vr = u.Expr as IVariableReference;
5144
if (vr == null || !vr.IsFixed) {
5145
already_fixed = false;
5149
if (already_fixed) {
5150
bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5153
initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5154
return new ExpressionEmitter (initializer, li);
5159
VariableDeclaration decl;
5160
Statement statement;
5163
public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5172
public Statement Statement {
5178
public BlockVariableDeclaration Variables {
5186
public override bool Resolve (BlockContext ec)
5188
using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5189
if (!decl.Resolve (ec))
5193
ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5194
bool ok = statement.Resolve (ec);
5195
bool flow_unreachable = ec.EndFlowBranching ();
5196
has_ret = flow_unreachable;
5201
protected override void DoEmit (EmitContext ec)
5203
decl.Variable.CreateBuilder (ec);
5204
decl.Initializer.Emit (ec);
5205
if (decl.Declarators != null) {
5206
foreach (var d in decl.Declarators) {
5207
d.Variable.CreateBuilder (ec);
5208
d.Initializer.Emit (ec);
5212
statement.Emit (ec);
5218
// Clear the pinned variable
5220
((Emitter) decl.Initializer).EmitExit (ec);
5221
if (decl.Declarators != null) {
5222
foreach (var d in decl.Declarators) {
5223
((Emitter)d.Initializer).EmitExit (ec);
5228
protected override void CloneTo (CloneContext clonectx, Statement t)
5230
Fixed target = (Fixed) t;
5232
target.decl = (VariableDeclaration) decl.Clone (clonectx);
5233
target.statement = statement.Clone (clonectx);
5236
public override object Accept (StructuralVisitor visitor)
5238
return visitor.Visit (this);
5242
public class Catch : Statement
5246
FullNamedExpression type_expr;
5247
CompilerAssign assign;
5250
public Catch (Block block, Location loc)
5258
public Block Block {
5264
public TypeSpec CatchType {
5270
public bool IsGeneral {
5272
return type_expr == null;
5276
public FullNamedExpression TypeExpression {
5285
public LocalVariable Variable {
5296
protected override void DoEmit (EmitContext ec)
5299
ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5301
ec.BeginCatchBlock (CatchType);
5304
li.CreateBuilder (ec);
5307
// Special case hoisted catch variable, we have to use a temporary variable
5308
// to pass via anonymous storey initialization with the value still on top
5311
if (li.HoistedVariant != null) {
5312
LocalTemporary lt = new LocalTemporary (li.Type);
5315
// switch to assigning from the temporary variable and not from top of the stack
5316
assign.UpdateSource (lt);
5319
ec.Emit (OpCodes.Pop);
5325
public override bool Resolve (BlockContext ec)
5327
using (ec.With (ResolveContext.Options.CatchScope, true)) {
5328
if (type_expr != null) {
5329
type = type_expr.ResolveAsType (ec);
5333
if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5334
ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5335
} else if (li != null) {
5337
li.PrepareForFlowAnalysis (ec);
5339
// source variable is at the top of the stack
5340
Expression source = new EmptyExpression (li.Type);
5341
if (li.Type.IsGenericParameter)
5342
source = new UnboxCast (source, li.Type);
5345
// Uses Location.Null to hide from symbol file
5347
assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5348
Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5352
return Block.Resolve (ec);
5356
protected override void CloneTo (CloneContext clonectx, Statement t)
5358
Catch target = (Catch) t;
5360
if (type_expr != null)
5361
target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5363
target.block = clonectx.LookupBlock (block);
5367
public class TryFinally : TryFinallyBlock
5371
public Statement Stmt {
5372
get { return this.stmt; }
5376
get { return this.fini; }
5379
public TryFinally (Statement stmt, Block fini, Location loc)
5385
public Block Finallyblock {
5391
public override bool Resolve (BlockContext ec)
5395
ec.StartFlowBranching (this);
5397
if (!stmt.Resolve (ec))
5401
ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5403
using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5404
if (!fini.Resolve (ec))
5408
ec.EndFlowBranching ();
5410
ok &= base.Resolve (ec);
5415
protected override void EmitTryBody (EmitContext ec)
5420
public override void EmitFinallyBody (EmitContext ec)
5425
protected override void CloneTo (CloneContext clonectx, Statement t)
5427
TryFinally target = (TryFinally) t;
5429
target.stmt = (Statement) stmt.Clone (clonectx);
5431
target.fini = clonectx.LookupBlock (fini);
5434
public override object Accept (StructuralVisitor visitor)
5436
return visitor.Visit (this);
5440
public class TryCatch : ExceptionStatement
5443
List<Catch> clauses;
5444
readonly bool inside_try_finally;
5446
public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5450
this.clauses = catch_clauses;
5451
this.inside_try_finally = inside_try_finally;
5454
public List<Catch> Clauses {
5460
public bool IsTryCatchFinally {
5462
return inside_try_finally;
5466
public override bool Resolve (BlockContext ec)
5470
ec.StartFlowBranching (this);
5472
if (!Block.Resolve (ec))
5475
for (int i = 0; i < clauses.Count; ++i) {
5477
ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5479
if (!c.Resolve (ec)) {
5484
TypeSpec resolved_type = c.CatchType;
5485
for (int ii = 0; ii < clauses.Count; ++ii) {
5489
if (clauses[ii].IsGeneral) {
5490
if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5493
if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5496
if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5499
ec.Report.Warning (1058, 1, c.loc,
5500
"A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5508
var ct = clauses[ii].CatchType;
5512
if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5513
ec.Report.Error (160, c.loc,
5514
"A previous catch clause already catches all exceptions of this or a super type `{0}'",
5515
ct.GetSignatureForError ());
5521
ec.EndFlowBranching ();
5523
return base.Resolve (ec) && ok;
5526
protected sealed override void DoEmit (EmitContext ec)
5528
if (!inside_try_finally)
5529
EmitTryBodyPrepare (ec);
5533
foreach (Catch c in clauses)
5536
if (!inside_try_finally)
5537
ec.EndExceptionBlock ();
5540
protected override void CloneTo (CloneContext clonectx, Statement t)
5542
TryCatch target = (TryCatch) t;
5544
target.Block = clonectx.LookupBlock (Block);
5545
if (clauses != null){
5546
target.clauses = new List<Catch> ();
5547
foreach (Catch c in clauses)
5548
target.clauses.Add ((Catch) c.Clone (clonectx));
5552
public override object Accept (StructuralVisitor visitor)
5554
return visitor.Visit (this);
5558
public class Using : TryFinallyBlock
5560
public class VariableDeclaration : BlockVariableDeclaration
5562
Statement dispose_call;
5564
public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5569
public VariableDeclaration (LocalVariable li, Location loc)
5575
public VariableDeclaration (Expression expr)
5578
loc = expr.Location;
5584
public bool IsNested { get; private set; }
5588
public void EmitDispose (EmitContext ec)
5590
dispose_call.Emit (ec);
5593
public override bool Resolve (BlockContext bc)
5598
return base.Resolve (bc, false);
5601
public Expression ResolveExpression (BlockContext bc)
5603
var e = Initializer.Resolve (bc);
5607
li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5608
Initializer = ResolveInitializer (bc, Variable, e);
5612
protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5614
if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5615
initializer = initializer.Resolve (bc);
5616
if (initializer == null)
5619
// Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5620
Arguments args = new Arguments (1);
5621
args.Add (new Argument (initializer));
5622
initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5623
if (initializer == null)
5626
var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5627
dispose_call = CreateDisposeCall (bc, var);
5628
dispose_call.Resolve (bc);
5630
return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5633
if (li == Variable) {
5634
CheckIDiposableConversion (bc, li, initializer);
5635
dispose_call = CreateDisposeCall (bc, li);
5636
dispose_call.Resolve (bc);
5639
return base.ResolveInitializer (bc, li, initializer);
5642
protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5646
if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5647
if (type.IsNullableType) {
5648
// it's handled in CreateDisposeCall
5652
bc.Report.SymbolRelatedToPreviousError (type);
5653
var loc = type_expr == null ? initializer.Location : type_expr.Location;
5654
bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5655
type.GetSignatureForError ());
5661
protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5663
var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5665
var loc = lv.Location;
5667
var idt = bc.BuiltinTypes.IDisposable;
5668
var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5670
var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5671
dispose_mg.InstanceExpression = type.IsNullableType ?
5672
new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5676
// Hide it from symbol file via null location
5678
Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5680
// Add conditional call when disposing possible null variable
5681
if (!type.IsStruct || type.IsNullableType)
5682
dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
5687
public void ResolveDeclaratorInitializer (BlockContext bc)
5689
Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5692
public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5694
for (int i = declarators.Count - 1; i >= 0; --i) {
5695
var d = declarators [i];
5696
var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
5697
vd.Initializer = d.Initializer;
5699
vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5700
vd.dispose_call.Resolve (bc);
5702
stmt = new Using (vd, stmt, d.Variable.Location);
5709
public override object Accept (StructuralVisitor visitor)
5711
return visitor.Visit (this);
5715
VariableDeclaration decl;
5717
public Using (VariableDeclaration decl, Statement stmt, Location loc)
5723
public Using (Expression expr, Statement stmt, Location loc)
5726
this.decl = new VariableDeclaration (expr);
5731
public Expression Expr {
5733
return decl.Variable == null ? decl.Initializer : null;
5737
public BlockVariableDeclaration Variables {
5745
public override void Emit (EmitContext ec)
5748
// Don't emit sequence point it will be set on variable declaration
5753
protected override void EmitTryBodyPrepare (EmitContext ec)
5756
base.EmitTryBodyPrepare (ec);
5759
protected override void EmitTryBody (EmitContext ec)
5764
public override void EmitFinallyBody (EmitContext ec)
5766
decl.EmitDispose (ec);
5769
public override bool Resolve (BlockContext ec)
5771
VariableReference vr;
5772
bool vr_locked = false;
5774
using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5775
if (decl.Variable == null) {
5776
vr = decl.ResolveExpression (ec) as VariableReference;
5778
vr_locked = vr.IsLockedByStatement;
5779
vr.IsLockedByStatement = true;
5782
if (decl.IsNested) {
5783
decl.ResolveDeclaratorInitializer (ec);
5785
if (!decl.Resolve (ec))
5788
if (decl.Declarators != null) {
5789
stmt = decl.RewriteUsingDeclarators (ec, stmt);
5797
ec.StartFlowBranching (this);
5801
ec.EndFlowBranching ();
5804
vr.IsLockedByStatement = vr_locked;
5811
protected override void CloneTo (CloneContext clonectx, Statement t)
5813
Using target = (Using) t;
5815
target.decl = (VariableDeclaration) decl.Clone (clonectx);
5816
target.stmt = stmt.Clone (clonectx);
5819
public override object Accept (StructuralVisitor visitor)
5821
return visitor.Visit (this);
5826
/// Implementation of the foreach C# statement
5828
public class Foreach : Statement
5830
abstract class IteratorStatement : Statement
5832
protected readonly Foreach for_each;
5834
protected IteratorStatement (Foreach @foreach)
5836
this.for_each = @foreach;
5837
this.loc = @foreach.expr.Location;
5840
protected override void CloneTo (CloneContext clonectx, Statement target)
5842
throw new NotImplementedException ();
5845
public override void Emit (EmitContext ec)
5847
if (ec.EmitAccurateDebugInfo) {
5848
ec.Emit (OpCodes.Nop);
5855
sealed class ArrayForeach : IteratorStatement
5857
TemporaryVariableReference[] lengths;
5858
Expression [] length_exprs;
5859
StatementExpression[] counter;
5860
TemporaryVariableReference[] variables;
5862
TemporaryVariableReference copy;
5864
public ArrayForeach (Foreach @foreach, int rank)
5867
counter = new StatementExpression[rank];
5868
variables = new TemporaryVariableReference[rank];
5869
length_exprs = new Expression [rank];
5872
// Only use temporary length variables when dealing with
5873
// multi-dimensional arrays
5876
lengths = new TemporaryVariableReference [rank];
5879
public override bool Resolve (BlockContext ec)
5881
Block variables_block = for_each.variable.Block;
5882
copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5885
int rank = length_exprs.Length;
5886
Arguments list = new Arguments (rank);
5887
for (int i = 0; i < rank; i++) {
5888
var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5890
counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5891
counter[i].Resolve (ec);
5894
length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5896
lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5897
lengths[i].Resolve (ec);
5899
Arguments args = new Arguments (1);
5900
args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5901
length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5904
list.Add (new Argument (v));
5907
var access = new ElementAccess (copy, list, loc).Resolve (ec);
5912
if (for_each.type is VarExpr) {
5913
// Infer implicitly typed local variable from foreach array type
5914
var_type = access.Type;
5916
var_type = for_each.type.ResolveAsType (ec);
5918
if (var_type == null)
5921
access = Convert.ExplicitConversion (ec, access, var_type, loc);
5926
for_each.variable.Type = var_type;
5928
var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5929
if (variable_ref == null)
5932
for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
5936
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5937
ec.CurrentBranching.CreateSibling ();
5939
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5940
if (!for_each.body.Resolve (ec))
5942
ec.EndFlowBranching ();
5944
// There's no direct control flow from the end of the embedded statement to the end of the loop
5945
ec.CurrentBranching.CurrentUsageVector.Goto ();
5947
ec.EndFlowBranching ();
5952
protected override void DoEmit (EmitContext ec)
5954
copy.EmitAssign (ec, for_each.expr);
5956
int rank = length_exprs.Length;
5957
Label[] test = new Label [rank];
5958
Label[] loop = new Label [rank];
5960
for (int i = 0; i < rank; i++) {
5961
test [i] = ec.DefineLabel ();
5962
loop [i] = ec.DefineLabel ();
5964
if (lengths != null)
5965
lengths [i].EmitAssign (ec, length_exprs [i]);
5968
IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5969
for (int i = 0; i < rank; i++) {
5970
variables [i].EmitAssign (ec, zero);
5972
ec.Emit (OpCodes.Br, test [i]);
5973
ec.MarkLabel (loop [i]);
5976
for_each.body.Emit (ec);
5978
ec.MarkLabel (ec.LoopBegin);
5979
ec.Mark (for_each.expr.Location);
5981
for (int i = rank - 1; i >= 0; i--){
5982
counter [i].Emit (ec);
5984
ec.MarkLabel (test [i]);
5985
variables [i].Emit (ec);
5987
if (lengths != null)
5988
lengths [i].Emit (ec);
5990
length_exprs [i].Emit (ec);
5992
ec.Emit (OpCodes.Blt, loop [i]);
5995
ec.MarkLabel (ec.LoopEnd);
5999
sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
6001
class RuntimeDispose : Using.VariableDeclaration
6003
public RuntimeDispose (LocalVariable lv, Location loc)
6008
protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6010
// Defered to runtime check
6013
protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6015
var idt = bc.BuiltinTypes.IDisposable;
6018
// Fabricates code like
6020
// if ((temp = vr as IDisposable) != null) temp.Dispose ();
6023
var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6025
var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6026
dispose_variable.CreateReferenceExpression (bc, loc),
6027
new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6028
loc), new NullLiteral (loc));
6030
var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6032
var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6033
dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6035
Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6036
return new If (idisaposable_test, dispose, loc);
6040
LocalVariable variable;
6042
Statement statement;
6043
ExpressionStatement init;
6044
TemporaryVariableReference enumerator_variable;
6045
bool ambiguous_getenumerator_name;
6047
public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6050
this.variable = var;
6054
void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6056
rc.Report.SymbolRelatedToPreviousError (enumerator);
6057
rc.Report.Error (202, loc,
6058
"foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6059
enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6062
MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6065
// Option 1: Try to match by name GetEnumerator first
6067
var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6068
"GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6070
var mg = mexpr as MethodGroupExpr;
6072
mg.InstanceExpression = expr;
6073
Arguments args = new Arguments (0);
6074
mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
6076
// For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6077
if (ambiguous_getenumerator_name)
6080
if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6086
// Option 2: Try to match using IEnumerable interfaces with preference of generic version
6089
PredefinedMember<MethodSpec> iface_candidate = null;
6090
var ptypes = rc.Module.PredefinedTypes;
6091
var gen_ienumerable = ptypes.IEnumerableGeneric;
6092
if (!gen_ienumerable.Define ())
6093
gen_ienumerable = null;
6095
var ifaces = t.Interfaces;
6096
if (ifaces != null) {
6097
foreach (var iface in ifaces) {
6098
if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6099
if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6100
rc.Report.SymbolRelatedToPreviousError (expr.Type);
6101
rc.Report.Error (1640, loc,
6102
"foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6103
expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6108
// TODO: Cache this somehow
6109
iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6110
MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6115
if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6116
iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6121
if (iface_candidate == null) {
6122
if (expr.Type != InternalType.ErrorType) {
6123
rc.Report.Error (1579, loc,
6124
"foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6125
expr.Type.GetSignatureForError (), "GetEnumerator");
6131
var method = iface_candidate.Resolve (loc);
6135
mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6136
mg.InstanceExpression = expr;
6140
MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6142
var ms = MemberCache.FindMember (enumerator.ReturnType,
6143
MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6144
BindingRestriction.InstanceOnly) as MethodSpec;
6146
if (ms == null || !ms.IsPublic) {
6147
Error_WrongEnumerator (rc, enumerator);
6151
return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6154
PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6156
var ps = MemberCache.FindMember (enumerator.ReturnType,
6157
MemberFilter.Property ("Current", null),
6158
BindingRestriction.InstanceOnly) as PropertySpec;
6160
if (ps == null || !ps.IsPublic) {
6161
Error_WrongEnumerator (rc, enumerator);
6168
public override bool Resolve (BlockContext ec)
6170
bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6173
expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6174
} else if (expr.Type.IsNullableType) {
6175
expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6178
var get_enumerator_mg = ResolveGetEnumerator (ec);
6179
if (get_enumerator_mg == null) {
6183
var get_enumerator = get_enumerator_mg.BestCandidate;
6184
enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6185
enumerator_variable.Resolve (ec);
6187
// Prepare bool MoveNext ()
6188
var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6189
if (move_next_mg == null) {
6193
move_next_mg.InstanceExpression = enumerator_variable;
6195
// Prepare ~T~ Current { get; }
6196
var current_prop = ResolveCurrent (ec, get_enumerator);
6197
if (current_prop == null) {
6201
var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6202
if (current_pe == null)
6205
VarExpr ve = for_each.type as VarExpr;
6209
// Source type is dynamic, set element type to dynamic too
6210
variable.Type = ec.BuiltinTypes.Dynamic;
6212
// Infer implicitly typed local variable from foreach enumerable type
6213
variable.Type = current_pe.Type;
6217
// Explicit cast of dynamic collection elements has to be done at runtime
6218
current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6221
variable.Type = for_each.type.ResolveAsType (ec);
6223
if (variable.Type == null)
6226
current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6227
if (current_pe == null)
6231
var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6232
if (variable_ref == null)
6235
for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
6237
var init = new Invocation (get_enumerator_mg, null);
6239
statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6240
for_each.body, Location.Null);
6242
var enum_type = enumerator_variable.Type;
6245
// Add Dispose method call when enumerator can be IDisposable
6247
if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6248
if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6250
// Runtime Dispose check
6252
var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6253
vd.Initializer = init;
6254
statement = new Using (vd, statement, Location.Null);
6257
// No Dispose call needed
6259
this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6260
this.init.Resolve (ec);
6264
// Static Dispose check
6266
var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6267
vd.Initializer = init;
6268
statement = new Using (vd, statement, Location.Null);
6271
return statement.Resolve (ec);
6274
protected override void DoEmit (EmitContext ec)
6276
enumerator_variable.LocalInfo.CreateBuilder (ec);
6279
init.EmitStatement (ec);
6281
statement.Emit (ec);
6284
#region IErrorHandler Members
6286
bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6288
ec.Report.SymbolRelatedToPreviousError (best);
6289
ec.Report.Warning (278, 2, expr.Location,
6290
"`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6291
expr.Type.GetSignatureForError (), "enumerable",
6292
best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6294
ambiguous_getenumerator_name = true;
6298
bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6303
bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6308
bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6317
LocalVariable variable;
6319
Statement statement;
6322
public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6325
this.variable = var;
6327
this.statement = stmt;
6332
public Expression Expr {
6333
get { return expr; }
6336
public Statement Statement {
6337
get { return statement; }
6340
public Expression TypeExpression {
6341
get { return type; }
6344
public LocalVariable Variable {
6345
get { return variable; }
6348
public override bool Resolve (BlockContext ec)
6350
expr = expr.Resolve (ec);
6355
ec.Report.Error (186, loc, "Use of null is not valid in this context");
6359
body.AddStatement (statement);
6361
if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6362
statement = new ArrayForeach (this, 1);
6363
} else if (expr.Type is ArrayContainer) {
6364
statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6366
if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6367
ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6368
expr.ExprClassName);
6372
statement = new CollectionForeach (this, variable, expr);
6375
return statement.Resolve (ec);
6378
protected override void DoEmit (EmitContext ec)
6380
variable.CreateBuilder (ec);
6382
Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6383
ec.LoopBegin = ec.DefineLabel ();
6384
ec.LoopEnd = ec.DefineLabel ();
6386
statement.Emit (ec);
6388
ec.LoopBegin = old_begin;
6389
ec.LoopEnd = old_end;
6392
protected override void CloneTo (CloneContext clonectx, Statement t)
6394
Foreach target = (Foreach) t;
6396
target.type = type.Clone (clonectx);
6397
target.expr = expr.Clone (clonectx);
6398
target.body = (Block) body.Clone (clonectx);
6399
target.statement = statement.Clone (clonectx);
6402
public override object Accept (StructuralVisitor visitor)
6404
return visitor.Visit (this);