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@seznam.cz)
9
// Copyright 2001, 2002, 2003 Ximian, Inc.
10
// Copyright 2003, 2004 Novell, Inc.
14
using System.Collections.Generic;
17
using IKVM.Reflection.Emit;
19
using System.Reflection.Emit;
22
namespace Mono.CSharp {
24
public abstract class Statement {
28
/// Resolves the statement, true means that all sub-statements
31
public virtual bool Resolve (BlockContext ec)
37
/// We already know that the statement is unreachable, but we still
38
/// need to resolve it to catch errors.
40
public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
43
// This conflicts with csc's way of doing this, but IMHO it's
44
// the right thing to do.
46
// If something is unreachable, we still check whether it's
47
// correct. This means that you cannot use unassigned variables
48
// in unreachable code, for instance.
52
ec.Report.Warning (162, 2, loc, "Unreachable code detected");
54
ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
55
bool ok = Resolve (ec);
56
ec.KillFlowBranching ();
62
/// Return value indicates whether all code paths emitted return.
64
protected abstract void DoEmit (EmitContext ec);
66
public virtual void Emit (EmitContext ec)
73
// This routine must be overrided in derived classes and make copies
74
// of all the data that might be modified if resolved
76
protected abstract void CloneTo (CloneContext clonectx, Statement target);
78
public Statement Clone (CloneContext clonectx)
80
Statement s = (Statement) this.MemberwiseClone ();
81
CloneTo (clonectx, s);
85
public virtual Expression CreateExpressionTree (ResolveContext ec)
87
ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
91
public virtual object Accept (StructuralVisitor visitor)
93
return visitor.Visit (this);
97
public sealed class EmptyStatement : Statement
99
public EmptyStatement (Location loc)
104
public override bool Resolve (BlockContext ec)
109
public override bool ResolveUnreachable (BlockContext ec, bool warn)
114
public override void Emit (EmitContext ec)
118
protected override void DoEmit (EmitContext ec)
120
throw new NotSupportedException ();
123
protected override void CloneTo (CloneContext clonectx, Statement target)
128
public override object Accept (StructuralVisitor visitor)
130
return visitor.Visit (this);
134
public class If : Statement {
136
public Statement TrueStatement;
137
public Statement FalseStatement;
141
public If (Expression bool_expr, Statement true_statement, Location l)
142
: this (bool_expr, true_statement, null, l)
146
public If (Expression bool_expr,
147
Statement true_statement,
148
Statement false_statement,
151
this.expr = bool_expr;
152
TrueStatement = true_statement;
153
FalseStatement = false_statement;
157
public Expression Expr {
158
get { return this.expr; }
161
public override bool Resolve (BlockContext ec)
165
expr = expr.Resolve (ec);
170
// Dead code elimination
172
if (expr is Constant) {
173
bool take = !((Constant) expr).IsDefaultValue;
176
if (!TrueStatement.Resolve (ec))
179
if ((FalseStatement != null) &&
180
!FalseStatement.ResolveUnreachable (ec, true))
182
FalseStatement = null;
184
if (!TrueStatement.ResolveUnreachable (ec, true))
186
TrueStatement = null;
188
if ((FalseStatement != null) &&
189
!FalseStatement.Resolve (ec))
197
ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
199
ok &= TrueStatement.Resolve (ec);
201
is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
203
ec.CurrentBranching.CreateSibling ();
205
if (FalseStatement != null)
206
ok &= FalseStatement.Resolve (ec);
208
ec.EndFlowBranching ();
213
protected override void DoEmit (EmitContext ec)
215
Label false_target = ec.DefineLabel ();
219
// If we're a boolean constant, Resolve() already
220
// eliminated dead code for us.
222
Constant c = expr as Constant;
224
c.EmitSideEffect (ec);
226
if (!c.IsDefaultValue)
227
TrueStatement.Emit (ec);
228
else if (FalseStatement != null)
229
FalseStatement.Emit (ec);
234
expr.EmitBranchable (ec, false_target, false);
236
TrueStatement.Emit (ec);
238
if (FalseStatement != null){
239
bool branch_emitted = false;
241
end = ec.DefineLabel ();
243
ec.Emit (OpCodes.Br, end);
244
branch_emitted = true;
247
ec.MarkLabel (false_target);
248
FalseStatement.Emit (ec);
253
ec.MarkLabel (false_target);
257
protected override void CloneTo (CloneContext clonectx, Statement t)
261
target.expr = expr.Clone (clonectx);
262
target.TrueStatement = TrueStatement.Clone (clonectx);
263
if (FalseStatement != null)
264
target.FalseStatement = FalseStatement.Clone (clonectx);
267
public override object Accept (StructuralVisitor visitor)
269
return visitor.Visit (this);
273
public class Do : Statement {
274
public Expression expr;
275
public Statement EmbeddedStatement;
277
public Do (Statement statement, BooleanExpression bool_expr, Location l)
280
EmbeddedStatement = statement;
284
public override bool Resolve (BlockContext ec)
288
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
290
bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
292
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
293
if (!EmbeddedStatement.Resolve (ec))
295
ec.EndFlowBranching ();
297
if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
298
ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
300
expr = expr.Resolve (ec);
303
else if (expr is Constant){
304
bool infinite = !((Constant) expr).IsDefaultValue;
306
ec.CurrentBranching.CurrentUsageVector.Goto ();
309
ec.EndFlowBranching ();
314
protected override void DoEmit (EmitContext ec)
316
Label loop = ec.DefineLabel ();
317
Label old_begin = ec.LoopBegin;
318
Label old_end = ec.LoopEnd;
320
ec.LoopBegin = ec.DefineLabel ();
321
ec.LoopEnd = ec.DefineLabel ();
324
EmbeddedStatement.Emit (ec);
325
ec.MarkLabel (ec.LoopBegin);
328
// Dead code elimination
330
if (expr is Constant){
331
bool res = !((Constant) expr).IsDefaultValue;
333
expr.EmitSideEffect (ec);
335
ec.Emit (OpCodes.Br, loop);
337
expr.EmitBranchable (ec, loop, true);
339
ec.MarkLabel (ec.LoopEnd);
341
ec.LoopBegin = old_begin;
342
ec.LoopEnd = old_end;
345
protected override void CloneTo (CloneContext clonectx, Statement t)
349
target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
350
target.expr = expr.Clone (clonectx);
353
public override object Accept (StructuralVisitor visitor)
355
return visitor.Visit (this);
359
public class While : Statement {
360
public Expression expr;
361
public Statement Statement;
362
bool infinite, empty;
364
public While (BooleanExpression bool_expr, Statement statement, Location l)
366
this.expr = bool_expr;
367
Statement = statement;
371
public override bool Resolve (BlockContext ec)
375
expr = expr.Resolve (ec);
380
// Inform whether we are infinite or not
382
if (expr is Constant){
383
bool value = !((Constant) expr).IsDefaultValue;
386
if (!Statement.ResolveUnreachable (ec, true))
394
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
396
ec.CurrentBranching.CreateSibling ();
398
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
399
if (!Statement.Resolve (ec))
401
ec.EndFlowBranching ();
403
// There's no direct control flow from the end of the embedded statement to the end of the loop
404
ec.CurrentBranching.CurrentUsageVector.Goto ();
406
ec.EndFlowBranching ();
411
protected override void DoEmit (EmitContext ec)
414
expr.EmitSideEffect (ec);
418
Label old_begin = ec.LoopBegin;
419
Label old_end = ec.LoopEnd;
421
ec.LoopBegin = ec.DefineLabel ();
422
ec.LoopEnd = ec.DefineLabel ();
425
// Inform whether we are infinite or not
427
if (expr is Constant){
428
// expr is 'true', since the 'empty' case above handles the 'false' case
429
ec.MarkLabel (ec.LoopBegin);
430
expr.EmitSideEffect (ec);
432
ec.Emit (OpCodes.Br, ec.LoopBegin);
435
// Inform that we are infinite (ie, `we return'), only
436
// if we do not `break' inside the code.
438
ec.MarkLabel (ec.LoopEnd);
440
Label while_loop = ec.DefineLabel ();
442
ec.Emit (OpCodes.Br, ec.LoopBegin);
443
ec.MarkLabel (while_loop);
447
ec.MarkLabel (ec.LoopBegin);
450
expr.EmitBranchable (ec, while_loop, true);
452
ec.MarkLabel (ec.LoopEnd);
455
ec.LoopBegin = old_begin;
456
ec.LoopEnd = old_end;
459
public override void Emit (EmitContext ec)
464
protected override void CloneTo (CloneContext clonectx, Statement t)
466
While target = (While) t;
468
target.expr = expr.Clone (clonectx);
469
target.Statement = Statement.Clone (clonectx);
472
public override object Accept (StructuralVisitor visitor)
474
return visitor.Visit (this);
478
public class For : Statement {
479
public Expression Test { get; private set; }
480
public Statement InitStatement { get; private set; }
481
public Statement Increment { get; private set; }
482
public Statement Statement { get; private set; }
483
bool infinite, empty;
485
public For (Statement init_statement,
486
BooleanExpression test,
491
InitStatement = init_statement;
493
Increment = increment;
494
Statement = statement;
498
public override bool Resolve (BlockContext ec)
502
if (InitStatement != null){
503
if (!InitStatement.Resolve (ec))
508
Test = Test.Resolve (ec);
511
else if (Test is Constant){
512
bool value = !((Constant) Test).IsDefaultValue;
515
if (!Statement.ResolveUnreachable (ec, true))
517
if ((Increment != null) &&
518
!Increment.ResolveUnreachable (ec, false))
528
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
530
ec.CurrentBranching.CreateSibling ();
532
bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
534
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
535
if (!Statement.Resolve (ec))
537
ec.EndFlowBranching ();
539
if (Increment != null){
540
if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
541
if (!Increment.ResolveUnreachable (ec, !was_unreachable))
544
if (!Increment.Resolve (ec))
549
// There's no direct control flow from the end of the embedded statement to the end of the loop
550
ec.CurrentBranching.CurrentUsageVector.Goto ();
552
ec.EndFlowBranching ();
557
protected override void DoEmit (EmitContext ec)
559
if (InitStatement != null)
560
InitStatement.Emit (ec);
563
Test.EmitSideEffect (ec);
567
Label old_begin = ec.LoopBegin;
568
Label old_end = ec.LoopEnd;
569
Label loop = ec.DefineLabel ();
570
Label test = ec.DefineLabel ();
572
ec.LoopBegin = ec.DefineLabel ();
573
ec.LoopEnd = ec.DefineLabel ();
575
ec.Emit (OpCodes.Br, test);
579
ec.MarkLabel (ec.LoopBegin);
584
// If test is null, there is no test, and we are just
589
// The Resolve code already catches the case for
590
// Test == Constant (false) so we know that
593
if (Test is Constant) {
594
Test.EmitSideEffect (ec);
595
ec.Emit (OpCodes.Br, loop);
597
Test.EmitBranchable (ec, loop, true);
601
ec.Emit (OpCodes.Br, loop);
602
ec.MarkLabel (ec.LoopEnd);
604
ec.LoopBegin = old_begin;
605
ec.LoopEnd = old_end;
608
protected override void CloneTo (CloneContext clonectx, Statement t)
610
For target = (For) t;
612
if (InitStatement != null)
613
target.InitStatement = InitStatement.Clone (clonectx);
615
target.Test = Test.Clone (clonectx);
616
if (Increment != null)
617
target.Increment = Increment.Clone (clonectx);
618
target.Statement = Statement.Clone (clonectx);
621
public override object Accept (StructuralVisitor visitor)
623
return visitor.Visit (this);
627
public class StatementExpression : Statement {
628
ExpressionStatement expr;
630
public StatementExpression (ExpressionStatement expr)
636
public ExpressionStatement Expr {
637
get { return this.expr; }
640
public override bool Resolve (BlockContext ec)
642
expr = expr.ResolveStatement (ec);
646
protected override void DoEmit (EmitContext ec)
648
expr.EmitStatement (ec);
651
public override string ToString ()
653
return "StatementExpression (" + expr + ")";
656
protected override void CloneTo (CloneContext clonectx, Statement t)
658
StatementExpression target = (StatementExpression) t;
660
target.expr = (ExpressionStatement) expr.Clone (clonectx);
663
public override object Accept (StructuralVisitor visitor)
665
return visitor.Visit (this);
670
// Simple version of statement list not requiring a block
672
public class StatementList : Statement
674
List<Statement> statements;
676
public StatementList (Statement first, Statement second)
678
statements = new List<Statement> () { first, second };
682
public IList<Statement> Statements {
689
public void Add (Statement statement)
691
statements.Add (statement);
694
public override bool Resolve (BlockContext ec)
696
foreach (var s in statements)
702
protected override void DoEmit (EmitContext ec)
704
foreach (var s in statements)
708
protected override void CloneTo (CloneContext clonectx, Statement target)
710
StatementList t = (StatementList) target;
712
t.statements = new List<Statement> (statements.Count);
713
foreach (Statement s in statements)
714
t.statements.Add (s.Clone (clonectx));
717
public override object Accept (StructuralVisitor visitor)
719
return visitor.Visit (this);
723
// A 'return' or a 'yield break'
724
public abstract class ExitStatement : Statement
726
protected bool unwind_protect;
727
protected abstract bool DoResolve (BlockContext ec);
729
public virtual void Error_FinallyClause (Report Report)
731
Report.Error (157, loc, "Control cannot leave the body of a finally clause");
734
public sealed override bool Resolve (BlockContext ec)
739
unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
741
ec.NeedReturnLabel ();
742
ec.CurrentBranching.CurrentUsageVector.Goto ();
748
/// Implements the return statement
750
public class Return : ExitStatement
752
public Expression Expr { get; protected set; }
754
public Return (Expression expr, Location l)
761
public Expression Expression {
768
protected override bool DoResolve (BlockContext ec)
771
if (ec.ReturnType.Kind == MemberKind.Void)
774
if (ec.CurrentIterator != null) {
775
Error_ReturnFromIterator (ec);
777
ec.Report.Error (126, loc,
778
"An object of a type convertible to `{0}' is required for the return statement",
779
ec.ReturnType.GetSignatureForError ());
785
Expr = Expr.Resolve (ec);
787
AnonymousExpression am = ec.CurrentAnonymousMethod;
789
if (ec.ReturnType.Kind == MemberKind.Void) {
790
ec.Report.Error (127, loc,
791
"`{0}': A return keyword must not be followed by any expression when method returns void",
792
ec.GetSignatureForError ());
796
Error_ReturnFromIterator (ec);
800
var l = am as AnonymousMethodBody;
801
if (l != null && l.ReturnTypeInference != null && Expr != null) {
802
l.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
810
if (Expr.Type != ec.ReturnType) {
811
Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
815
ec.Report.Error (1662, loc,
816
"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",
817
am.ContainerType, am.GetSignatureForError ());
826
protected override void DoEmit (EmitContext ec)
832
ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
836
ec.Emit (OpCodes.Leave, ec.ReturnLabel);
838
ec.Emit (OpCodes.Ret);
841
void Error_ReturnFromIterator (ResolveContext rc)
843
rc.Report.Error (1622, loc,
844
"Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
847
protected override void CloneTo (CloneContext clonectx, Statement t)
849
Return target = (Return) t;
850
// It's null for simple return;
852
target.Expr = Expr.Clone (clonectx);
854
public override object Accept (StructuralVisitor visitor)
856
return visitor.Visit (this);
860
public class Goto : Statement {
862
LabeledStatement label;
865
public override bool Resolve (BlockContext ec)
867
unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
868
ec.CurrentBranching.CurrentUsageVector.Goto ();
872
public Goto (string label, Location l)
878
public string Target {
879
get { return target; }
882
public void SetResolvedTarget (LabeledStatement label)
885
label.AddReference ();
888
protected override void CloneTo (CloneContext clonectx, Statement target)
893
protected override void DoEmit (EmitContext ec)
896
throw new InternalErrorException ("goto emitted before target resolved");
897
Label l = label.LabelTarget (ec);
898
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
901
public override object Accept (StructuralVisitor visitor)
903
return visitor.Visit (this);
907
public class LabeledStatement : Statement {
914
FlowBranching.UsageVector vectors;
916
public LabeledStatement (string name, Block block, Location l)
923
public Label LabelTarget (EmitContext ec)
928
label = ec.DefineLabel ();
943
public bool IsDefined {
944
get { return defined; }
947
public bool HasBeenReferenced {
948
get { return referenced; }
951
public FlowBranching.UsageVector JumpOrigins {
952
get { return vectors; }
955
public void AddUsageVector (FlowBranching.UsageVector vector)
957
vector = vector.Clone ();
958
vector.Next = vectors;
962
protected override void CloneTo (CloneContext clonectx, Statement target)
967
public override bool Resolve (BlockContext ec)
969
// this flow-branching will be terminated when the surrounding block ends
970
ec.StartFlowBranching (this);
974
protected override void DoEmit (EmitContext ec)
976
if (!HasBeenReferenced)
977
ec.Report.Warning (164, 2, loc, "This label has not been referenced");
980
ec.MarkLabel (label);
983
public void AddReference ()
988
public override object Accept (StructuralVisitor visitor)
990
return visitor.Visit (this);
996
/// `goto default' statement
998
public class GotoDefault : Statement {
1000
public GotoDefault (Location l)
1005
protected override void CloneTo (CloneContext clonectx, Statement target)
1010
public override bool Resolve (BlockContext ec)
1012
ec.CurrentBranching.CurrentUsageVector.Goto ();
1014
if (ec.Switch == null) {
1015
ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1019
if (!ec.Switch.GotDefault) {
1020
FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1027
protected override void DoEmit (EmitContext ec)
1029
ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
1032
public override object Accept (StructuralVisitor visitor)
1034
return visitor.Visit (this);
1039
/// `goto case' statement
1041
public class GotoCase : Statement {
1045
public GotoCase (Expression e, Location l)
1051
public Expression Expr {
1052
get { return this.expr; }
1055
public override bool Resolve (BlockContext ec)
1057
if (ec.Switch == null){
1058
ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1062
ec.CurrentBranching.CurrentUsageVector.Goto ();
1064
expr = expr.Resolve (ec);
1068
Constant c = expr as Constant;
1070
ec.Report.Error (150, expr.Location, "A constant value is expected");
1075
if (ec.Switch.IsNullable && c is NullLiteral) {
1078
TypeSpec type = ec.Switch.SwitchType;
1079
res = c.TryReduce (ec, type, c.Location);
1081
c.Error_ValueCannotBeConverted (ec, loc, type, true);
1085
if (!Convert.ImplicitStandardConversionExists (c, type))
1086
ec.Report.Warning (469, 2, loc,
1087
"The `goto case' value is not implicitly convertible to type `{0}'",
1088
TypeManager.CSharpName (type));
1092
sl = ec.Switch.ResolveGotoCase (ec, res);
1096
protected override void DoEmit (EmitContext ec)
1098
ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1101
protected override void CloneTo (CloneContext clonectx, Statement t)
1103
GotoCase target = (GotoCase) t;
1105
target.expr = expr.Clone (clonectx);
1108
public override object Accept (StructuralVisitor visitor)
1110
return visitor.Visit (this);
1114
public class Throw : Statement {
1117
public Throw (Expression expr, Location l)
1123
public Expression Expr {
1124
get { return this.expr; }
1127
public override bool Resolve (BlockContext ec)
1130
ec.CurrentBranching.CurrentUsageVector.Goto ();
1131
return ec.CurrentBranching.CheckRethrow (loc);
1134
expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1135
ec.CurrentBranching.CurrentUsageVector.Goto ();
1140
var et = ec.BuiltinTypes.Exception;
1141
if (Convert.ImplicitConversionExists (ec, expr, et))
1142
expr = Convert.ImplicitConversion (ec, expr, et, loc);
1144
ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1149
protected override void DoEmit (EmitContext ec)
1152
ec.Emit (OpCodes.Rethrow);
1156
ec.Emit (OpCodes.Throw);
1160
protected override void CloneTo (CloneContext clonectx, Statement t)
1162
Throw target = (Throw) t;
1165
target.expr = expr.Clone (clonectx);
1168
public override object Accept (StructuralVisitor visitor)
1170
return visitor.Visit (this);
1174
public class Break : Statement {
1176
public Break (Location l)
1181
bool unwind_protect;
1183
public override bool Resolve (BlockContext ec)
1185
unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1186
ec.CurrentBranching.CurrentUsageVector.Goto ();
1190
protected override void DoEmit (EmitContext ec)
1192
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1195
protected override void CloneTo (CloneContext clonectx, Statement t)
1200
public override object Accept (StructuralVisitor visitor)
1202
return visitor.Visit (this);
1206
public class Continue : Statement {
1208
public Continue (Location l)
1213
bool unwind_protect;
1215
public override bool Resolve (BlockContext ec)
1217
unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1218
ec.CurrentBranching.CurrentUsageVector.Goto ();
1222
protected override void DoEmit (EmitContext ec)
1224
ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1227
protected override void CloneTo (CloneContext clonectx, Statement t)
1232
public override object Accept (StructuralVisitor visitor)
1234
return visitor.Visit (this);
1238
public interface ILocalVariable
1240
void Emit (EmitContext ec);
1241
void EmitAssign (EmitContext ec);
1242
void EmitAddressOf (EmitContext ec);
1245
public interface INamedBlockVariable
1247
Block Block { get; }
1248
Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1249
bool IsDeclared { get; }
1250
Location Location { get; }
1253
public class BlockVariableDeclaration : Statement
1255
public class Declarator
1258
Expression initializer;
1260
public Declarator (LocalVariable li, Expression initializer)
1262
if (li.Type != null)
1263
throw new ArgumentException ("Expected null variable type");
1266
this.initializer = initializer;
1269
public Declarator (Declarator clone, Expression initializer)
1272
this.initializer = initializer;
1277
public LocalVariable Variable {
1283
public Expression Initializer {
1288
initializer = value;
1295
Expression initializer;
1296
protected FullNamedExpression type_expr;
1297
protected LocalVariable li;
1298
protected List<Declarator> declarators;
1300
public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1302
this.type_expr = type;
1304
this.loc = type_expr.Location;
1307
protected BlockVariableDeclaration (LocalVariable li)
1314
public List<Declarator> Declarators {
1320
public Expression Initializer {
1325
initializer = value;
1329
public FullNamedExpression TypeExpression {
1335
public LocalVariable Variable {
1343
public void AddDeclarator (Declarator decl)
1345
if (declarators == null)
1346
declarators = new List<Declarator> ();
1348
declarators.Add (decl);
1351
void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1353
var container = bc.CurrentMemberDefinition.Parent;
1355
Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1356
new MemberName (li.Name, li.Location), null);
1358
container.AddField (f);
1361
li.HoistedVariant = new HoistedEvaluatorVariable (f);
1365
public override bool Resolve (BlockContext bc)
1367
if (li.Type == null) {
1368
TypeSpec type = null;
1369
var vexpr = type_expr as VarExpr;
1372
// C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1373
// same name exists or as a keyword when no type was found
1375
if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1376
if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1377
bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1380
bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1384
if (li.IsConstant) {
1385
bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1389
if (Initializer == null) {
1390
bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1394
if (declarators != null) {
1395
bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1399
Initializer = Initializer.Resolve (bc);
1400
if (Initializer != null) {
1401
((VarExpr) type_expr).InferType (bc, Initializer);
1402
type = type_expr.Type;
1407
type = type_expr.ResolveAsType (bc);
1411
if (li.IsConstant && !type.IsConstantCompatible) {
1412
Const.Error_InvalidConstantType (type, loc, bc.Report);
1417
FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1422
bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1424
CreateEvaluatorVariable (bc, li);
1426
li.PrepareForFlowAnalysis (bc);
1429
if (initializer != null) {
1430
initializer = ResolveInitializer (bc, li, initializer);
1431
// li.Variable.DefinitelyAssigned
1434
if (declarators != null) {
1435
foreach (var d in declarators) {
1436
d.Variable.Type = li.Type;
1438
CreateEvaluatorVariable (bc, d.Variable);
1440
d.Variable.PrepareForFlowAnalysis (bc);
1443
if (d.Initializer != null) {
1444
d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1445
// d.Variable.DefinitelyAssigned
1453
protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1455
var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1456
return a.ResolveStatement (bc);
1459
protected override void DoEmit (EmitContext ec)
1464
li.CreateBuilder (ec);
1466
if (Initializer != null)
1467
((ExpressionStatement) Initializer).EmitStatement (ec);
1469
if (declarators != null) {
1470
foreach (var d in declarators) {
1471
d.Variable.CreateBuilder (ec);
1472
if (d.Initializer != null)
1473
((ExpressionStatement) d.Initializer).EmitStatement (ec);
1478
protected override void CloneTo (CloneContext clonectx, Statement target)
1480
BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1482
if (type_expr != null)
1483
t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1485
if (initializer != null)
1486
t.initializer = initializer.Clone (clonectx);
1488
if (declarators != null) {
1489
t.declarators = null;
1490
foreach (var d in declarators)
1491
t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1495
public override object Accept (StructuralVisitor visitor)
1497
return visitor.Visit (this);
1501
public class BlockConstantDeclaration : BlockVariableDeclaration
1503
public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1508
protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1510
initializer = initializer.Resolve (bc);
1511
if (initializer == null)
1514
var c = initializer as Constant;
1516
initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1520
c = c.ConvertImplicitly (li.Type);
1522
if (TypeSpec.IsReferenceType (li.Type))
1523
initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1525
initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
1530
li.ConstantValue = c;
1534
public override object Accept (StructuralVisitor visitor)
1536
return visitor.Visit (this);
1541
// The information about a user-perceived local variable
1543
public class LocalVariable : INamedBlockVariable, ILocalVariable
1550
AddressTaken = 1 << 2,
1551
CompilerGenerated = 1 << 3,
1553
ForeachVariable = 1 << 5,
1554
FixedVariable = 1 << 6,
1555
UsingVariable = 1 << 7,
1556
// DefinitelyAssigned = 1 << 8,
1559
ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1563
readonly string name;
1564
readonly Location loc;
1565
readonly Block block;
1567
Constant const_value;
1569
public VariableInfo VariableInfo;
1570
HoistedVariable hoisted_variant;
1572
LocalBuilder builder;
1574
public LocalVariable (Block block, string name, Location loc)
1581
public LocalVariable (Block block, string name, Flags flags, Location loc)
1582
: this (block, name, loc)
1588
// Used by variable declarators
1590
public LocalVariable (LocalVariable li, string name, Location loc)
1591
: this (li.block, name, li.flags, loc)
1597
public bool AddressTaken {
1598
get { return (flags & Flags.AddressTaken) != 0; }
1599
set { flags |= Flags.AddressTaken; }
1602
public Block Block {
1608
public Constant ConstantValue {
1613
const_value = value;
1618
// Hoisted local variable variant
1620
public HoistedVariable HoistedVariant {
1622
return hoisted_variant;
1625
hoisted_variant = value;
1629
public bool IsDeclared {
1631
return type != null;
1635
public bool IsConstant {
1637
return (flags & Flags.Constant) != 0;
1641
public bool IsLocked {
1643
return (flags & Flags.IsLocked) != 0;
1646
flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1650
public bool IsThis {
1652
return (flags & Flags.IsThis) != 0;
1656
public bool IsFixed {
1658
return (flags & Flags.FixedVariable) != 0;
1662
public bool IsReadonly {
1664
return (flags & Flags.ReadonlyMask) != 0;
1668
public Location Location {
1674
public string Name {
1680
public TypeSpec Type {
1691
public void CreateBuilder (EmitContext ec)
1693
if ((flags & Flags.Used) == 0) {
1694
if (VariableInfo == null) {
1695
// Missing flow analysis or wrong variable flags
1696
throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1699
if (VariableInfo.IsEverAssigned)
1700
ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1702
ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1705
if (HoistedVariant != null)
1708
if (builder != null) {
1709
if ((flags & Flags.CompilerGenerated) != 0)
1712
// To avoid Used warning duplicates
1713
throw new InternalErrorException ("Already created variable `{0}'", name);
1717
// All fixed variabled are pinned, a slot has to be alocated
1719
builder = ec.DeclareLocal (Type, IsFixed);
1720
if (SymbolWriter.HasSymbolWriter)
1721
ec.DefineLocalVariable (name, builder);
1724
public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1726
LocalVariable li = new LocalVariable (block, "<$$>", Flags.CompilerGenerated | Flags.Used, loc);
1731
public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1733
if (IsConstant && const_value != null)
1734
return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1736
return new LocalVariableReference (this, loc);
1739
public void Emit (EmitContext ec)
1741
// TODO: Need something better for temporary variables
1742
if ((flags & Flags.CompilerGenerated) != 0)
1745
ec.Emit (OpCodes.Ldloc, builder);
1748
public void EmitAssign (EmitContext ec)
1750
// TODO: Need something better for temporary variables
1751
if ((flags & Flags.CompilerGenerated) != 0)
1754
ec.Emit (OpCodes.Stloc, builder);
1757
public void EmitAddressOf (EmitContext ec)
1759
ec.Emit (OpCodes.Ldloca, builder);
1762
public string GetReadOnlyContext ()
1764
switch (flags & Flags.ReadonlyMask) {
1765
case Flags.FixedVariable:
1766
return "fixed variable";
1767
case Flags.ForeachVariable:
1768
return "foreach iteration variable";
1769
case Flags.UsingVariable:
1770
return "using variable";
1773
throw new InternalErrorException ("Variable is not readonly");
1776
public bool IsThisAssigned (BlockContext ec, Block block)
1778
if (VariableInfo == null)
1779
throw new Exception ();
1781
if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1784
return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1787
public bool IsAssigned (BlockContext ec)
1789
if (VariableInfo == null)
1790
throw new Exception ();
1792
return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1795
public void PrepareForFlowAnalysis (BlockContext bc)
1798
// No need for definitely assigned check for these guys
1800
if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1803
VariableInfo = new VariableInfo (this, bc.FlowOffset);
1804
bc.FlowOffset += VariableInfo.Length;
1808
// Mark the variables as referenced in the user code
1810
public void SetIsUsed ()
1812
flags |= Flags.Used;
1815
public override string ToString ()
1817
return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
1822
/// Block represents a C# block.
1826
/// This class is used in a number of places: either to represent
1827
/// explicit blocks that the programmer places or implicit blocks.
1829
/// Implicit blocks are used as labels or to introduce variable
1832
/// Top-level blocks derive from Block, and they are called ToplevelBlock
1833
/// they contain extra information that is not necessary on normal blocks.
1835
public class Block : Statement {
1843
HasCapturedVariable = 64,
1844
HasCapturedThis = 1 << 7,
1845
IsExpressionTree = 1 << 8,
1846
CompilerGenerated = 1 << 9
1849
public Block Parent;
1850
public Location StartLocation;
1851
public Location EndLocation;
1853
public ExplicitBlock Explicit;
1854
public ParametersBlock ParametersBlock;
1856
protected Flags flags;
1859
// The statements in this block
1861
protected List<Statement> statements;
1863
protected List<Statement> scope_initializers;
1865
int? resolving_init_idx;
1867
protected Block original;
1871
public int ID = id++;
1873
static int clone_id_counter;
1877
// int assignable_slots;
1878
bool unreachable_shown;
1881
public Block (Block parent, Location start, Location end)
1882
: this (parent, 0, start, end)
1886
public Block (Block parent, Flags flags, Location start, Location end)
1888
if (parent != null) {
1889
// the appropriate constructors will fixup these fields
1890
ParametersBlock = parent.ParametersBlock;
1891
Explicit = parent.Explicit;
1894
this.Parent = parent;
1896
this.StartLocation = start;
1897
this.EndLocation = end;
1899
statements = new List<Statement> (4);
1901
this.original = this;
1906
public bool HasRet {
1907
get { return (flags & Flags.HasRet) != 0; }
1910
public Block Original {
1916
public bool IsCompilerGenerated {
1917
get { return (flags & Flags.CompilerGenerated) != 0; }
1918
set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
1921
public bool Unchecked {
1922
get { return (flags & Flags.Unchecked) != 0; }
1923
set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1926
public bool Unsafe {
1927
get { return (flags & Flags.Unsafe) != 0; }
1928
set { flags |= Flags.Unsafe; }
1931
public List<Statement> Statements {
1932
get { return this.statements; }
1936
public Block CreateSwitchBlock (Location start)
1938
// FIXME: Only explicit block should be created
1939
var new_block = new Block (this, start, start);
1940
new_block.IsCompilerGenerated = true;
1944
public void SetEndLocation (Location loc)
1949
public void AddLabel (LabeledStatement target)
1951
ParametersBlock.TopBlock.AddLabel (target.Name, target);
1954
public void AddLocalName (LocalVariable li)
1956
AddLocalName (li.Name, li);
1959
public virtual void AddLocalName (string name, INamedBlockVariable li)
1961
ParametersBlock.TopBlock.AddLocalName (name, li);
1964
public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
1966
if (reason == null) {
1967
Error_AlreadyDeclared (name, variable);
1971
ParametersBlock.TopBlock.Report.Error (136, variable.Location,
1972
"A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
1973
"to `{0}', which is already used in a `{1}' scope to denote something else",
1977
public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
1979
var pi = variable as ParametersBlock.ParameterInfo;
1981
var p = pi.Parameter;
1982
ParametersBlock.TopBlock.Report.Error (100, p.Location, "The parameter name `{0}' is a duplicate", p.Name);
1985
ParametersBlock.TopBlock.Report.Error (128, variable.Location,
1986
"A local variable named `{0}' is already defined in this scope", name);
1989
public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
1991
ParametersBlock.TopBlock.Report.Error (412, loc,
1992
"The type parameter name `{0}' is the same as local variable or parameter name",
1997
// It should be used by expressions which require to
1998
// register a statement during resolve process.
2000
public void AddScopeStatement (Statement s)
2002
if (scope_initializers == null)
2003
scope_initializers = new List<Statement> ();
2006
// Simple recursive helper, when resolve scope initializer another
2007
// new scope initializer can be added, this ensures it's initialized
2008
// before existing one. For now this can happen with expression trees
2009
// in base ctor initializer only
2011
if (resolving_init_idx.HasValue) {
2012
scope_initializers.Insert (resolving_init_idx.Value, s);
2013
++resolving_init_idx;
2015
scope_initializers.Add (s);
2019
public void AddStatement (Statement s)
2024
public int AssignableSlots {
2026
// FIXME: HACK, we don't know the block available variables count now, so set this high enough
2028
// return assignable_slots;
2032
public LabeledStatement LookupLabel (string name)
2034
return ParametersBlock.TopBlock.GetLabel (name, this);
2037
public override bool Resolve (BlockContext ec)
2039
Block prev_block = ec.CurrentBlock;
2042
ec.CurrentBlock = this;
2043
ec.StartFlowBranching (this);
2046
// Compiler generated scope statements
2048
if (scope_initializers != null) {
2049
for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2050
scope_initializers[resolving_init_idx.Value].Resolve (ec);
2053
resolving_init_idx = null;
2057
// This flag is used to notate nested statements as unreachable from the beginning of this block.
2058
// For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2059
// from the beginning of the function. The outer Resolve() that detected the unreachability is
2060
// responsible for handling the situation.
2062
int statement_count = statements.Count;
2063
for (int ix = 0; ix < statement_count; ix++){
2064
Statement s = statements [ix];
2067
// Warn if we detect unreachable code.
2070
if (s is EmptyStatement)
2073
if (!unreachable_shown && !(s is LabeledStatement)) {
2074
ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2075
unreachable_shown = true;
2078
Block c_block = s as Block;
2079
if (c_block != null)
2080
c_block.unreachable = c_block.unreachable_shown = true;
2084
// Note that we're not using ResolveUnreachable() for unreachable
2085
// statements here. ResolveUnreachable() creates a temporary
2086
// flow branching and kills it afterwards. This leads to problems
2087
// if you have two unreachable statements where the first one
2088
// assigns a variable and the second one tries to access it.
2091
if (!s.Resolve (ec)) {
2093
if (ec.IsInProbingMode)
2096
statements [ix] = new EmptyStatement (s.loc);
2100
if (unreachable && !(s is LabeledStatement) && !(s is Block))
2101
statements [ix] = new EmptyStatement (s.loc);
2103
unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2104
if (unreachable && s is LabeledStatement)
2105
throw new InternalErrorException ("should not happen");
2108
while (ec.CurrentBranching is FlowBranchingLabeled)
2109
ec.EndFlowBranching ();
2111
bool flow_unreachable = ec.EndFlowBranching ();
2113
ec.CurrentBlock = prev_block;
2115
if (flow_unreachable)
2116
flags |= Flags.HasRet;
2118
// If we're a non-static `struct' constructor which doesn't have an
2119
// initializer, then we must initialize all of the struct's fields.
2120
if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2126
public override bool ResolveUnreachable (BlockContext ec, bool warn)
2128
unreachable_shown = true;
2132
ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2134
ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2135
bool ok = Resolve (ec);
2136
ec.KillFlowBranching ();
2141
protected override void DoEmit (EmitContext ec)
2143
for (int ix = 0; ix < statements.Count; ix++){
2144
statements [ix].Emit (ec);
2148
public override void Emit (EmitContext ec)
2150
if (scope_initializers != null)
2151
EmitScopeInitializers (ec);
2153
ec.Mark (StartLocation);
2156
if (SymbolWriter.HasSymbolWriter)
2157
EmitSymbolInfo (ec);
2160
protected void EmitScopeInitializers (EmitContext ec)
2162
SymbolWriter.OpenCompilerGeneratedBlock (ec);
2164
using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2165
foreach (Statement s in scope_initializers)
2169
SymbolWriter.CloseCompilerGeneratedBlock (ec);
2172
protected virtual void EmitSymbolInfo (EmitContext ec)
2177
public override string ToString ()
2179
return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2183
protected override void CloneTo (CloneContext clonectx, Statement t)
2185
Block target = (Block) t;
2187
target.clone_id = clone_id_counter++;
2190
clonectx.AddBlockMap (this, target);
2191
if (original != this)
2192
clonectx.AddBlockMap (original, target);
2194
target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2195
target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2198
target.Parent = clonectx.RemapBlockCopy (Parent);
2200
target.statements = new List<Statement> (statements.Count);
2201
foreach (Statement s in statements)
2202
target.statements.Add (s.Clone (clonectx));
2204
public override object Accept (StructuralVisitor visitor)
2206
return visitor.Visit (this);
2210
public class ExplicitBlock : Block
2212
protected AnonymousMethodStorey am_storey;
2214
public ExplicitBlock (Block parent, Location start, Location end)
2215
: this (parent, (Flags) 0, start, end)
2219
public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2220
: base (parent, flags, start, end)
2222
this.Explicit = this;
2227
public AnonymousMethodStorey AnonymousMethodStorey {
2233
public bool HasCapturedThis {
2234
set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2236
return (flags & Flags.HasCapturedThis) != 0;
2240
public bool HasCapturedVariable {
2241
set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2243
return (flags & Flags.HasCapturedVariable) != 0;
2250
// Creates anonymous method storey in current block
2252
public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2255
// An iterator has only 1 storey block
2257
if (ec.CurrentIterator != null)
2258
return ec.CurrentIterator.Storey;
2261
// When referencing a variable in iterator storey from children anonymous method
2263
if (ParametersBlock.am_storey is IteratorStorey) {
2264
return ParametersBlock.am_storey;
2267
if (am_storey == null) {
2268
MemberBase mc = ec.MemberContext as MemberBase;
2271
// Creates anonymous method storey for this block
2273
am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2279
public override void Emit (EmitContext ec)
2281
if (am_storey != null) {
2282
DefineAnonymousStorey (ec);
2283
am_storey.EmitStoreyInstantiation (ec, this);
2286
bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2287
if (emit_debug_info)
2292
if (emit_debug_info)
2296
void DefineAnonymousStorey (EmitContext ec)
2299
// Creates anonymous method storey
2301
if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2303
// Creates parent storey reference when hoisted this is accessible
2305
if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2306
ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2309
// Hoisted this exists in top-level parent storey only
2311
while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2312
parent = parent.Parent.Explicit;
2314
am_storey.AddParentStoreyReference (ec, parent.am_storey);
2317
am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2319
// TODO MemberCache: Review
2320
am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2323
am_storey.CreateType ();
2324
am_storey.DefineType ();
2325
am_storey.ResolveTypeParameters ();
2327
var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2328
if (ref_blocks != null) {
2329
foreach (ExplicitBlock ref_block in ref_blocks) {
2330
for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2331
if (b.am_storey != null) {
2332
b.am_storey.AddParentStoreyReference (ec, am_storey);
2334
// Stop propagation inside same top block
2335
if (b.ParametersBlock.Original == ParametersBlock.Original)
2338
b = b.ParametersBlock;
2341
b.HasCapturedVariable = true;
2346
am_storey.Define ();
2347
am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2350
public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2352
tryBlock.statements = statements;
2353
statements = new List<Statement> (1);
2354
statements.Add (tf);
2359
// ParametersBlock was introduced to support anonymous methods
2360
// and lambda expressions
2362
public class ParametersBlock : ExplicitBlock
2364
public class ParameterInfo : INamedBlockVariable
2366
readonly ParametersBlock block;
2368
public VariableInfo VariableInfo;
2371
public ParameterInfo (ParametersBlock block, int index)
2379
public Block Block {
2385
public bool IsDeclared {
2391
public bool IsLocked {
2400
public Location Location {
2402
return Parameter.Location;
2406
public Parameter Parameter {
2408
return block.Parameters [index];
2412
public TypeSpec ParameterType {
2414
return Parameter.Type;
2420
public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2422
return new ParameterReference (this, loc);
2427
// Block is converted to an expression
2429
sealed class BlockScopeExpression : Expression
2432
readonly ParametersBlock block;
2434
public BlockScopeExpression (Expression child, ParametersBlock block)
2440
public override Expression CreateExpressionTree (ResolveContext ec)
2442
throw new NotSupportedException ();
2445
protected override Expression DoResolve (ResolveContext ec)
2450
child = child.Resolve (ec);
2454
eclass = child.eclass;
2459
public override void Emit (EmitContext ec)
2461
block.EmitScopeInitializers (ec);
2466
protected ParametersCompiled parameters;
2467
protected ParameterInfo[] parameter_info;
2469
protected bool unreachable;
2470
protected ToplevelBlock top_block;
2472
public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2473
: base (parent, 0, start, start)
2475
if (parameters == null)
2476
throw new ArgumentNullException ("parameters");
2478
this.parameters = parameters;
2479
ParametersBlock = this;
2481
this.top_block = parent.ParametersBlock.top_block;
2482
ProcessParameters ();
2485
protected ParametersBlock (ParametersCompiled parameters, Location start)
2486
: base (null, 0, start, start)
2488
if (parameters == null)
2489
throw new ArgumentNullException ("parameters");
2491
this.parameters = parameters;
2492
ParametersBlock = this;
2496
// It's supposed to be used by method body implementation of anonymous methods
2498
protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2499
: base (null, 0, source.StartLocation, source.EndLocation)
2501
this.parameters = parameters;
2502
this.statements = source.statements;
2503
this.scope_initializers = source.scope_initializers;
2505
this.resolved = true;
2506
this.unreachable = source.unreachable;
2507
this.am_storey = source.am_storey;
2509
ParametersBlock = this;
2512
// Overwrite original for comparison purposes when linking cross references
2513
// between anonymous methods
2521
// Block has been converted to expression tree
2523
public bool IsExpressionTree {
2525
return (flags & Flags.IsExpressionTree) != 0;
2530
// The parameters for the block.
2532
public ParametersCompiled Parameters {
2538
public ToplevelBlock TopBlock {
2544
public bool Resolved {
2553
// Check whether all `out' parameters have been assigned.
2555
public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2557
if (vector.IsUnreachable)
2560
int n = parameter_info == null ? 0 : parameter_info.Length;
2562
for (int i = 0; i < n; i++) {
2563
VariableInfo var = parameter_info[i].VariableInfo;
2568
if (vector.IsAssigned (var, false))
2571
TopBlock.Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2576
public override Expression CreateExpressionTree (ResolveContext ec)
2578
if (statements.Count == 1) {
2579
Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2580
if (scope_initializers != null)
2581
expr = new BlockScopeExpression (expr, this);
2586
return base.CreateExpressionTree (ec);
2589
public ParameterInfo GetParameterInfo (Parameter p)
2591
for (int i = 0; i < parameters.Count; ++i) {
2592
if (parameters[i] == p)
2593
return parameter_info[i];
2596
throw new ArgumentException ("Invalid parameter");
2599
public Expression GetParameterReference (int index, Location loc)
2601
return new ParameterReference (parameter_info[index], loc);
2604
public Statement PerformClone ()
2606
CloneContext clonectx = new CloneContext ();
2607
return Clone (clonectx);
2610
protected void ProcessParameters ()
2612
if (parameters.Count == 0)
2615
parameter_info = new ParameterInfo[parameters.Count];
2616
for (int i = 0; i < parameter_info.Length; ++i) {
2617
var p = parameters.FixedParameters[i];
2621
// TODO: Should use Parameter only and more block there
2622
parameter_info[i] = new ParameterInfo (this, i);
2624
AddLocalName (p.Name, parameter_info[i]);
2628
public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2635
if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2636
flags |= Flags.IsExpressionTree;
2641
using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2642
FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2647
unreachable = top_level.End ();
2649
} catch (Exception e) {
2650
if (e is CompletionResult || rc.Report.IsDisabled)
2653
if (rc.CurrentBlock != null) {
2654
rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2656
rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2659
if (rc.Module.Compiler.Settings.DebugFlags > 0)
2663
if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
2664
if (rc.CurrentAnonymousMethod == null) {
2665
// FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2666
if (md is IteratorMethod) {
2669
rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2673
rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2674
rc.CurrentAnonymousMethod.GetSignatureForError ());
2682
void ResolveMeta (BlockContext ec)
2684
int orig_count = parameters.Count;
2686
for (int i = 0; i < orig_count; ++i) {
2687
Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2689
if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2692
VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2693
parameter_info[i].VariableInfo = vi;
2694
ec.FlowOffset += vi.Length;
2698
public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
2700
ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2701
pb.EndLocation = EndLocation;
2702
pb.statements = statements;
2704
var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2705
am_storey = new IteratorStorey (iterator);
2707
statements = new List<Statement> (1);
2708
AddStatement (new Return (iterator, iterator.Location));
2715
public class ToplevelBlock : ParametersBlock
2717
LocalVariable this_variable;
2718
CompilerContext compiler;
2719
Dictionary<string, object> names;
2720
Dictionary<string, object> labels;
2722
public HoistedVariable HoistedThisVariable;
2724
public Report Report {
2725
get { return compiler.Report; }
2728
public ToplevelBlock (CompilerContext ctx, Location loc)
2729
: this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2733
public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2734
: base (parameters, start)
2736
this.compiler = ctx;
2739
ProcessParameters ();
2743
// Recreates a top level block from parameters block. Used for
2744
// compiler generated methods where the original block comes from
2745
// explicit child block. This works for already resolved blocks
2746
// only to ensure we resolve them in the correct flow order
2748
public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
2749
: base (source, parameters)
2751
this.compiler = source.TopBlock.compiler;
2755
public override void AddLocalName (string name, INamedBlockVariable li)
2758
names = new Dictionary<string, object> ();
2761
if (!names.TryGetValue (name, out value)) {
2762
names.Add (name, li);
2766
INamedBlockVariable existing = value as INamedBlockVariable;
2767
List<INamedBlockVariable> existing_list;
2768
if (existing != null) {
2769
existing_list = new List<INamedBlockVariable> ();
2770
existing_list.Add (existing);
2771
names[name] = existing_list;
2773
existing_list = (List<INamedBlockVariable>) value;
2777
// A collision checking between local names
2779
for (int i = 0; i < existing_list.Count; ++i) {
2780
existing = existing_list[i];
2781
Block b = existing.Block;
2783
// Collision at same level
2784
if (li.Block == b) {
2785
li.Block.Error_AlreadyDeclared (name, li);
2789
// Collision with parent
2791
while ((b = b.Parent) != null) {
2792
if (existing.Block == b) {
2793
li.Block.Error_AlreadyDeclared (name, li, "parent or current");
2794
i = existing_list.Count;
2799
// Collision with with children
2801
while ((b = b.Parent) != null) {
2802
if (li.Block == b) {
2803
li.Block.Error_AlreadyDeclared (name, li, "child");
2804
i = existing_list.Count;
2810
existing_list.Add (li);
2813
public void AddLabel (string name, LabeledStatement label)
2816
labels = new Dictionary<string, object> ();
2819
if (!labels.TryGetValue (name, out value)) {
2820
labels.Add (name, label);
2824
LabeledStatement existing = value as LabeledStatement;
2825
List<LabeledStatement> existing_list;
2826
if (existing != null) {
2827
existing_list = new List<LabeledStatement> ();
2828
existing_list.Add (existing);
2829
labels[name] = existing_list;
2831
existing_list = (List<LabeledStatement>) value;
2835
// A collision checking between labels
2837
for (int i = 0; i < existing_list.Count; ++i) {
2838
existing = existing_list[i];
2839
Block b = existing.Block;
2841
// Collision at same level
2842
if (label.Block == b) {
2843
Report.SymbolRelatedToPreviousError (existing.loc, name);
2844
Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
2848
// Collision with parent
2850
while ((b = b.Parent) != null) {
2851
if (existing.Block == b) {
2852
Report.Error (158, label.loc,
2853
"The label `{0}' shadows another label by the same name in a contained scope", name);
2854
i = existing_list.Count;
2859
// Collision with with children
2861
while ((b = b.Parent) != null) {
2862
if (label.Block == b) {
2863
Report.Error (158, label.loc,
2864
"The label `{0}' shadows another label by the same name in a contained scope", name);
2865
i = existing_list.Count;
2871
existing_list.Add (label);
2875
// Creates an arguments set from all parameters, useful for method proxy calls
2877
public Arguments GetAllParametersArguments ()
2879
int count = parameters.Count;
2880
Arguments args = new Arguments (count);
2881
for (int i = 0; i < count; ++i) {
2882
var arg_expr = GetParameterReference (i, parameter_info[i].Location);
2883
args.Add (new Argument (arg_expr));
2890
// Lookup inside a block, the returned value can represent 3 states
2892
// true+variable: A local name was found and it's valid
2893
// false+variable: A local name was found in a child block only
2894
// false+null: No local name was found
2896
public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
2902
if (!names.TryGetValue (name, out value))
2905
variable = value as INamedBlockVariable;
2907
if (variable != null) {
2909
if (variable.Block == b.Original)
2913
} while (b != null);
2921
} while (b != null);
2923
List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
2924
for (int i = 0; i < list.Count; ++i) {
2927
if (variable.Block == b.Original)
2931
} while (b != null);
2939
} while (b != null);
2949
public LabeledStatement GetLabel (string name, Block block)
2955
if (!labels.TryGetValue (name, out value)) {
2959
var label = value as LabeledStatement;
2961
if (label != null) {
2962
if (label.Block == b.Original)
2965
// TODO: Temporary workaround for the switch block implicit label block
2966
if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2969
List<LabeledStatement> list = (List<LabeledStatement>) value;
2970
for (int i = 0; i < list.Count; ++i) {
2972
if (label.Block == b.Original)
2975
// TODO: Temporary workaround for the switch block implicit label block
2976
if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2985
// Returns the "this" instance variable of this block.
2986
// See AddThisVariable() for more information.
2988
public LocalVariable ThisVariable {
2989
get { return this_variable; }
2993
// This is used by non-static `struct' constructors which do not have an
2994
// initializer - in this case, the constructor must initialize all of the
2995
// struct's fields. To do this, we add a "this" variable and use the flow
2996
// analysis code to ensure that it's been fully initialized before control
2997
// leaves the constructor.
2999
public LocalVariable AddThisVariable (BlockContext bc, TypeContainer ds, Location l)
3001
if (this_variable == null) {
3002
this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, l);
3003
this_variable.Type = ds.CurrentType;
3004
this_variable.PrepareForFlowAnalysis (bc);
3007
return this_variable;
3010
public bool IsIterator {
3011
get { return (flags & Flags.IsIterator) != 0; }
3012
set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
3015
public bool IsThisAssigned (BlockContext ec)
3017
return this_variable == null || this_variable.IsThisAssigned (ec, this);
3020
public override void Emit (EmitContext ec)
3022
if (Report.Errors > 0)
3028
if (ec.HasReturnLabel)
3029
ec.ReturnLabel = ec.DefineLabel ();
3033
ec.Mark (EndLocation);
3035
if (ec.HasReturnLabel)
3036
ec.MarkLabel (ec.ReturnLabel);
3038
if (ec.return_value != null) {
3039
ec.Emit (OpCodes.Ldloc, ec.return_value);
3040
ec.Emit (OpCodes.Ret);
3043
// If `HasReturnLabel' is set, then we already emitted a
3044
// jump to the end of the method, so we must emit a `ret'
3047
// Unfortunately, System.Reflection.Emit automatically emits
3048
// a leave to the end of a finally block. This is a problem
3049
// if no code is following the try/finally block since we may
3050
// jump to a point after the end of the method.
3051
// As a workaround, we're always creating a return label in
3055
if (ec.HasReturnLabel || !unreachable) {
3056
if (ec.ReturnType.Kind != MemberKind.Void)
3057
ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3058
ec.Emit (OpCodes.Ret);
3063
} catch (Exception e){
3064
Console.WriteLine ("Exception caught by the compiler while emitting:");
3065
Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3067
Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3073
protected override void EmitSymbolInfo (EmitContext ec)
3075
AnonymousExpression ae = ec.CurrentAnonymousMethod;
3076
if ((ae != null) && (ae.Storey != null))
3077
SymbolWriter.DefineScopeVariable (ae.Storey.ID);
3079
base.EmitSymbolInfo (ec);
3083
public class SwitchLabel {
3086
readonly Location loc;
3091
// if expr == null, then it is the default case.
3093
public SwitchLabel (Expression expr, Location l)
3099
public bool IsDefault {
3101
return label == null;
3105
public Expression Label {
3111
public Location Location {
3117
public Constant Converted {
3126
public Label GetILLabel (EmitContext ec)
3128
if (il_label == null){
3129
il_label = ec.DefineLabel ();
3132
return il_label.Value;
3136
// Resolves the expression, reduces it to a literal if possible
3137
// and then converts it to the requested type.
3139
public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3141
Expression e = label.Resolve (ec);
3146
Constant c = e as Constant;
3148
ec.Report.Error (150, loc, "A constant value is expected");
3152
if (allow_nullable && c is NullLiteral) {
3157
converted = c.ImplicitConversionRequired (ec, required_type, loc);
3158
return converted != null;
3161
public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3164
if (converted == null)
3167
label = converted.GetValueAsLiteral ();
3169
ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3170
ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3173
public SwitchLabel Clone (CloneContext clonectx)
3178
return new SwitchLabel (label.Clone (clonectx), loc);
3182
public class SwitchSection {
3183
public readonly List<SwitchLabel> Labels;
3184
public readonly Block Block;
3186
public SwitchSection (List<SwitchLabel> labels, Block block)
3192
public SwitchSection Clone (CloneContext clonectx)
3194
var cloned_labels = new List<SwitchLabel> ();
3196
foreach (SwitchLabel sl in Labels)
3197
cloned_labels.Add (sl.Clone (clonectx));
3199
return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3203
public class Switch : Statement
3205
// structure used to hold blocks of keys while calculating table switch
3206
sealed class LabelsRange : IComparable<LabelsRange>
3208
public readonly long min;
3210
public readonly List<long> label_values;
3212
public LabelsRange (long value)
3215
label_values = new List<long> ();
3216
label_values.Add (value);
3219
public LabelsRange (long min, long max, ICollection<long> values)
3223
this.label_values = new List<long> (values);
3228
return max - min + 1;
3232
public bool AddValue (long value)
3234
var gap = value - min + 1;
3235
// Ensure the range has > 50% occupancy
3236
if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3240
label_values.Add (value);
3244
public int CompareTo (LabelsRange other)
3246
int nLength = label_values.Count;
3247
int nLengthOther = other.label_values.Count;
3248
if (nLengthOther == nLength)
3249
return (int) (other.min - min);
3251
return nLength - nLengthOther;
3255
public List<SwitchSection> Sections;
3256
public Expression Expr;
3259
// Mapping of all labels to their SwitchLabels
3261
Dictionary<long, SwitchLabel> labels;
3262
Dictionary<string, SwitchLabel> string_labels;
3265
/// The governing switch type
3267
public TypeSpec SwitchType;
3272
Label default_target;
3274
Expression new_expr;
3277
SwitchSection constant_section;
3278
SwitchSection default_section;
3279
SwitchLabel null_section;
3281
ExpressionStatement string_dictionary;
3282
FieldExpr switch_cache_field;
3283
static int unique_counter;
3284
ExplicitBlock block;
3287
// Nullable Types support
3289
Nullable.Unwrap unwrap;
3291
public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3299
public ExplicitBlock Block {
3305
public Label DefaultLabel {
3307
return default_target;
3311
public bool GotDefault {
3313
return default_section != null;
3317
public bool IsNullable {
3319
return unwrap != null;
3324
// Determines the governing type for a switch. The returned
3325
// expression might be the expression from the switch, or an
3326
// expression that includes any potential conversions to
3328
Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3330
switch (expr.Type.BuiltinType) {
3331
case BuiltinTypeSpec.Type.Byte:
3332
case BuiltinTypeSpec.Type.SByte:
3333
case BuiltinTypeSpec.Type.UShort:
3334
case BuiltinTypeSpec.Type.Short:
3335
case BuiltinTypeSpec.Type.UInt:
3336
case BuiltinTypeSpec.Type.Int:
3337
case BuiltinTypeSpec.Type.ULong:
3338
case BuiltinTypeSpec.Type.Long:
3339
case BuiltinTypeSpec.Type.Char:
3340
case BuiltinTypeSpec.Type.String:
3341
case BuiltinTypeSpec.Type.Bool:
3345
if (expr.Type.IsEnum)
3349
// Try to find a *user* defined implicit conversion.
3351
// If there is no implicit conversion, or if there are multiple
3352
// conversions, we have to report an error
3354
Expression converted = null;
3355
foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3358
e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3363
// Ignore over-worked ImplicitUserConversions that do
3364
// an implicit conversion in addition to the user conversion.
3366
if (!(e is UserCast))
3369
if (converted != null){
3370
ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3379
public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3381
// LAMESPEC: For some reason it does not contain bool which looks like csc bug
3397
// Performs the basic sanity checks on the switch statement
3398
// (looks for duplicate keys and non-constant expressions).
3400
// It also returns a hashtable with the keys that we will later
3401
// use to compute the switch tables
3403
bool CheckSwitch (ResolveContext ec)
3406
if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3407
string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3409
labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3411
foreach (SwitchSection ss in Sections){
3412
foreach (SwitchLabel sl in ss.Labels){
3414
if (default_section != null){
3415
sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3418
default_section = ss;
3422
if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3428
if (string_labels != null) {
3429
string s = sl.Converted.GetValue () as string;
3433
string_labels.Add (s, sl);
3435
if (sl.Converted is NullLiteral) {
3438
labels.Add (sl.Converted.GetValueAsLong (), sl);
3441
} catch (ArgumentException) {
3442
if (string_labels != null)
3443
sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3445
sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3455
// This method emits code for a lookup-based switch statement (non-string)
3456
// Basically it groups the cases into blocks that are at least half full,
3457
// and then spits out individual lookup opcodes for each block.
3458
// It emits the longest blocks first, and short blocks are just
3459
// handled with direct compares.
3461
void EmitTableSwitch (EmitContext ec, Expression val)
3463
Label lbl_default = default_target;
3465
if (labels.Count > 0) {
3466
List<LabelsRange> ranges;
3467
if (string_labels != null) {
3468
// We have done all hard work for string already
3469
// setup single range only
3470
ranges = new List<LabelsRange> (1);
3471
ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3473
var element_keys = new long[labels.Count];
3474
labels.Keys.CopyTo (element_keys, 0);
3475
Array.Sort (element_keys);
3478
// Build possible ranges of switch labes to reduce number
3481
ranges = new List<LabelsRange> (element_keys.Length);
3482
var range = new LabelsRange (element_keys[0]);
3484
for (int i = 1; i < element_keys.Length; ++i) {
3485
var l = element_keys[i];
3486
if (range.AddValue (l))
3489
range = new LabelsRange (l);
3493
// sort the blocks so we can tackle the largest ones first
3497
TypeSpec compare_type = TypeManager.IsEnumType (SwitchType) ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3499
for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3500
LabelsRange kb = ranges[range_index];
3501
lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3503
// Optimize small ranges using simple equality check
3504
if (kb.Range <= 2) {
3505
foreach (var key in kb.label_values) {
3506
SwitchLabel sl = labels[key];
3507
if (sl.Converted.IsDefaultValue) {
3508
val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3511
sl.Converted.Emit (ec);
3512
ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3516
// TODO: if all the keys in the block are the same and there are
3517
// no gaps/defaults then just use a range-check.
3518
if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3519
// TODO: optimize constant/I4 cases
3521
// check block range (could be > 2^31)
3523
ec.EmitLong (kb.min);
3524
ec.Emit (OpCodes.Blt, lbl_default);
3527
ec.EmitLong (kb.max);
3528
ec.Emit (OpCodes.Bgt, lbl_default);
3533
ec.EmitLong (kb.min);
3534
ec.Emit (OpCodes.Sub);
3537
ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3541
int first = (int) kb.min;
3544
ec.Emit (OpCodes.Sub);
3545
} else if (first < 0) {
3546
ec.EmitInt (-first);
3547
ec.Emit (OpCodes.Add);
3551
// first, build the list of labels for the switch
3553
long cJumps = kb.Range;
3554
Label[] switch_labels = new Label[cJumps];
3555
for (int iJump = 0; iJump < cJumps; iJump++) {
3556
var key = kb.label_values[iKey];
3557
if (key == kb.min + iJump) {
3558
switch_labels[iJump] = labels[key].GetILLabel (ec);
3561
switch_labels[iJump] = lbl_default;
3565
// emit the switch opcode
3566
ec.Emit (OpCodes.Switch, switch_labels);
3569
// mark the default for this block
3570
if (range_index != 0)
3571
ec.MarkLabel (lbl_default);
3574
// the last default just goes to the end
3575
if (ranges.Count > 0)
3576
ec.Emit (OpCodes.Br, lbl_default);
3579
// now emit the code for the sections
3580
bool found_default = false;
3582
foreach (SwitchSection ss in Sections) {
3583
foreach (SwitchLabel sl in ss.Labels) {
3585
ec.MarkLabel (lbl_default);
3586
found_default = true;
3587
if (null_section == null)
3588
ec.MarkLabel (null_target);
3589
} else if (sl.Converted.IsNull) {
3590
ec.MarkLabel (null_target);
3593
ec.MarkLabel (sl.GetILLabel (ec));
3599
if (!found_default) {
3600
ec.MarkLabel (lbl_default);
3601
if (null_section == null) {
3602
ec.MarkLabel (null_target);
3607
SwitchLabel FindLabel (Constant value)
3609
SwitchLabel sl = null;
3611
if (string_labels != null) {
3612
string s = value.GetValue () as string;
3614
if (null_section != null)
3616
else if (default_section != null)
3617
sl = default_section.Labels[0];
3619
string_labels.TryGetValue (s, out sl);
3622
if (value is NullLiteral) {
3625
labels.TryGetValue (value.GetValueAsLong (), out sl);
3632
SwitchSection FindSection (SwitchLabel label)
3634
foreach (SwitchSection ss in Sections){
3635
foreach (SwitchLabel sl in ss.Labels){
3644
public static void Reset ()
3649
public override bool Resolve (BlockContext ec)
3651
Expr = Expr.Resolve (ec);
3655
new_expr = SwitchGoverningType (ec, Expr);
3657
if (new_expr == null && Expr.Type.IsNullableType) {
3658
unwrap = Nullable.Unwrap.Create (Expr, false);
3662
new_expr = SwitchGoverningType (ec, unwrap);
3665
if (new_expr == null){
3666
ec.Report.Error (151, loc,
3667
"A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3668
TypeManager.CSharpName (Expr.Type));
3673
SwitchType = new_expr.Type;
3675
if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
3676
ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
3680
if (!CheckSwitch (ec))
3683
Switch old_switch = ec.Switch;
3685
ec.Switch.SwitchType = SwitchType;
3687
ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3689
var constant = new_expr as Constant;
3690
if (constant != null) {
3692
SwitchLabel label = FindLabel (constant);
3694
constant_section = FindSection (label);
3696
if (constant_section == null)
3697
constant_section = default_section;
3702
foreach (SwitchSection ss in Sections){
3704
ec.CurrentBranching.CreateSibling (
3705
null, FlowBranching.SiblingType.SwitchSection);
3709
if (is_constant && (ss != constant_section)) {
3710
// If we're a constant switch, we're only emitting
3711
// one single section - mark all the others as
3713
ec.CurrentBranching.CurrentUsageVector.Goto ();
3714
if (!ss.Block.ResolveUnreachable (ec, true)) {
3718
if (!ss.Block.Resolve (ec))
3723
if (default_section == null)
3724
ec.CurrentBranching.CreateSibling (
3725
null, FlowBranching.SiblingType.SwitchSection);
3727
ec.EndFlowBranching ();
3728
ec.Switch = old_switch;
3733
if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && !is_constant) {
3734
// TODO: Optimize single case, and single+default case
3735
ResolveStringSwitchMap (ec);
3741
public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
3743
var sl = FindLabel (value);
3746
FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
3752
void ResolveStringSwitchMap (ResolveContext ec)
3754
FullNamedExpression string_dictionary_type;
3755
if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
3756
string_dictionary_type = new TypeExpression (
3757
ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
3758
new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
3760
} else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
3761
string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
3763
ec.Module.PredefinedTypes.Dictionary.Resolve ();
3767
var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3768
Field field = new Field (ctype, string_dictionary_type,
3769
Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3770
new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3771
if (!field.Define ())
3773
ctype.AddField (field);
3775
var init = new List<Expression> ();
3777
labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
3778
string value = null;
3779
foreach (SwitchSection section in Sections) {
3780
bool contains_label = false;
3781
foreach (SwitchLabel sl in section.Labels) {
3782
if (sl.IsDefault || sl.Converted.IsNull)
3785
if (!contains_label) {
3786
labels.Add (counter, sl);
3787
contains_label = true;
3790
value = (string) sl.Converted.GetValue ();
3791
var init_args = new List<Expression> (2);
3792
init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
3794
sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
3795
init_args.Add (sl.Converted);
3797
init.Add (new CollectionElementInitializer (init_args, loc));
3801
// Don't add empty sections
3807
Arguments args = new Arguments (1);
3808
args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
3809
Expression initializer = new NewInitialize (string_dictionary_type, args,
3810
new CollectionOrObjectInitializers (init, loc), loc);
3812
switch_cache_field = new FieldExpr (field, loc);
3813
string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3816
void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3818
Label l_initialized = ec.DefineLabel ();
3821
// Skip initialization when value is null
3823
value.EmitBranchable (ec, null_target, false);
3826
// Check if string dictionary is initialized and initialize
3828
switch_cache_field.EmitBranchable (ec, l_initialized, true);
3829
string_dictionary.EmitStatement (ec);
3830
ec.MarkLabel (l_initialized);
3832
LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
3834
ResolveContext rc = new ResolveContext (ec.MemberContext);
3836
if (switch_cache_field.Type.IsGeneric) {
3837
Arguments get_value_args = new Arguments (2);
3838
get_value_args.Add (new Argument (value));
3839
get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3840
Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3841
if (get_item == null)
3845
// A value was not found, go to default case
3847
get_item.EmitBranchable (ec, default_target, false);
3849
Arguments get_value_args = new Arguments (1);
3850
get_value_args.Add (new Argument (value));
3852
Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3853
if (get_item == null)
3856
LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
3857
get_item_object.EmitAssign (ec, get_item, true, false);
3858
ec.Emit (OpCodes.Brfalse, default_target);
3860
ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3861
new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
3863
get_item_int.EmitStatement (ec);
3864
get_item_object.Release (ec);
3867
EmitTableSwitch (ec, string_switch_variable);
3868
string_switch_variable.Release (ec);
3871
protected override void DoEmit (EmitContext ec)
3874
// Needed to emit anonymous storey initialization
3875
// Otherwise it does not contain any statements for now
3879
default_target = ec.DefineLabel ();
3880
null_target = ec.DefineLabel ();
3882
// Store variable for comparission purposes
3883
// TODO: Don't duplicate non-captured VariableReference
3884
LocalTemporary value;
3886
value = new LocalTemporary (SwitchType);
3887
unwrap.EmitCheck (ec);
3888
ec.Emit (OpCodes.Brfalse, null_target);
3891
} else if (!is_constant) {
3892
value = new LocalTemporary (SwitchType);
3899
// Setup the codegen context
3901
Label old_end = ec.LoopEnd;
3902
Switch old_switch = ec.Switch;
3904
ec.LoopEnd = ec.DefineLabel ();
3909
if (constant_section != null)
3910
constant_section.Block.Emit (ec);
3911
} else if (string_dictionary != null) {
3912
DoEmitStringSwitch (value, ec);
3914
EmitTableSwitch (ec, value);
3920
// Restore context state.
3921
ec.MarkLabel (ec.LoopEnd);
3924
// Restore the previous context
3926
ec.LoopEnd = old_end;
3927
ec.Switch = old_switch;
3930
protected override void CloneTo (CloneContext clonectx, Statement t)
3932
Switch target = (Switch) t;
3934
target.Expr = Expr.Clone (clonectx);
3935
target.Sections = new List<SwitchSection> ();
3936
foreach (SwitchSection ss in Sections){
3937
target.Sections.Add (ss.Clone (clonectx));
3941
public override object Accept (StructuralVisitor visitor)
3943
return visitor.Visit (this);
3947
// A place where execution can restart in an iterator
3948
public abstract class ResumableStatement : Statement
3951
protected Label resume_point;
3953
public Label PrepareForEmit (EmitContext ec)
3957
resume_point = ec.DefineLabel ();
3959
return resume_point;
3962
public virtual Label PrepareForDispose (EmitContext ec, Label end)
3966
public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3971
// Base class for statements that are implemented in terms of try...finally
3972
public abstract class ExceptionStatement : ResumableStatement
3976
List<ResumableStatement> resume_points;
3977
int first_resume_pc;
3978
protected Statement stmt;
3979
Label dispose_try_block;
3980
bool prepared_for_dispose, emitted_dispose;
3982
protected ExceptionStatement (Statement stmt, Location loc)
3990
public Statement Statement {
3998
protected abstract void EmitPreTryBody (EmitContext ec);
3999
protected abstract void EmitTryBody (EmitContext ec);
4000
protected abstract void EmitFinallyBody (EmitContext ec);
4002
protected sealed override void DoEmit (EmitContext ec)
4004
EmitPreTryBody (ec);
4006
if (resume_points != null) {
4007
ec.EmitInt ((int) Iterator.State.Running);
4008
ec.Emit (OpCodes.Stloc, iter.CurrentPC);
4011
ec.BeginExceptionBlock ();
4013
if (resume_points != null) {
4014
ec.MarkLabel (resume_point);
4016
// For normal control flow, we want to fall-through the Switch
4017
// So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4018
ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
4019
ec.EmitInt (first_resume_pc);
4020
ec.Emit (OpCodes.Sub);
4022
Label [] labels = new Label [resume_points.Count];
4023
for (int i = 0; i < resume_points.Count; ++i)
4024
labels [i] = resume_points [i].PrepareForEmit (ec);
4025
ec.Emit (OpCodes.Switch, labels);
4030
ec.BeginFinallyBlock ();
4032
Label start_finally = ec.DefineLabel ();
4033
if (resume_points != null) {
4034
ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
4035
ec.Emit (OpCodes.Brfalse_S, start_finally);
4036
ec.Emit (OpCodes.Endfinally);
4039
ec.MarkLabel (start_finally);
4040
EmitFinallyBody (ec);
4042
ec.EndExceptionBlock ();
4045
public void SomeCodeFollows ()
4047
code_follows = true;
4050
public override bool Resolve (BlockContext ec)
4052
// System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4053
// So, ensure there's some IL code after this statement.
4054
if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4055
ec.NeedReturnLabel ();
4057
iter = ec.CurrentIterator;
4061
public void AddResumePoint (ResumableStatement stmt, int pc)
4063
if (resume_points == null) {
4064
resume_points = new List<ResumableStatement> ();
4065
first_resume_pc = pc;
4068
if (pc != first_resume_pc + resume_points.Count)
4069
throw new InternalErrorException ("missed an intervening AddResumePoint?");
4071
resume_points.Add (stmt);
4074
public override Label PrepareForDispose (EmitContext ec, Label end)
4076
if (!prepared_for_dispose) {
4077
prepared_for_dispose = true;
4078
dispose_try_block = ec.DefineLabel ();
4080
return dispose_try_block;
4083
public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4085
if (emitted_dispose)
4088
emitted_dispose = true;
4090
Label end_of_try = ec.DefineLabel ();
4092
// Ensure that the only way we can get into this code is through a dispatcher
4093
if (have_dispatcher)
4094
ec.Emit (OpCodes.Br, end);
4096
ec.BeginExceptionBlock ();
4098
ec.MarkLabel (dispose_try_block);
4100
Label [] labels = null;
4101
for (int i = 0; i < resume_points.Count; ++i) {
4102
ResumableStatement s = (ResumableStatement) resume_points [i];
4103
Label ret = s.PrepareForDispose (ec, end_of_try);
4104
if (ret.Equals (end_of_try) && labels == null)
4106
if (labels == null) {
4107
labels = new Label [resume_points.Count];
4108
for (int j = 0; j < i; ++j)
4109
labels [j] = end_of_try;
4114
if (labels != null) {
4116
for (j = 1; j < labels.Length; ++j)
4117
if (!labels [0].Equals (labels [j]))
4119
bool emit_dispatcher = j < labels.Length;
4121
if (emit_dispatcher) {
4122
//SymbolWriter.StartIteratorDispatcher (ec.ig);
4123
ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4124
ec.EmitInt (first_resume_pc);
4125
ec.Emit (OpCodes.Sub);
4126
ec.Emit (OpCodes.Switch, labels);
4127
//SymbolWriter.EndIteratorDispatcher (ec.ig);
4130
foreach (ResumableStatement s in resume_points)
4131
s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4134
ec.MarkLabel (end_of_try);
4136
ec.BeginFinallyBlock ();
4138
EmitFinallyBody (ec);
4140
ec.EndExceptionBlock ();
4144
public class Lock : ExceptionStatement {
4146
TemporaryVariableReference expr_copy;
4147
TemporaryVariableReference lock_taken;
4149
public Lock (Expression expr, Statement stmt, Location loc)
4155
public Expression Expr {
4156
get { return this.expr; }
4159
public override bool Resolve (BlockContext ec)
4161
expr = expr.Resolve (ec);
4165
if (!TypeSpec.IsReferenceType (expr.Type)) {
4166
ec.Report.Error (185, loc,
4167
"`{0}' is not a reference type as required by the lock statement",
4168
expr.Type.GetSignatureForError ());
4171
if (expr.Type.IsGenericParameter) {
4172
expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4175
VariableReference lv = expr as VariableReference;
4178
locked = lv.IsLockedByStatement;
4179
lv.IsLockedByStatement = true;
4185
ec.StartFlowBranching (this);
4186
Statement.Resolve (ec);
4187
ec.EndFlowBranching ();
4190
lv.IsLockedByStatement = locked;
4196
// Have to keep original lock value around to unlock same location
4197
// in the case the original has changed or is null
4199
expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock.Parent, loc);
4200
expr_copy.Resolve (ec);
4203
// Ensure Monitor methods are available
4205
if (ResolvePredefinedMethods (ec) > 1) {
4206
lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock.Parent, loc);
4207
lock_taken.Resolve (ec);
4213
protected override void EmitPreTryBody (EmitContext ec)
4215
expr_copy.EmitAssign (ec, expr);
4217
if (lock_taken != null) {
4219
// Initialize ref variable
4221
lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4224
// Monitor.Enter (expr_copy)
4226
expr_copy.Emit (ec);
4227
ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4231
protected override void EmitTryBody (EmitContext ec)
4234
// Monitor.Enter (expr_copy, ref lock_taken)
4236
if (lock_taken != null) {
4237
expr_copy.Emit (ec);
4238
lock_taken.LocalInfo.CreateBuilder (ec);
4239
lock_taken.AddressOf (ec, AddressOp.Load);
4240
ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4243
Statement.Emit (ec);
4246
protected override void EmitFinallyBody (EmitContext ec)
4249
// if (lock_taken) Monitor.Exit (expr_copy)
4251
Label skip = ec.DefineLabel ();
4253
if (lock_taken != null) {
4254
lock_taken.Emit (ec);
4255
ec.Emit (OpCodes.Brfalse_S, skip);
4258
expr_copy.Emit (ec);
4259
var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4261
ec.Emit (OpCodes.Call, m);
4263
ec.MarkLabel (skip);
4266
int ResolvePredefinedMethods (ResolveContext rc)
4268
// Try 4.0 Monitor.Enter (object, ref bool) overload first
4269
var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4273
m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4277
rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4281
protected override void CloneTo (CloneContext clonectx, Statement t)
4283
Lock target = (Lock) t;
4285
target.expr = expr.Clone (clonectx);
4286
target.stmt = Statement.Clone (clonectx);
4289
public override object Accept (StructuralVisitor visitor)
4291
return visitor.Visit (this);
4296
public class Unchecked : Statement {
4299
public Unchecked (Block b, Location loc)
4306
public override bool Resolve (BlockContext ec)
4308
using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4309
return Block.Resolve (ec);
4312
protected override void DoEmit (EmitContext ec)
4314
using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4318
protected override void CloneTo (CloneContext clonectx, Statement t)
4320
Unchecked target = (Unchecked) t;
4322
target.Block = clonectx.LookupBlock (Block);
4325
public override object Accept (StructuralVisitor visitor)
4327
return visitor.Visit (this);
4331
public class Checked : Statement {
4334
public Checked (Block b, Location loc)
4337
b.Unchecked = false;
4341
public override bool Resolve (BlockContext ec)
4343
using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4344
return Block.Resolve (ec);
4347
protected override void DoEmit (EmitContext ec)
4349
using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4353
protected override void CloneTo (CloneContext clonectx, Statement t)
4355
Checked target = (Checked) t;
4357
target.Block = clonectx.LookupBlock (Block);
4360
public override object Accept (StructuralVisitor visitor)
4362
return visitor.Visit (this);
4366
public class Unsafe : Statement {
4369
public Unsafe (Block b, Location loc)
4372
Block.Unsafe = true;
4376
public override bool Resolve (BlockContext ec)
4378
if (ec.CurrentIterator != null)
4379
ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4381
using (ec.Set (ResolveContext.Options.UnsafeScope))
4382
return Block.Resolve (ec);
4385
protected override void DoEmit (EmitContext ec)
4390
protected override void CloneTo (CloneContext clonectx, Statement t)
4392
Unsafe target = (Unsafe) t;
4394
target.Block = clonectx.LookupBlock (Block);
4397
public override object Accept (StructuralVisitor visitor)
4399
return visitor.Visit (this);
4406
public class Fixed : Statement
4408
abstract class Emitter : ShimExpression
4410
protected LocalVariable vi;
4412
protected Emitter (Expression expr, LocalVariable li)
4418
public abstract void EmitExit (EmitContext ec);
4421
class ExpressionEmitter : Emitter {
4422
public ExpressionEmitter (Expression converted, LocalVariable li) :
4423
base (converted, li)
4427
protected override Expression DoResolve (ResolveContext rc)
4429
throw new NotImplementedException ();
4432
public override void Emit (EmitContext ec) {
4434
// Store pointer in pinned location
4440
public override void EmitExit (EmitContext ec)
4442
ec.Emit (OpCodes.Ldc_I4_0);
4443
ec.Emit (OpCodes.Conv_U);
4448
class StringEmitter : Emitter
4450
LocalVariable pinned_string;
4452
public StringEmitter (Expression expr, LocalVariable li, Location loc)
4457
protected override Expression DoResolve (ResolveContext rc)
4459
pinned_string = new LocalVariable (vi.Block, "$pinned",
4460
LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4462
pinned_string.Type = rc.BuiltinTypes.String;
4464
eclass = ExprClass.Variable;
4465
type = rc.BuiltinTypes.Int;
4469
public override void Emit (EmitContext ec)
4471
pinned_string.CreateBuilder (ec);
4474
pinned_string.EmitAssign (ec);
4476
// TODO: Should use Binary::Add
4477
pinned_string.Emit (ec);
4478
ec.Emit (OpCodes.Conv_I);
4480
var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
4484
PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
4485
//pe.InstanceExpression = pinned_string;
4486
pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4488
ec.Emit (OpCodes.Add);
4492
public override void EmitExit (EmitContext ec)
4494
ec.Emit (OpCodes.Ldnull);
4495
pinned_string.EmitAssign (ec);
4500
public class VariableDeclaration : BlockVariableDeclaration
4502
public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4507
protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4509
if (!Variable.Type.IsPointer && li == Variable) {
4510
bc.Report.Error (209, TypeExpression.Location,
4511
"The type of locals declared in a fixed statement must be a pointer type");
4516
// The rules for the possible declarators are pretty wise,
4517
// but the production on the grammar is more concise.
4519
// So we have to enforce these rules here.
4521
// We do not resolve before doing the case 1 test,
4522
// because the grammar is explicit in that the token &
4523
// is present, so we need to test for this particular case.
4526
if (initializer is Cast) {
4527
bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4531
initializer = initializer.Resolve (bc);
4533
if (initializer == null)
4539
if (initializer.Type.IsArray) {
4540
TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4543
// Provided that array_type is unmanaged,
4545
if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
4549
// and T* is implicitly convertible to the
4550
// pointer type given in the fixed statement.
4552
ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4554
Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
4555
if (converted == null)
4559
// fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4561
converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4562
new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4563
new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
4564
new NullLiteral (loc),
4567
converted = converted.Resolve (bc);
4569
return new ExpressionEmitter (converted, li);
4575
if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
4576
return new StringEmitter (initializer, li, loc).Resolve (bc);
4579
// Case 3: fixed buffer
4580
if (initializer is FixedBufferPtr) {
4581
return new ExpressionEmitter (initializer, li);
4585
// Case 4: & object.
4587
bool already_fixed = true;
4588
Unary u = initializer as Unary;
4589
if (u != null && u.Oper == Unary.Operator.AddressOf) {
4590
IVariableReference vr = u.Expr as IVariableReference;
4591
if (vr == null || !vr.IsFixed) {
4592
already_fixed = false;
4596
if (already_fixed) {
4597
bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4600
initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4601
return new ExpressionEmitter (initializer, li);
4606
VariableDeclaration decl;
4607
Statement statement;
4610
public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4619
public Statement Statement {
4625
public BlockVariableDeclaration Variables {
4633
public override bool Resolve (BlockContext ec)
4635
using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4636
if (!decl.Resolve (ec))
4640
ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4641
bool ok = statement.Resolve (ec);
4642
bool flow_unreachable = ec.EndFlowBranching ();
4643
has_ret = flow_unreachable;
4648
protected override void DoEmit (EmitContext ec)
4650
decl.Variable.CreateBuilder (ec);
4651
decl.Initializer.Emit (ec);
4652
if (decl.Declarators != null) {
4653
foreach (var d in decl.Declarators) {
4654
d.Variable.CreateBuilder (ec);
4655
d.Initializer.Emit (ec);
4659
statement.Emit (ec);
4665
// Clear the pinned variable
4667
((Emitter) decl.Initializer).EmitExit (ec);
4668
if (decl.Declarators != null) {
4669
foreach (var d in decl.Declarators) {
4670
((Emitter)d.Initializer).EmitExit (ec);
4675
protected override void CloneTo (CloneContext clonectx, Statement t)
4677
Fixed target = (Fixed) t;
4679
target.decl = (VariableDeclaration) decl.Clone (clonectx);
4680
target.statement = statement.Clone (clonectx);
4683
public override object Accept (StructuralVisitor visitor)
4685
return visitor.Visit (this);
4689
public class Catch : Statement
4693
FullNamedExpression type_expr;
4694
CompilerAssign assign;
4697
public Catch (Block block, Location loc)
4705
public Block Block {
4711
public TypeSpec CatchType {
4717
public bool IsGeneral {
4719
return type_expr == null;
4723
public FullNamedExpression TypeExpression {
4732
public LocalVariable Variable {
4743
protected override void DoEmit (EmitContext ec)
4746
ec.BeginCatchBlock (ec.BuiltinTypes.Object);
4748
ec.BeginCatchBlock (CatchType);
4751
li.CreateBuilder (ec);
4754
// Special case hoisted catch variable, we have to use a temporary variable
4755
// to pass via anonymous storey initialization with the value still on top
4758
if (li.HoistedVariant != null) {
4759
LocalTemporary lt = new LocalTemporary (li.Type);
4760
SymbolWriter.OpenCompilerGeneratedBlock (ec);
4762
SymbolWriter.CloseCompilerGeneratedBlock (ec);
4764
// switch to assigning from the temporary variable and not from top of the stack
4765
assign.UpdateSource (lt);
4768
SymbolWriter.OpenCompilerGeneratedBlock (ec);
4769
ec.Emit (OpCodes.Pop);
4770
SymbolWriter.CloseCompilerGeneratedBlock (ec);
4776
public override bool Resolve (BlockContext ec)
4778
using (ec.With (ResolveContext.Options.CatchScope, true)) {
4779
if (type_expr != null) {
4780
type = type_expr.ResolveAsType (ec);
4784
if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
4785
ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4786
} else if (li != null) {
4788
li.PrepareForFlowAnalysis (ec);
4790
// source variable is at the top of the stack
4791
Expression source = new EmptyExpression (li.Type);
4792
if (li.Type.IsGenericParameter)
4793
source = new UnboxCast (source, li.Type);
4795
assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
4796
Block.AddScopeStatement (new StatementExpression (assign));
4800
return Block.Resolve (ec);
4804
protected override void CloneTo (CloneContext clonectx, Statement t)
4806
Catch target = (Catch) t;
4808
if (type_expr != null)
4809
target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
4811
target.block = clonectx.LookupBlock (block);
4815
public class TryFinally : ExceptionStatement {
4818
public Statement Stmt {
4819
get { return this.stmt; }
4823
get { return this.fini; }
4826
public TryFinally (Statement stmt, Block fini, Location loc)
4832
public override bool Resolve (BlockContext ec)
4836
ec.StartFlowBranching (this);
4838
if (!stmt.Resolve (ec))
4842
ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4843
using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4844
if (!fini.Resolve (ec))
4848
ec.EndFlowBranching ();
4850
ok &= base.Resolve (ec);
4855
protected override void EmitPreTryBody (EmitContext ec)
4859
protected override void EmitTryBody (EmitContext ec)
4864
protected override void EmitFinallyBody (EmitContext ec)
4869
protected override void CloneTo (CloneContext clonectx, Statement t)
4871
TryFinally target = (TryFinally) t;
4873
target.stmt = (Statement) stmt.Clone (clonectx);
4875
target.fini = clonectx.LookupBlock (fini);
4878
public override object Accept (StructuralVisitor visitor)
4880
return visitor.Visit (this);
4884
public class TryCatch : Statement {
4886
public List<Catch> Specific;
4887
public Catch General;
4888
bool inside_try_finally, code_follows;
4890
public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4893
this.Specific = catch_clauses;
4894
this.inside_try_finally = inside_try_finally;
4896
Catch c = catch_clauses [0];
4899
catch_clauses.RemoveAt (0);
4905
public override bool Resolve (BlockContext ec)
4909
ec.StartFlowBranching (this);
4911
if (!Block.Resolve (ec))
4914
TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4916
foreach (Catch c in Specific){
4917
ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4919
if (!c.Resolve (ec)) {
4924
TypeSpec resolved_type = c.CatchType;
4925
for (int ii = 0; ii < last_index; ++ii) {
4926
if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4927
ec.Report.Error (160, c.loc,
4928
"A previous catch clause already catches all exceptions of this or a super type `{0}'",
4929
TypeManager.CSharpName (prev_catches [ii]));
4934
prev_catches [last_index++] = resolved_type;
4937
if (General != null) {
4938
foreach (Catch c in Specific) {
4939
if (c.CatchType.BuiltinType != BuiltinTypeSpec.Type.Exception)
4942
if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
4945
if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
4948
ec.Report.Warning (1058, 1, c.loc,
4949
"A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4952
ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4954
if (!General.Resolve (ec))
4958
ec.EndFlowBranching ();
4960
// System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4961
// So, ensure there's some IL code after this statement
4962
if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4963
ec.NeedReturnLabel ();
4968
public void SomeCodeFollows ()
4970
code_follows = true;
4973
protected override void DoEmit (EmitContext ec)
4975
if (!inside_try_finally)
4976
ec.BeginExceptionBlock ();
4980
foreach (Catch c in Specific)
4983
if (General != null)
4986
if (!inside_try_finally)
4987
ec.EndExceptionBlock ();
4990
protected override void CloneTo (CloneContext clonectx, Statement t)
4992
TryCatch target = (TryCatch) t;
4994
target.Block = clonectx.LookupBlock (Block);
4995
if (General != null)
4996
target.General = (Catch) General.Clone (clonectx);
4997
if (Specific != null){
4998
target.Specific = new List<Catch> ();
4999
foreach (Catch c in Specific)
5000
target.Specific.Add ((Catch) c.Clone (clonectx));
5003
public override object Accept (StructuralVisitor visitor)
5005
return visitor.Visit (this);
5009
public class Using : ExceptionStatement
5011
public class VariableDeclaration : BlockVariableDeclaration
5013
Statement dispose_call;
5015
public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5020
public VariableDeclaration (LocalVariable li, Location loc)
5026
public VariableDeclaration (Expression expr)
5029
loc = expr.Location;
5035
public bool IsNested { get; private set; }
5039
public void EmitDispose (EmitContext ec)
5041
dispose_call.Emit (ec);
5044
public override bool Resolve (BlockContext bc)
5049
return base.Resolve (bc);
5052
public Expression ResolveExpression (BlockContext bc)
5054
var e = Initializer.Resolve (bc);
5058
li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5059
Initializer = ResolveInitializer (bc, Variable, e);
5063
protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5065
if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5066
initializer = initializer.Resolve (bc);
5067
if (initializer == null)
5070
// Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5071
Arguments args = new Arguments (1);
5072
args.Add (new Argument (initializer));
5073
initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5074
if (initializer == null)
5077
var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5078
dispose_call = CreateDisposeCall (bc, var);
5079
dispose_call.Resolve (bc);
5081
return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5084
if (li == Variable) {
5085
CheckIDiposableConversion (bc, li, initializer);
5086
dispose_call = CreateDisposeCall (bc, li);
5087
dispose_call.Resolve (bc);
5090
return base.ResolveInitializer (bc, li, initializer);
5093
protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5097
if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5098
if (type.IsNullableType) {
5099
// it's handled in CreateDisposeCall
5103
bc.Report.SymbolRelatedToPreviousError (type);
5104
var loc = type_expr == null ? initializer.Location : type_expr.Location;
5105
bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5106
type.GetSignatureForError ());
5112
protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5114
var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5116
var loc = lv.Location;
5118
var idt = bc.BuiltinTypes.IDisposable;
5119
var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5121
var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5122
dispose_mg.InstanceExpression = type.IsNullableType ?
5123
new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5126
Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5128
// Add conditional call when disposing possible null variable
5129
if (!type.IsStruct || type.IsNullableType)
5130
dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
5135
public Statement RewriteForDeclarators (BlockContext bc, Statement stmt)
5137
for (int i = declarators.Count - 1; i >= 0; --i) {
5138
var d = declarators [i];
5139
var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5140
vd.Initializer = d.Initializer;
5142
vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5143
vd.dispose_call.Resolve (bc);
5145
stmt = new Using (vd, stmt, d.Variable.Location);
5152
public override object Accept (StructuralVisitor visitor)
5154
return visitor.Visit (this);
5158
VariableDeclaration decl;
5160
public Using (VariableDeclaration decl, Statement stmt, Location loc)
5166
public Using (Expression expr, Statement stmt, Location loc)
5169
this.decl = new VariableDeclaration (expr);
5174
public Expression Expression {
5176
return decl.Variable == null ? decl.Initializer : null;
5180
public BlockVariableDeclaration Variables {
5188
protected override void EmitPreTryBody (EmitContext ec)
5193
protected override void EmitTryBody (EmitContext ec)
5198
protected override void EmitFinallyBody (EmitContext ec)
5200
decl.EmitDispose (ec);
5203
public override bool Resolve (BlockContext ec)
5205
VariableReference vr;
5206
bool vr_locked = false;
5208
using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5209
if (decl.Variable == null) {
5210
vr = decl.ResolveExpression (ec) as VariableReference;
5212
vr_locked = vr.IsLockedByStatement;
5213
vr.IsLockedByStatement = true;
5216
if (!decl.Resolve (ec))
5219
if (decl.Declarators != null) {
5220
stmt = decl.RewriteForDeclarators (ec, stmt);
5227
ec.StartFlowBranching (this);
5231
ec.EndFlowBranching ();
5234
vr.IsLockedByStatement = vr_locked;
5241
protected override void CloneTo (CloneContext clonectx, Statement t)
5243
Using target = (Using) t;
5245
target.decl = (VariableDeclaration) decl.Clone (clonectx);
5246
target.stmt = stmt.Clone (clonectx);
5248
public override object Accept (StructuralVisitor visitor)
5250
return visitor.Visit (this);
5255
/// Implementation of the foreach C# statement
5257
public class Foreach : Statement {
5259
sealed class ArrayForeach : Statement
5261
readonly Foreach for_each;
5262
readonly Statement statement;
5265
TemporaryVariableReference[] lengths;
5266
Expression [] length_exprs;
5267
StatementExpression[] counter;
5268
TemporaryVariableReference[] variables;
5270
TemporaryVariableReference copy;
5272
LocalVariableReference variable;
5274
public ArrayForeach (Foreach @foreach, int rank)
5276
for_each = @foreach;
5277
statement = for_each.statement;
5279
variable = new LocalVariableReference (for_each.variable, loc);
5281
counter = new StatementExpression[rank];
5282
variables = new TemporaryVariableReference[rank];
5283
length_exprs = new Expression [rank];
5286
// Only use temporary length variables when dealing with
5287
// multi-dimensional arrays
5290
lengths = new TemporaryVariableReference [rank];
5293
protected override void CloneTo (CloneContext clonectx, Statement target)
5295
throw new NotImplementedException ();
5298
public override bool Resolve (BlockContext ec)
5300
Block variables_block = variable.local_info.Block;
5301
copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5304
int rank = length_exprs.Length;
5305
Arguments list = new Arguments (rank);
5306
for (int i = 0; i < rank; i++) {
5307
var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5309
counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5310
counter[i].Resolve (ec);
5313
length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5315
lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5316
lengths[i].Resolve (ec);
5318
Arguments args = new Arguments (1);
5319
args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5320
length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5323
list.Add (new Argument (v));
5326
access = new ElementAccess (copy, list, loc).Resolve (ec);
5331
if (for_each.type is VarExpr) {
5332
// Infer implicitly typed local variable from foreach array type
5333
var_type = access.Type;
5335
var_type = for_each.type.ResolveAsType (ec);
5338
if (var_type == null)
5341
conv = Convert.ExplicitConversion (ec, access, var_type, loc);
5347
ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5348
ec.CurrentBranching.CreateSibling ();
5350
variable.local_info.Type = conv.Type;
5351
variable.Resolve (ec);
5353
ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5354
if (!statement.Resolve (ec))
5356
ec.EndFlowBranching ();
5358
// There's no direct control flow from the end of the embedded statement to the end of the loop
5359
ec.CurrentBranching.CurrentUsageVector.Goto ();
5361
ec.EndFlowBranching ();
5366
protected override void DoEmit (EmitContext ec)
5368
copy.EmitAssign (ec, for_each.expr);
5370
int rank = length_exprs.Length;
5371
Label[] test = new Label [rank];
5372
Label[] loop = new Label [rank];
5374
for (int i = 0; i < rank; i++) {
5375
test [i] = ec.DefineLabel ();
5376
loop [i] = ec.DefineLabel ();
5378
if (lengths != null)
5379
lengths [i].EmitAssign (ec, length_exprs [i]);
5382
IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5383
for (int i = 0; i < rank; i++) {
5384
variables [i].EmitAssign (ec, zero);
5386
ec.Emit (OpCodes.Br, test [i]);
5387
ec.MarkLabel (loop [i]);
5390
variable.local_info.CreateBuilder (ec);
5391
variable.EmitAssign (ec, conv, false, false);
5393
statement.Emit (ec);
5395
ec.MarkLabel (ec.LoopBegin);
5397
for (int i = rank - 1; i >= 0; i--){
5398
counter [i].Emit (ec);
5400
ec.MarkLabel (test [i]);
5401
variables [i].Emit (ec);
5403
if (lengths != null)
5404
lengths [i].Emit (ec);
5406
length_exprs [i].Emit (ec);
5408
ec.Emit (OpCodes.Blt, loop [i]);
5411
ec.MarkLabel (ec.LoopEnd);
5415
sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5417
class Body : Statement
5420
LocalVariableReference variable;
5421
Expression current, conv;
5422
Statement statement;
5424
public Body (TypeSpec type, LocalVariable variable,
5425
Expression current, Statement statement,
5429
this.variable = new LocalVariableReference (variable, loc);
5430
this.current = current;
5431
this.statement = statement;
5435
protected override void CloneTo (CloneContext clonectx, Statement target)
5437
throw new NotImplementedException ();
5440
public override bool Resolve (BlockContext ec)
5442
current = current.Resolve (ec);
5443
if (current == null)
5446
conv = Convert.ExplicitConversion (ec, current, type, loc);
5450
variable.local_info.Type = conv.Type;
5451
variable.Resolve (ec);
5453
if (!statement.Resolve (ec))
5459
protected override void DoEmit (EmitContext ec)
5461
variable.local_info.CreateBuilder (ec);
5462
variable.EmitAssign (ec, conv, false, false);
5464
statement.Emit (ec);
5468
class RuntimeDispose : Using.VariableDeclaration
5470
public RuntimeDispose (LocalVariable lv, Location loc)
5475
protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5477
// Defered to runtime check
5480
protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5482
var idt = bc.BuiltinTypes.IDisposable;
5485
// Fabricates code like
5487
// if ((temp = vr as IDisposable) != null) temp.Dispose ();
5490
var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
5492
var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5493
dispose_variable.CreateReferenceExpression (bc, loc),
5494
new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5495
loc), new NullLiteral (loc), loc);
5497
var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5499
var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5500
dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5502
Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5503
return new If (idisaposable_test, dispose, loc);
5507
LocalVariable variable;
5509
Statement statement;
5510
Expression var_type;
5511
ExpressionStatement init;
5512
TemporaryVariableReference enumerator_variable;
5513
bool ambiguous_getenumerator_name;
5515
public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5517
this.var_type = var_type;
5518
this.variable = var;
5524
protected override void CloneTo (CloneContext clonectx, Statement target)
5526
throw new NotImplementedException ();
5529
void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5531
rc.Report.SymbolRelatedToPreviousError (enumerator);
5532
rc.Report.Error (202, loc,
5533
"foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5534
enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5537
MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5540
// Option 1: Try to match by name GetEnumerator first
5542
var mexpr = Expression.MemberLookup (rc, false, expr.Type,
5543
"GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5545
var mg = mexpr as MethodGroupExpr;
5547
mg.InstanceExpression = expr;
5548
Arguments args = new Arguments (0);
5549
mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5551
// For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5552
if (ambiguous_getenumerator_name)
5555
if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5561
// Option 2: Try to match using IEnumerable interfaces with preference of generic version
5564
PredefinedMember<MethodSpec> iface_candidate = null;
5565
var ptypes = rc.Module.PredefinedTypes;
5566
var gen_ienumerable = ptypes.IEnumerableGeneric;
5567
if (!gen_ienumerable.Define ())
5568
gen_ienumerable = null;
5571
var ifaces = t.Interfaces;
5572
if (ifaces != null) {
5573
foreach (var iface in ifaces) {
5574
if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
5575
if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
5576
rc.Report.SymbolRelatedToPreviousError (expr.Type);
5577
rc.Report.Error (1640, loc,
5578
"foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5579
expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
5584
// TODO: Cache this somehow
5585
iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
5586
MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
5591
if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
5592
iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
5597
if (t.IsGenericParameter)
5602
} while (t != null);
5604
if (iface_candidate == null) {
5605
rc.Report.Error (1579, loc,
5606
"foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5607
expr.Type.GetSignatureForError (), "GetEnumerator");
5612
var method = iface_candidate.Resolve (loc);
5616
mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5617
mg.InstanceExpression = expr;
5621
MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5623
var ms = MemberCache.FindMember (enumerator.ReturnType,
5624
MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
5625
BindingRestriction.InstanceOnly) as MethodSpec;
5627
if (ms == null || !ms.IsPublic) {
5628
Error_WrongEnumerator (rc, enumerator);
5632
return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5635
PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5637
var ps = MemberCache.FindMember (enumerator.ReturnType,
5638
MemberFilter.Property ("Current", null),
5639
BindingRestriction.InstanceOnly) as PropertySpec;
5641
if (ps == null || !ps.IsPublic) {
5642
Error_WrongEnumerator (rc, enumerator);
5649
public override bool Resolve (BlockContext ec)
5651
bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
5654
expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
5655
} else if (expr.Type.IsNullableType) {
5656
expr = new Nullable.UnwrapCall (expr).Resolve (ec);
5659
var get_enumerator_mg = ResolveGetEnumerator (ec);
5660
if (get_enumerator_mg == null) {
5664
var get_enumerator = get_enumerator_mg.BestCandidate;
5665
enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
5666
enumerator_variable.Resolve (ec);
5668
// Prepare bool MoveNext ()
5669
var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5670
if (move_next_mg == null) {
5674
move_next_mg.InstanceExpression = enumerator_variable;
5676
// Prepare ~T~ Current { get; }
5677
var current_prop = ResolveCurrent (ec, get_enumerator);
5678
if (current_prop == null) {
5682
var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
5683
if (current_pe == null)
5686
VarExpr ve = var_type as VarExpr;
5690
// Source type is dynamic, set element type to dynamic too
5691
variable.Type = ec.BuiltinTypes.Dynamic;
5693
// Infer implicitly typed local variable from foreach enumerable type
5694
variable.Type = current_pe.Type;
5698
// Explicit cast of dynamic collection elements has to be done at runtime
5699
current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
5702
variable.Type = var_type.ResolveAsType (ec);
5705
if (variable.Type == null)
5708
var init = new Invocation (get_enumerator_mg, null);
5710
statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5711
new Body (variable.Type, variable, current_pe, statement, loc), loc);
5713
var enum_type = enumerator_variable.Type;
5716
// Add Dispose method call when enumerator can be IDisposable
5718
if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
5719
if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
5721
// Runtime Dispose check
5723
var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
5724
vd.Initializer = init;
5725
statement = new Using (vd, statement, loc);
5728
// No Dispose call needed
5730
this.init = new SimpleAssign (enumerator_variable, init);
5731
this.init.Resolve (ec);
5735
// Static Dispose check
5737
var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
5738
vd.Initializer = init;
5739
statement = new Using (vd, statement, loc);
5742
return statement.Resolve (ec);
5745
protected override void DoEmit (EmitContext ec)
5747
enumerator_variable.LocalInfo.CreateBuilder (ec);
5750
init.EmitStatement (ec);
5752
statement.Emit (ec);
5755
#region IErrorHandler Members
5757
bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5759
ec.Report.SymbolRelatedToPreviousError (best);
5760
ec.Report.Warning (278, 2, loc,
5761
"`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5762
expr.Type.GetSignatureForError (), "enumerable",
5763
best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5765
ambiguous_getenumerator_name = true;
5769
bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5774
bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5779
bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5788
LocalVariable variable;
5790
Statement statement;
5792
public Expression TypeExpr {
5793
get { return this.type; }
5796
public LocalVariable Variable {
5797
get { return this.variable; }
5800
public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
5803
this.variable = var;
5809
public Statement Statement {
5810
get { return statement; }
5813
public Expression Expr {
5814
get { return this.expr; }
5817
public override bool Resolve (BlockContext ec)
5819
expr = expr.Resolve (ec);
5824
ec.Report.Error (186, loc, "Use of null is not valid in this context");
5828
if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5829
statement = new ArrayForeach (this, 1);
5830
} else if (expr.Type is ArrayContainer) {
5831
statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5833
if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5834
ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5835
expr.ExprClassName);
5839
statement = new CollectionForeach (type, variable, expr, statement, loc);
5842
return statement.Resolve (ec);
5845
protected override void DoEmit (EmitContext ec)
5847
Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5848
ec.LoopBegin = ec.DefineLabel ();
5849
ec.LoopEnd = ec.DefineLabel ();
5851
statement.Emit (ec);
5853
ec.LoopBegin = old_begin;
5854
ec.LoopEnd = old_end;
5857
protected override void CloneTo (CloneContext clonectx, Statement t)
5859
Foreach target = (Foreach) t;
5861
target.type = type.Clone (clonectx);
5862
target.expr = expr.Clone (clonectx);
5863
target.statement = statement.Clone (clonectx);
5866
public override object Accept (StructuralVisitor visitor)
5868
return visitor.Visit (this);