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.
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Diagnostics;
18 using System.Collections.Generic;
20 namespace Mono.CSharp {
22 public abstract class Statement {
26 /// Resolves the statement, true means that all sub-statements
29 public virtual bool Resolve (BlockContext ec)
35 /// We already know that the statement is unreachable, but we still
36 /// need to resolve it to catch errors.
38 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
41 // This conflicts with csc's way of doing this, but IMHO it's
42 // the right thing to do.
44 // If something is unreachable, we still check whether it's
45 // correct. This means that you cannot use unassigned variables
46 // in unreachable code, for instance.
50 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
52 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
53 bool ok = Resolve (ec);
54 ec.KillFlowBranching ();
60 /// Return value indicates whether all code paths emitted return.
62 protected abstract void DoEmit (EmitContext ec);
64 public virtual void Emit (EmitContext ec)
71 // This routine must be overrided in derived classes and make copies
72 // of all the data that might be modified if resolved
74 protected abstract void CloneTo (CloneContext clonectx, Statement target);
76 public Statement Clone (CloneContext clonectx)
78 Statement s = (Statement) this.MemberwiseClone ();
79 CloneTo (clonectx, s);
83 public virtual Expression CreateExpressionTree (ResolveContext ec)
85 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
89 public Statement PerformClone ()
91 CloneContext clonectx = new CloneContext ();
93 return Clone (clonectx);
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)
129 public class If : Statement {
131 public Statement TrueStatement;
132 public Statement FalseStatement;
136 public If (Expression bool_expr, Statement true_statement, Location l)
137 : this (bool_expr, true_statement, null, l)
141 public If (Expression bool_expr,
142 Statement true_statement,
143 Statement false_statement,
146 this.expr = bool_expr;
147 TrueStatement = true_statement;
148 FalseStatement = false_statement;
152 public override bool Resolve (BlockContext ec)
156 Report.Debug (1, "START IF BLOCK", loc);
158 expr = expr.Resolve (ec);
163 // Dead code elimination
165 if (expr is Constant) {
166 bool take = !((Constant) expr).IsDefaultValue;
169 if (!TrueStatement.Resolve (ec))
172 if ((FalseStatement != null) &&
173 !FalseStatement.ResolveUnreachable (ec, true))
175 FalseStatement = null;
177 if (!TrueStatement.ResolveUnreachable (ec, true))
179 TrueStatement = null;
181 if ((FalseStatement != null) &&
182 !FalseStatement.Resolve (ec))
190 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
192 ok &= TrueStatement.Resolve (ec);
194 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
196 ec.CurrentBranching.CreateSibling ();
198 if (FalseStatement != null)
199 ok &= FalseStatement.Resolve (ec);
201 ec.EndFlowBranching ();
203 Report.Debug (1, "END IF BLOCK", loc);
208 protected override void DoEmit (EmitContext ec)
210 Label false_target = ec.DefineLabel ();
214 // If we're a boolean constant, Resolve() already
215 // eliminated dead code for us.
217 Constant c = expr as Constant;
219 c.EmitSideEffect (ec);
221 if (!c.IsDefaultValue)
222 TrueStatement.Emit (ec);
223 else if (FalseStatement != null)
224 FalseStatement.Emit (ec);
229 expr.EmitBranchable (ec, false_target, false);
231 TrueStatement.Emit (ec);
233 if (FalseStatement != null){
234 bool branch_emitted = false;
236 end = ec.DefineLabel ();
238 ec.Emit (OpCodes.Br, end);
239 branch_emitted = true;
242 ec.MarkLabel (false_target);
243 FalseStatement.Emit (ec);
248 ec.MarkLabel (false_target);
252 protected override void CloneTo (CloneContext clonectx, Statement t)
256 target.expr = expr.Clone (clonectx);
257 target.TrueStatement = TrueStatement.Clone (clonectx);
258 if (FalseStatement != null)
259 target.FalseStatement = FalseStatement.Clone (clonectx);
263 public class Do : Statement {
264 public Expression expr;
265 public Statement EmbeddedStatement;
267 public Do (Statement statement, BooleanExpression bool_expr, Location l)
270 EmbeddedStatement = statement;
274 public override bool Resolve (BlockContext ec)
278 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
280 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
282 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
283 if (!EmbeddedStatement.Resolve (ec))
285 ec.EndFlowBranching ();
287 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
288 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
290 expr = expr.Resolve (ec);
293 else if (expr is Constant){
294 bool infinite = !((Constant) expr).IsDefaultValue;
296 ec.CurrentBranching.CurrentUsageVector.Goto ();
299 ec.EndFlowBranching ();
304 protected override void DoEmit (EmitContext ec)
306 Label loop = ec.DefineLabel ();
307 Label old_begin = ec.LoopBegin;
308 Label old_end = ec.LoopEnd;
310 ec.LoopBegin = ec.DefineLabel ();
311 ec.LoopEnd = ec.DefineLabel ();
314 EmbeddedStatement.Emit (ec);
315 ec.MarkLabel (ec.LoopBegin);
318 // Dead code elimination
320 if (expr is Constant){
321 bool res = !((Constant) expr).IsDefaultValue;
323 expr.EmitSideEffect (ec);
325 ec.Emit (OpCodes.Br, loop);
327 expr.EmitBranchable (ec, loop, true);
329 ec.MarkLabel (ec.LoopEnd);
331 ec.LoopBegin = old_begin;
332 ec.LoopEnd = old_end;
335 protected override void CloneTo (CloneContext clonectx, Statement t)
339 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
340 target.expr = expr.Clone (clonectx);
344 public class While : Statement {
345 public Expression expr;
346 public Statement Statement;
347 bool infinite, empty;
349 public While (BooleanExpression bool_expr, Statement statement, Location l)
351 this.expr = bool_expr;
352 Statement = statement;
356 public override bool Resolve (BlockContext ec)
360 expr = expr.Resolve (ec);
365 // Inform whether we are infinite or not
367 if (expr is Constant){
368 bool value = !((Constant) expr).IsDefaultValue;
371 if (!Statement.ResolveUnreachable (ec, true))
379 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
381 ec.CurrentBranching.CreateSibling ();
383 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
384 if (!Statement.Resolve (ec))
386 ec.EndFlowBranching ();
388 // There's no direct control flow from the end of the embedded statement to the end of the loop
389 ec.CurrentBranching.CurrentUsageVector.Goto ();
391 ec.EndFlowBranching ();
396 protected override void DoEmit (EmitContext ec)
399 expr.EmitSideEffect (ec);
403 Label old_begin = ec.LoopBegin;
404 Label old_end = ec.LoopEnd;
406 ec.LoopBegin = ec.DefineLabel ();
407 ec.LoopEnd = ec.DefineLabel ();
410 // Inform whether we are infinite or not
412 if (expr is Constant){
413 // expr is 'true', since the 'empty' case above handles the 'false' case
414 ec.MarkLabel (ec.LoopBegin);
415 expr.EmitSideEffect (ec);
417 ec.Emit (OpCodes.Br, ec.LoopBegin);
420 // Inform that we are infinite (ie, `we return'), only
421 // if we do not `break' inside the code.
423 ec.MarkLabel (ec.LoopEnd);
425 Label while_loop = ec.DefineLabel ();
427 ec.Emit (OpCodes.Br, ec.LoopBegin);
428 ec.MarkLabel (while_loop);
432 ec.MarkLabel (ec.LoopBegin);
435 expr.EmitBranchable (ec, while_loop, true);
437 ec.MarkLabel (ec.LoopEnd);
440 ec.LoopBegin = old_begin;
441 ec.LoopEnd = old_end;
444 public override void Emit (EmitContext ec)
449 protected override void CloneTo (CloneContext clonectx, Statement t)
451 While target = (While) t;
453 target.expr = expr.Clone (clonectx);
454 target.Statement = Statement.Clone (clonectx);
458 public class For : Statement {
460 Statement InitStatement;
462 public Statement Statement;
463 bool infinite, empty;
465 public For (Statement init_statement,
466 BooleanExpression test,
471 InitStatement = init_statement;
473 Increment = increment;
474 Statement = statement;
478 public override bool Resolve (BlockContext ec)
482 if (InitStatement != null){
483 if (!InitStatement.Resolve (ec))
488 Test = Test.Resolve (ec);
491 else if (Test is Constant){
492 bool value = !((Constant) Test).IsDefaultValue;
495 if (!Statement.ResolveUnreachable (ec, true))
497 if ((Increment != null) &&
498 !Increment.ResolveUnreachable (ec, false))
508 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
510 ec.CurrentBranching.CreateSibling ();
512 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
514 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
515 if (!Statement.Resolve (ec))
517 ec.EndFlowBranching ();
519 if (Increment != null){
520 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
521 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
524 if (!Increment.Resolve (ec))
529 // There's no direct control flow from the end of the embedded statement to the end of the loop
530 ec.CurrentBranching.CurrentUsageVector.Goto ();
532 ec.EndFlowBranching ();
537 protected override void DoEmit (EmitContext ec)
539 if (InitStatement != null)
540 InitStatement.Emit (ec);
543 Test.EmitSideEffect (ec);
547 Label old_begin = ec.LoopBegin;
548 Label old_end = ec.LoopEnd;
549 Label loop = ec.DefineLabel ();
550 Label test = ec.DefineLabel ();
552 ec.LoopBegin = ec.DefineLabel ();
553 ec.LoopEnd = ec.DefineLabel ();
555 ec.Emit (OpCodes.Br, test);
559 ec.MarkLabel (ec.LoopBegin);
564 // If test is null, there is no test, and we are just
569 // The Resolve code already catches the case for
570 // Test == Constant (false) so we know that
573 if (Test is Constant) {
574 Test.EmitSideEffect (ec);
575 ec.Emit (OpCodes.Br, loop);
577 Test.EmitBranchable (ec, loop, true);
581 ec.Emit (OpCodes.Br, loop);
582 ec.MarkLabel (ec.LoopEnd);
584 ec.LoopBegin = old_begin;
585 ec.LoopEnd = old_end;
588 protected override void CloneTo (CloneContext clonectx, Statement t)
590 For target = (For) t;
592 if (InitStatement != null)
593 target.InitStatement = InitStatement.Clone (clonectx);
595 target.Test = Test.Clone (clonectx);
596 if (Increment != null)
597 target.Increment = Increment.Clone (clonectx);
598 target.Statement = Statement.Clone (clonectx);
602 public class StatementExpression : Statement {
603 ExpressionStatement expr;
605 public StatementExpression (ExpressionStatement expr)
611 public override bool Resolve (BlockContext ec)
613 expr = expr.ResolveStatement (ec);
617 protected override void DoEmit (EmitContext ec)
619 expr.EmitStatement (ec);
622 public override string ToString ()
624 return "StatementExpression (" + expr + ")";
627 protected override void CloneTo (CloneContext clonectx, Statement t)
629 StatementExpression target = (StatementExpression) t;
631 target.expr = (ExpressionStatement) expr.Clone (clonectx);
636 // Simple version of statement list not requiring a block
638 public class StatementList : Statement
640 List<Statement> statements;
642 public StatementList (Statement first, Statement second)
644 statements = new List<Statement> () { first, second };
648 public IList<Statement> Statements {
655 public void Add (Statement statement)
657 statements.Add (statement);
660 public override bool Resolve (BlockContext ec)
662 foreach (var s in statements)
668 protected override void DoEmit (EmitContext ec)
670 foreach (var s in statements)
674 protected override void CloneTo (CloneContext clonectx, Statement target)
676 StatementList t = (StatementList) target;
678 t.statements = new List<Statement> (statements.Count);
679 foreach (Statement s in statements)
680 t.statements.Add (s.Clone (clonectx));
684 // A 'return' or a 'yield break'
685 public abstract class ExitStatement : Statement
687 protected bool unwind_protect;
688 protected abstract bool DoResolve (BlockContext ec);
690 public virtual void Error_FinallyClause (Report Report)
692 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
695 public sealed override bool Resolve (BlockContext ec)
700 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
702 ec.NeedReturnLabel ();
703 ec.CurrentBranching.CurrentUsageVector.Goto ();
709 /// Implements the return statement
711 public class Return : ExitStatement
713 protected Expression Expr;
714 public Return (Expression expr, Location l)
721 public Expression Expression {
728 protected override bool DoResolve (BlockContext ec)
731 if (ec.ReturnType == TypeManager.void_type)
734 if (ec.CurrentIterator != null) {
735 Error_ReturnFromIterator (ec);
737 ec.Report.Error (126, loc,
738 "An object of a type convertible to `{0}' is required for the return statement",
739 ec.ReturnType.GetSignatureForError ());
745 Expr = Expr.Resolve (ec);
747 AnonymousExpression am = ec.CurrentAnonymousMethod;
749 if (ec.ReturnType == TypeManager.void_type) {
750 ec.Report.Error (127, loc,
751 "`{0}': A return keyword must not be followed by any expression when method returns void",
752 ec.GetSignatureForError ());
756 Error_ReturnFromIterator (ec);
760 var l = am as AnonymousMethodBody;
761 if (l != null && l.ReturnTypeInference != null && Expr != null) {
762 l.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
770 if (Expr.Type != ec.ReturnType) {
771 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
775 ec.Report.Error (1662, loc,
776 "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",
777 am.ContainerType, am.GetSignatureForError ());
786 protected override void DoEmit (EmitContext ec)
792 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
796 ec.Emit (OpCodes.Leave, ec.ReturnLabel);
798 ec.Emit (OpCodes.Ret);
801 void Error_ReturnFromIterator (ResolveContext rc)
803 rc.Report.Error (1622, loc,
804 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
807 protected override void CloneTo (CloneContext clonectx, Statement t)
809 Return target = (Return) t;
810 // It's null for simple return;
812 target.Expr = Expr.Clone (clonectx);
816 public class Goto : Statement {
818 LabeledStatement label;
821 public override bool Resolve (BlockContext ec)
823 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
824 ec.CurrentBranching.CurrentUsageVector.Goto ();
828 public Goto (string label, Location l)
834 public string Target {
835 get { return target; }
838 public void SetResolvedTarget (LabeledStatement label)
841 label.AddReference ();
844 protected override void CloneTo (CloneContext clonectx, Statement target)
849 protected override void DoEmit (EmitContext ec)
852 throw new InternalErrorException ("goto emitted before target resolved");
853 Label l = label.LabelTarget (ec);
854 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
858 public class LabeledStatement : Statement {
865 FlowBranching.UsageVector vectors;
867 public LabeledStatement (string name, Block block, Location l)
874 public Label LabelTarget (EmitContext ec)
879 label = ec.DefineLabel ();
894 public bool IsDefined {
895 get { return defined; }
898 public bool HasBeenReferenced {
899 get { return referenced; }
902 public FlowBranching.UsageVector JumpOrigins {
903 get { return vectors; }
906 public void AddUsageVector (FlowBranching.UsageVector vector)
908 vector = vector.Clone ();
909 vector.Next = vectors;
913 protected override void CloneTo (CloneContext clonectx, Statement target)
918 public override bool Resolve (BlockContext ec)
920 // this flow-branching will be terminated when the surrounding block ends
921 ec.StartFlowBranching (this);
925 protected override void DoEmit (EmitContext ec)
927 if (!HasBeenReferenced)
928 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
931 ec.MarkLabel (label);
934 public void AddReference ()
942 /// `goto default' statement
944 public class GotoDefault : Statement {
946 public GotoDefault (Location l)
951 protected override void CloneTo (CloneContext clonectx, Statement target)
956 public override bool Resolve (BlockContext ec)
958 ec.CurrentBranching.CurrentUsageVector.Goto ();
960 if (ec.Switch == null) {
961 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
965 if (!ec.Switch.GotDefault) {
966 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
973 protected override void DoEmit (EmitContext ec)
975 ec.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
980 /// `goto case' statement
982 public class GotoCase : Statement {
986 public GotoCase (Expression e, Location l)
992 public override bool Resolve (BlockContext ec)
994 if (ec.Switch == null){
995 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
999 ec.CurrentBranching.CurrentUsageVector.Goto ();
1001 expr = expr.Resolve (ec);
1005 Constant c = expr as Constant;
1007 ec.Report.Error (150, expr.Location, "A constant value is expected");
1011 TypeSpec type = ec.Switch.SwitchType;
1012 Constant res = c.TryReduce (ec, type, c.Location);
1014 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1018 if (!Convert.ImplicitStandardConversionExists (c, type))
1019 ec.Report.Warning (469, 2, loc,
1020 "The `goto case' value is not implicitly convertible to type `{0}'",
1021 TypeManager.CSharpName (type));
1023 object val = res.GetValue ();
1025 val = SwitchLabel.NullStringCase;
1027 if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
1028 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1029 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1036 protected override void DoEmit (EmitContext ec)
1038 ec.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1041 protected override void CloneTo (CloneContext clonectx, Statement t)
1043 GotoCase target = (GotoCase) t;
1045 target.expr = expr.Clone (clonectx);
1049 public class Throw : Statement {
1052 public Throw (Expression expr, Location l)
1058 public override bool Resolve (BlockContext ec)
1061 ec.CurrentBranching.CurrentUsageVector.Goto ();
1062 return ec.CurrentBranching.CheckRethrow (loc);
1065 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1066 ec.CurrentBranching.CurrentUsageVector.Goto ();
1071 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1072 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1074 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1079 protected override void DoEmit (EmitContext ec)
1082 ec.Emit (OpCodes.Rethrow);
1086 ec.Emit (OpCodes.Throw);
1090 protected override void CloneTo (CloneContext clonectx, Statement t)
1092 Throw target = (Throw) t;
1095 target.expr = expr.Clone (clonectx);
1099 public class Break : Statement {
1101 public Break (Location l)
1106 bool unwind_protect;
1108 public override bool Resolve (BlockContext ec)
1110 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1111 ec.CurrentBranching.CurrentUsageVector.Goto ();
1115 protected override void DoEmit (EmitContext ec)
1117 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1120 protected override void CloneTo (CloneContext clonectx, Statement t)
1126 public class Continue : Statement {
1128 public Continue (Location l)
1133 bool unwind_protect;
1135 public override bool Resolve (BlockContext ec)
1137 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1138 ec.CurrentBranching.CurrentUsageVector.Goto ();
1142 protected override void DoEmit (EmitContext ec)
1144 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1147 protected override void CloneTo (CloneContext clonectx, Statement t)
1153 public interface ILocalVariable
1155 void Emit (EmitContext ec);
1156 void EmitAssign (EmitContext ec);
1157 void EmitAddressOf (EmitContext ec);
1160 public interface INamedBlockVariable
1162 Block Block { get; }
1163 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1164 bool IsDeclared { get; }
1165 Location Location { get; }
1168 public class BlockVariableDeclaration : Statement
1170 public class Declarator
1173 Expression initializer;
1175 public Declarator (LocalVariable li, Expression initializer)
1177 if (li.Type != null)
1178 throw new ArgumentException ("Expected null variable type");
1181 this.initializer = initializer;
1184 public Declarator (Declarator clone, Expression initializer)
1187 this.initializer = initializer;
1192 public LocalVariable Variable {
1198 public Expression Initializer {
1203 initializer = value;
1210 Expression initializer;
1211 protected FullNamedExpression type_expr;
1212 protected LocalVariable li;
1213 protected List<Declarator> declarators;
1215 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1217 this.type_expr = type;
1219 this.loc = type_expr.Location;
1222 protected BlockVariableDeclaration (LocalVariable li)
1229 public List<Declarator> Declarators {
1235 public Expression Initializer {
1240 initializer = value;
1244 public FullNamedExpression TypeExpression {
1250 public LocalVariable Variable {
1258 public void AddDeclarator (Declarator decl)
1260 if (declarators == null)
1261 declarators = new List<Declarator> ();
1263 declarators.Add (decl);
1266 void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1268 var container = bc.CurrentMemberDefinition.Parent;
1270 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1271 new MemberName (li.Name, li.Location), null);
1273 container.AddField (f);
1275 Evaluator.QueueField (f);
1277 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1281 public override bool Resolve (BlockContext bc)
1283 if (li.Type == null) {
1284 TypeSpec type = null;
1285 if (type_expr is VarExpr) {
1287 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1288 // same name exists or as a keyword when no type was found
1290 var texpr = type_expr.ResolveAsTypeTerminal (bc, true);
1291 if (texpr == null) {
1292 if (RootContext.Version < LanguageVersion.V_3)
1293 bc.Report.FeatureIsNotAvailable (loc, "implicitly typed local variable");
1296 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1300 if (li.IsConstant) {
1301 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1305 if (Initializer == null) {
1306 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1310 if (declarators != null) {
1311 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1315 Initializer = Initializer.Resolve (bc);
1316 if (Initializer != null) {
1317 ((VarExpr) type_expr).InferType (bc, Initializer);
1318 type = type_expr.Type;
1324 var texpr = type_expr.ResolveAsTypeTerminal (bc, false);
1330 if (li.IsConstant && !type.IsConstantCompatible) {
1331 Const.Error_InvalidConstantType (type, loc, bc.Report);
1336 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1338 if (type.IsPointer && !bc.IsUnsafe)
1339 Expression.UnsafeError (bc, loc);
1344 bool eval_global = RootContext.StatementMode && bc.CurrentBlock is ToplevelBlock;
1346 CreateEvaluatorVariable (bc, li);
1348 li.PrepareForFlowAnalysis (bc);
1351 if (initializer != null) {
1352 initializer = ResolveInitializer (bc, li, initializer);
1353 // li.Variable.DefinitelyAssigned
1356 if (declarators != null) {
1357 foreach (var d in declarators) {
1358 d.Variable.Type = li.Type;
1360 CreateEvaluatorVariable (bc, d.Variable);
1362 d.Variable.PrepareForFlowAnalysis (bc);
1365 if (d.Initializer != null) {
1366 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1367 // d.Variable.DefinitelyAssigned
1375 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1377 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1378 return a.ResolveStatement (bc);
1381 protected override void DoEmit (EmitContext ec)
1386 li.CreateBuilder (ec);
1388 if (Initializer != null)
1389 ((ExpressionStatement) Initializer).EmitStatement (ec);
1391 if (declarators != null) {
1392 foreach (var d in declarators) {
1393 d.Variable.CreateBuilder (ec);
1394 if (d.Initializer != null)
1395 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1400 protected override void CloneTo (CloneContext clonectx, Statement target)
1402 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1404 if (type_expr != null)
1405 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1407 if (initializer != null)
1408 t.initializer = initializer.Clone (clonectx);
1410 if (declarators != null) {
1411 t.declarators = null;
1412 foreach (var d in declarators)
1413 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1418 class BlockConstantDeclaration : BlockVariableDeclaration
1420 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1425 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1427 initializer = initializer.Resolve (bc);
1428 if (initializer == null)
1431 var c = initializer as Constant;
1433 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1437 c = c.ConvertImplicitly (bc, li.Type);
1439 if (TypeManager.IsReferenceType (li.Type))
1440 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1442 initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
1447 li.ConstantValue = c;
1453 // The information about a user-perceived local variable
1455 public class LocalVariable : INamedBlockVariable, ILocalVariable
1462 AddressTaken = 1 << 2,
1463 CompilerGenerated = 1 << 3,
1465 ForeachVariable = 1 << 5,
1466 FixedVariable = 1 << 6,
1467 UsingVariable = 1 << 7,
1468 // DefinitelyAssigned = 1 << 8,
1471 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1475 readonly string name;
1476 readonly Location loc;
1477 readonly Block block;
1479 Constant const_value;
1481 public VariableInfo VariableInfo;
1482 HoistedVariable hoisted_variant;
1484 LocalBuilder builder;
1486 public LocalVariable (Block block, string name, Location loc)
1493 public LocalVariable (Block block, string name, Flags flags, Location loc)
1494 : this (block, name, loc)
1500 // Used by variable declarators
1502 public LocalVariable (LocalVariable li, string name, Location loc)
1503 : this (li.block, name, li.flags, loc)
1509 public bool AddressTaken {
1510 get { return (flags & Flags.AddressTaken) != 0; }
1511 set { flags |= Flags.AddressTaken; }
1514 public Block Block {
1520 public Constant ConstantValue {
1525 const_value = value;
1530 // Hoisted local variable variant
1532 public HoistedVariable HoistedVariant {
1534 return hoisted_variant;
1537 hoisted_variant = value;
1541 public bool IsDeclared {
1543 return type != null;
1547 public bool IsConstant {
1549 return (flags & Flags.Constant) != 0;
1553 public bool IsLocked {
1555 return (flags & Flags.IsLocked) != 0;
1558 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1562 public bool IsThis {
1564 return (flags & Flags.IsThis) != 0;
1568 public bool IsFixed {
1570 return (flags & Flags.FixedVariable) != 0;
1574 public bool IsReadonly {
1576 return (flags & Flags.ReadonlyMask) != 0;
1580 public Location Location {
1586 public string Name {
1592 public TypeSpec Type {
1603 public void CreateBuilder (EmitContext ec)
1605 if ((flags & Flags.Used) == 0) {
1606 if (VariableInfo == null) {
1607 // Missing flow analysis or wrong variable flags
1608 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1611 if (VariableInfo.IsEverAssigned)
1612 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1614 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1617 if (HoistedVariant != null)
1620 if (builder != null) {
1621 if ((flags & Flags.CompilerGenerated) != 0)
1624 // To avoid Used warning duplicates
1625 throw new InternalErrorException ("Already created variable `{0}'", name);
1629 // All fixed variabled are pinned, a slot has to be alocated
1631 builder = ec.DeclareLocal (Type, IsFixed);
1632 if (SymbolWriter.HasSymbolWriter)
1633 ec.DefineLocalVariable (name, builder);
1636 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1638 LocalVariable li = new LocalVariable (block, "<$$>", Flags.CompilerGenerated | Flags.Used, loc);
1643 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1645 if (IsConstant && const_value != null)
1646 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc).Resolve (rc);
1648 return new LocalVariableReference (this, loc);
1651 public void Emit (EmitContext ec)
1653 // TODO: Need something better for temporary variables
1654 if ((flags & Flags.CompilerGenerated) != 0)
1657 ec.Emit (OpCodes.Ldloc, builder);
1660 public void EmitAssign (EmitContext ec)
1662 // TODO: Need something better for temporary variables
1663 if ((flags & Flags.CompilerGenerated) != 0)
1666 ec.Emit (OpCodes.Stloc, builder);
1669 public void EmitAddressOf (EmitContext ec)
1671 ec.Emit (OpCodes.Ldloca, builder);
1674 public string GetReadOnlyContext ()
1676 switch (flags & Flags.ReadonlyMask) {
1677 case Flags.FixedVariable:
1678 return "fixed variable";
1679 case Flags.ForeachVariable:
1680 return "foreach iteration variable";
1681 case Flags.UsingVariable:
1682 return "using variable";
1685 throw new InternalErrorException ("Variable is not readonly");
1688 public bool IsThisAssigned (BlockContext ec, Block block)
1690 if (VariableInfo == null)
1691 throw new Exception ();
1693 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1696 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1699 public bool IsAssigned (BlockContext ec)
1701 if (VariableInfo == null)
1702 throw new Exception ();
1704 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1707 public void PrepareForFlowAnalysis (BlockContext bc)
1710 // No need for definitely assigned check for these guys
1712 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1715 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1716 bc.FlowOffset += VariableInfo.Length;
1720 // Mark the variables as referenced in the user code
1722 public void SetIsUsed ()
1724 flags |= Flags.Used;
1727 public override string ToString ()
1729 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
1734 /// Block represents a C# block.
1738 /// This class is used in a number of places: either to represent
1739 /// explicit blocks that the programmer places or implicit blocks.
1741 /// Implicit blocks are used as labels or to introduce variable
1744 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1745 /// they contain extra information that is not necessary on normal blocks.
1747 public class Block : Statement {
1755 HasCapturedVariable = 64,
1756 HasCapturedThis = 1 << 7,
1757 IsExpressionTree = 1 << 8,
1758 CompilerGenerated = 1 << 9
1761 public Block Parent;
1762 public Location StartLocation;
1763 public Location EndLocation;
1765 public ExplicitBlock Explicit;
1766 public ParametersBlock ParametersBlock;
1768 protected Flags flags;
1771 // The statements in this block
1773 protected List<Statement> statements;
1775 protected List<Statement> scope_initializers;
1777 int? resolving_init_idx;
1783 public int ID = id++;
1785 static int clone_id_counter;
1789 // int assignable_slots;
1790 bool unreachable_shown;
1793 public Block (Block parent, Location start, Location end)
1794 : this (parent, 0, start, end)
1798 public Block (Block parent, Flags flags, Location start, Location end)
1800 if (parent != null) {
1801 // the appropriate constructors will fixup these fields
1802 ParametersBlock = parent.ParametersBlock;
1803 Explicit = parent.Explicit;
1806 this.Parent = parent;
1808 this.StartLocation = start;
1809 this.EndLocation = end;
1811 statements = new List<Statement> (4);
1813 this.original = this;
1818 public bool HasRet {
1819 get { return (flags & Flags.HasRet) != 0; }
1822 public Block Original {
1828 public bool IsCompilerGenerated {
1829 get { return (flags & Flags.CompilerGenerated) != 0; }
1830 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
1833 public bool Unchecked {
1834 get { return (flags & Flags.Unchecked) != 0; }
1835 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1838 public bool Unsafe {
1839 get { return (flags & Flags.Unsafe) != 0; }
1840 set { flags |= Flags.Unsafe; }
1845 public Block CreateSwitchBlock (Location start)
1847 // FIXME: Only explicit block should be created
1848 var new_block = new Block (this, start, start);
1849 new_block.IsCompilerGenerated = true;
1853 public void SetEndLocation (Location loc)
1858 public void AddLabel (LabeledStatement target)
1860 ParametersBlock.TopBlock.AddLabel (target.Name, target);
1863 public void AddLocalName (LocalVariable li)
1865 AddLocalName (li.Name, li);
1868 public virtual void AddLocalName (string name, INamedBlockVariable li)
1870 ParametersBlock.TopBlock.AddLocalName (name, li);
1873 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
1875 if (reason == null) {
1876 Error_AlreadyDeclared (name, variable);
1880 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
1881 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
1882 "to `{0}', which is already used in a `{1}' scope to denote something else",
1886 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
1888 var pi = variable as ParametersBlock.ParameterInfo;
1890 var p = pi.Parameter;
1891 if (p is AnonymousTypeClass.GeneratedParameter) {
1892 ParametersBlock.TopBlock.Report.Error (833, p.Location, "`{0}': An anonymous type cannot have multiple properties with the same name",
1895 ParametersBlock.TopBlock.Report.Error (100, p.Location, "The parameter name `{0}' is a duplicate", p.Name);
1901 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
1902 "A local variable named `{0}' is already defined in this scope", name);
1905 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
1907 ParametersBlock.TopBlock.Report.Error (412, loc,
1908 "The type parameter name `{0}' is the same as local variable or parameter name",
1913 // It should be used by expressions which require to
1914 // register a statement during resolve process.
1916 public void AddScopeStatement (Statement s)
1918 if (scope_initializers == null)
1919 scope_initializers = new List<Statement> ();
1922 // Simple recursive helper, when resolve scope initializer another
1923 // new scope initializer can be added, this ensures it's initialized
1924 // before existing one. For now this can happen with expression trees
1925 // in base ctor initializer only
1927 if (resolving_init_idx.HasValue) {
1928 scope_initializers.Insert (resolving_init_idx.Value, s);
1929 ++resolving_init_idx;
1931 scope_initializers.Add (s);
1935 public void AddStatement (Statement s)
1940 public int AssignableSlots {
1942 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
1944 // return assignable_slots;
1948 public LabeledStatement LookupLabel (string name)
1950 return ParametersBlock.TopBlock.GetLabel (name, this);
1953 public override bool Resolve (BlockContext ec)
1955 Block prev_block = ec.CurrentBlock;
1958 ec.CurrentBlock = this;
1959 ec.StartFlowBranching (this);
1961 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
1964 // Compiler generated scope statements
1966 if (scope_initializers != null) {
1967 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
1968 scope_initializers[resolving_init_idx.Value].Resolve (ec);
1971 resolving_init_idx = null;
1975 // This flag is used to notate nested statements as unreachable from the beginning of this block.
1976 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
1977 // from the beginning of the function. The outer Resolve() that detected the unreachability is
1978 // responsible for handling the situation.
1980 int statement_count = statements.Count;
1981 for (int ix = 0; ix < statement_count; ix++){
1982 Statement s = statements [ix];
1985 // Warn if we detect unreachable code.
1988 if (s is EmptyStatement)
1991 if (!unreachable_shown && !(s is LabeledStatement)) {
1992 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
1993 unreachable_shown = true;
1996 Block c_block = s as Block;
1997 if (c_block != null)
1998 c_block.unreachable = c_block.unreachable_shown = true;
2002 // Note that we're not using ResolveUnreachable() for unreachable
2003 // statements here. ResolveUnreachable() creates a temporary
2004 // flow branching and kills it afterwards. This leads to problems
2005 // if you have two unreachable statements where the first one
2006 // assigns a variable and the second one tries to access it.
2009 if (!s.Resolve (ec)) {
2011 if (ec.IsInProbingMode)
2014 statements [ix] = new EmptyStatement (s.loc);
2018 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2019 statements [ix] = new EmptyStatement (s.loc);
2021 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2022 if (unreachable && s is LabeledStatement)
2023 throw new InternalErrorException ("should not happen");
2026 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2027 ec.CurrentBranching, statement_count);
2029 while (ec.CurrentBranching is FlowBranchingLabeled)
2030 ec.EndFlowBranching ();
2032 bool flow_unreachable = ec.EndFlowBranching ();
2034 ec.CurrentBlock = prev_block;
2036 if (flow_unreachable)
2037 flags |= Flags.HasRet;
2039 // If we're a non-static `struct' constructor which doesn't have an
2040 // initializer, then we must initialize all of the struct's fields.
2041 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2047 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2049 unreachable_shown = true;
2053 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2055 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2056 bool ok = Resolve (ec);
2057 ec.KillFlowBranching ();
2062 protected override void DoEmit (EmitContext ec)
2064 for (int ix = 0; ix < statements.Count; ix++){
2065 statements [ix].Emit (ec);
2069 public override void Emit (EmitContext ec)
2071 if (scope_initializers != null)
2072 EmitScopeInitializers (ec);
2074 ec.Mark (StartLocation);
2077 if (SymbolWriter.HasSymbolWriter)
2078 EmitSymbolInfo (ec);
2081 protected void EmitScopeInitializers (EmitContext ec)
2083 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2085 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2086 foreach (Statement s in scope_initializers)
2090 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2093 protected virtual void EmitSymbolInfo (EmitContext ec)
2098 public override string ToString ()
2100 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2104 protected override void CloneTo (CloneContext clonectx, Statement t)
2106 Block target = (Block) t;
2108 target.clone_id = clone_id_counter++;
2111 clonectx.AddBlockMap (this, target);
2113 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2114 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2117 target.Parent = clonectx.RemapBlockCopy (Parent);
2119 target.statements = new List<Statement> (statements.Count);
2120 foreach (Statement s in statements)
2121 target.statements.Add (s.Clone (clonectx));
2125 public class ExplicitBlock : Block
2127 protected AnonymousMethodStorey am_storey;
2129 public ExplicitBlock (Block parent, Location start, Location end)
2130 : this (parent, (Flags) 0, start, end)
2134 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2135 : base (parent, flags, start, end)
2137 this.Explicit = this;
2142 public AnonymousMethodStorey AnonymousMethodStorey {
2148 public bool HasCapturedThis {
2149 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2151 return (flags & Flags.HasCapturedThis) != 0;
2155 public bool HasCapturedVariable {
2156 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2158 return (flags & Flags.HasCapturedVariable) != 0;
2165 // Creates anonymous method storey in current block
2167 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2170 // An iterator has only 1 storey block
2172 if (ec.CurrentIterator != null)
2173 return ec.CurrentIterator.Storey;
2176 // When referencing a variable in iterator storey from children anonymous method
2178 if (ParametersBlock.am_storey is IteratorStorey) {
2179 return ParametersBlock.am_storey;
2182 if (am_storey == null) {
2183 MemberBase mc = ec.MemberContext as MemberBase;
2186 // Creates anonymous method storey for this block
2188 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2194 public override void Emit (EmitContext ec)
2196 if (am_storey != null) {
2197 DefineAnonymousStorey (ec);
2198 am_storey.EmitStoreyInstantiation (ec, this);
2201 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2202 if (emit_debug_info)
2207 if (emit_debug_info)
2211 void DefineAnonymousStorey (EmitContext ec)
2214 // Creates anonymous method storey
2216 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2218 // Creates parent storey reference when hoisted this is accessible
2220 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2221 ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2224 // Hoisted this exists in top-level parent storey only
2226 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2227 parent = parent.Parent.Explicit;
2229 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2232 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2234 // TODO MemberCache: Review
2235 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2238 am_storey.CreateType ();
2239 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2240 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2242 am_storey.DefineType ();
2243 am_storey.ResolveTypeParameters ();
2245 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2246 if (ref_blocks != null) {
2247 foreach (ExplicitBlock ref_block in ref_blocks) {
2248 for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2249 if (b.am_storey != null) {
2250 b.am_storey.AddParentStoreyReference (ec, am_storey);
2252 // Stop propagation inside same top block
2253 if (b.ParametersBlock.am_storey == ParametersBlock.am_storey)
2256 b = b.ParametersBlock;
2259 b.HasCapturedVariable = true;
2264 am_storey.Define ();
2265 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2268 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2270 tryBlock.statements = statements;
2271 statements = new List<Statement> (1);
2272 statements.Add (tf);
2277 // ParametersBlock was introduced to support anonymous methods
2278 // and lambda expressions
2280 public class ParametersBlock : ExplicitBlock
2282 public class ParameterInfo : INamedBlockVariable
2284 readonly ParametersBlock block;
2286 public VariableInfo VariableInfo;
2289 public ParameterInfo (ParametersBlock block, int index)
2297 public Block Block {
2303 public bool IsDeclared {
2309 public bool IsLocked {
2318 public Location Location {
2320 return Parameter.Location;
2324 public Parameter Parameter {
2326 return block.Parameters [index];
2330 public TypeSpec ParameterType {
2332 return Parameter.Type;
2338 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2340 return new ParameterReference (this, loc);
2345 // Block is converted to an expression
2347 sealed class BlockScopeExpression : Expression
2350 readonly ParametersBlock block;
2352 public BlockScopeExpression (Expression child, ParametersBlock block)
2358 public override Expression CreateExpressionTree (ResolveContext ec)
2360 throw new NotSupportedException ();
2363 protected override Expression DoResolve (ResolveContext ec)
2368 child = child.Resolve (ec);
2372 eclass = child.eclass;
2377 public override void Emit (EmitContext ec)
2379 block.EmitScopeInitializers (ec);
2384 protected ParametersCompiled parameters;
2385 protected ParameterInfo[] parameter_info;
2387 protected bool unreachable;
2388 protected ToplevelBlock top_block;
2390 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2391 : base (parent, 0, start, start)
2393 if (parameters == null)
2394 throw new ArgumentNullException ("parameters");
2396 this.parameters = parameters;
2397 ParametersBlock = this;
2399 this.top_block = parent.ParametersBlock.top_block;
2400 ProcessParameters ();
2403 protected ParametersBlock (ParametersCompiled parameters, Location start)
2404 : base (null, 0, start, start)
2406 if (parameters == null)
2407 throw new ArgumentNullException ("parameters");
2409 this.parameters = parameters;
2410 ParametersBlock = this;
2413 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2414 : base (null, 0, source.StartLocation, source.EndLocation)
2416 this.parameters = parameters;
2417 this.statements = source.statements;
2418 this.scope_initializers = source.scope_initializers;
2420 this.resolved = true;
2421 this.unreachable = source.unreachable;
2422 this.am_storey = source.am_storey;
2424 ParametersBlock = this;
2430 // Block has been converted to expression tree
2432 public bool IsExpressionTree {
2434 return (flags & Flags.IsExpressionTree) != 0;
2439 // The parameters for the block.
2441 public ParametersCompiled Parameters {
2447 public ToplevelBlock TopBlock {
2453 public bool Resolved {
2462 // Check whether all `out' parameters have been assigned.
2464 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2466 if (vector.IsUnreachable)
2469 int n = parameter_info == null ? 0 : parameter_info.Length;
2471 for (int i = 0; i < n; i++) {
2472 VariableInfo var = parameter_info[i].VariableInfo;
2477 if (vector.IsAssigned (var, false))
2480 TopBlock.Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2485 public override Expression CreateExpressionTree (ResolveContext ec)
2487 if (statements.Count == 1) {
2488 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2489 if (scope_initializers != null)
2490 expr = new BlockScopeExpression (expr, this);
2495 return base.CreateExpressionTree (ec);
2498 public ParameterInfo GetParameterInfo (Parameter p)
2500 for (int i = 0; i < parameters.Count; ++i) {
2501 if (parameters[i] == p)
2502 return parameter_info[i];
2505 throw new ArgumentException ("Invalid parameter");
2508 public Expression GetParameterReference (int index, Location loc)
2510 return new ParameterReference (parameter_info[index], loc);
2513 protected void ProcessParameters ()
2515 if (parameters.Count == 0)
2518 parameter_info = new ParameterInfo[parameters.Count];
2519 for (int i = 0; i < parameter_info.Length; ++i) {
2520 var p = parameters.FixedParameters[i];
2524 // TODO: Should use Parameter only and more block there
2525 parameter_info[i] = new ParameterInfo (this, i);
2526 AddLocalName (p.Name, parameter_info[i]);
2530 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2537 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2538 flags |= Flags.IsExpressionTree;
2543 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2544 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2549 unreachable = top_level.End ();
2551 } catch (Exception e) {
2552 if (e is CompletionResult || rc.Report.IsDisabled)
2555 if (rc.CurrentBlock != null) {
2556 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2558 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2561 if (Report.DebugFlags > 0)
2565 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2566 if (rc.CurrentAnonymousMethod == null) {
2567 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2568 if (md is IteratorMethod) {
2571 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2575 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2576 rc.CurrentAnonymousMethod.GetSignatureForError ());
2584 void ResolveMeta (BlockContext ec)
2586 int orig_count = parameters.Count;
2588 for (int i = 0; i < orig_count; ++i) {
2589 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2591 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2594 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2595 parameter_info[i].VariableInfo = vi;
2596 ec.FlowOffset += vi.Length;
2600 public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
2602 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2603 pb.EndLocation = EndLocation;
2604 pb.statements = statements;
2606 var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2607 am_storey = new IteratorStorey (iterator);
2609 statements = new List<Statement> (1);
2610 AddStatement (new Return (iterator, iterator.Location));
2617 public class ToplevelBlock : ParametersBlock
2619 LocalVariable this_variable;
2620 CompilerContext compiler;
2621 Dictionary<string, object> names;
2622 Dictionary<string, object> labels;
2624 public HoistedVariable HoistedThisVariable;
2626 public Report Report {
2627 get { return compiler.Report; }
2630 public ToplevelBlock (CompilerContext ctx, Location loc)
2631 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2635 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2636 : base (parameters, start)
2638 this.compiler = ctx;
2641 ProcessParameters ();
2645 // Recreates a top level block from parameters block. Used for
2646 // compiler generated methods where the original block comes from
2647 // explicit child block. This works for already resolved blocks
2648 // only to ensure we resolve them in the correct flow order
2650 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
2651 : base (source, parameters)
2653 this.compiler = source.TopBlock.compiler;
2657 public override void AddLocalName (string name, INamedBlockVariable li)
2660 names = new Dictionary<string, object> ();
2663 if (!names.TryGetValue (name, out value)) {
2664 names.Add (name, li);
2668 INamedBlockVariable existing = value as INamedBlockVariable;
2669 List<INamedBlockVariable> existing_list;
2670 if (existing != null) {
2671 existing_list = new List<INamedBlockVariable> ();
2672 existing_list.Add (existing);
2673 names[name] = existing_list;
2675 existing_list = (List<INamedBlockVariable>) value;
2679 // A collision checking between local names
2681 for (int i = 0; i < existing_list.Count; ++i) {
2682 existing = existing_list[i];
2683 Block b = existing.Block;
2685 // Collision at same level
2686 if (li.Block == b) {
2687 li.Block.Error_AlreadyDeclared (name, li);
2691 // Collision with parent
2693 while ((b = b.Parent) != null) {
2694 if (existing.Block == b) {
2695 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
2696 i = existing_list.Count;
2701 // Collision with with children
2703 while ((b = b.Parent) != null) {
2704 if (li.Block == b) {
2705 li.Block.Error_AlreadyDeclared (name, li, "child");
2706 i = existing_list.Count;
2712 existing_list.Add (li);
2715 public void AddLabel (string name, LabeledStatement label)
2718 labels = new Dictionary<string, object> ();
2721 if (!labels.TryGetValue (name, out value)) {
2722 labels.Add (name, label);
2726 LabeledStatement existing = value as LabeledStatement;
2727 List<LabeledStatement> existing_list;
2728 if (existing != null) {
2729 existing_list = new List<LabeledStatement> ();
2730 existing_list.Add (existing);
2731 labels[name] = existing_list;
2733 existing_list = (List<LabeledStatement>) value;
2737 // A collision checking between labels
2739 for (int i = 0; i < existing_list.Count; ++i) {
2740 existing = existing_list[i];
2741 Block b = existing.Block;
2743 // Collision at same level
2744 if (label.Block == b) {
2745 Report.SymbolRelatedToPreviousError (existing.loc, name);
2746 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
2750 // Collision with parent
2752 while ((b = b.Parent) != null) {
2753 if (existing.Block == b) {
2754 Report.Error (158, label.loc,
2755 "The label `{0}' shadows another label by the same name in a contained scope", name);
2756 i = existing_list.Count;
2761 // Collision with with children
2763 while ((b = b.Parent) != null) {
2764 if (label.Block == b) {
2765 Report.Error (158, label.loc,
2766 "The label `{0}' shadows another label by the same name in a contained scope", name);
2767 i = existing_list.Count;
2773 existing_list.Add (label);
2777 // Creates an arguments set from all parameters, useful for method proxy calls
2779 public Arguments GetAllParametersArguments ()
2781 int count = parameters.Count;
2782 Arguments args = new Arguments (count);
2783 for (int i = 0; i < count; ++i) {
2784 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
2785 args.Add (new Argument (arg_expr));
2792 // Lookup inside a block, the returned value can represent 3 states
2794 // true+variable: A local name was found and it's valid
2795 // false+variable: A local name was found in a child block only
2796 // false+null: No local name was found
2798 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
2804 if (!names.TryGetValue (name, out value))
2807 variable = value as INamedBlockVariable;
2809 if (variable != null) {
2811 if (variable.Block == b.Original)
2815 } while (b != null);
2823 } while (b != null);
2825 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
2826 for (int i = 0; i < list.Count; ++i) {
2829 if (variable.Block == b.Original)
2833 } while (b != null);
2841 } while (b != null);
2851 public LabeledStatement GetLabel (string name, Block block)
2857 if (!labels.TryGetValue (name, out value)) {
2861 var label = value as LabeledStatement;
2863 if (label != null) {
2864 if (label.Block == b.Original)
2867 // TODO: Temporary workaround for the switch block implicit label block
2868 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2871 List<LabeledStatement> list = (List<LabeledStatement>) value;
2872 for (int i = 0; i < list.Count; ++i) {
2874 if (label.Block == b.Original)
2877 // TODO: Temporary workaround for the switch block implicit label block
2878 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2887 // Returns the "this" instance variable of this block.
2888 // See AddThisVariable() for more information.
2890 public LocalVariable ThisVariable {
2891 get { return this_variable; }
2895 // This is used by non-static `struct' constructors which do not have an
2896 // initializer - in this case, the constructor must initialize all of the
2897 // struct's fields. To do this, we add a "this" variable and use the flow
2898 // analysis code to ensure that it's been fully initialized before control
2899 // leaves the constructor.
2901 public LocalVariable AddThisVariable (BlockContext bc, TypeContainer ds, Location l)
2903 if (this_variable == null) {
2904 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, l);
2905 this_variable.Type = ds.CurrentType;
2906 this_variable.PrepareForFlowAnalysis (bc);
2909 return this_variable;
2912 public bool IsIterator {
2913 get { return (flags & Flags.IsIterator) != 0; }
2914 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2917 public bool IsThisAssigned (BlockContext ec)
2919 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2922 public override void Emit (EmitContext ec)
2924 if (Report.Errors > 0)
2930 if (ec.HasReturnLabel)
2931 ec.ReturnLabel = ec.DefineLabel ();
2935 ec.Mark (EndLocation);
2937 if (ec.HasReturnLabel)
2938 ec.MarkLabel (ec.ReturnLabel);
2940 if (ec.return_value != null) {
2941 ec.Emit (OpCodes.Ldloc, ec.return_value);
2942 ec.Emit (OpCodes.Ret);
2945 // If `HasReturnLabel' is set, then we already emitted a
2946 // jump to the end of the method, so we must emit a `ret'
2949 // Unfortunately, System.Reflection.Emit automatically emits
2950 // a leave to the end of a finally block. This is a problem
2951 // if no code is following the try/finally block since we may
2952 // jump to a point after the end of the method.
2953 // As a workaround, we're always creating a return label in
2957 if (ec.HasReturnLabel || !unreachable) {
2958 if (ec.ReturnType != TypeManager.void_type)
2959 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2960 ec.Emit (OpCodes.Ret);
2965 } catch (Exception e){
2966 Console.WriteLine ("Exception caught by the compiler while emitting:");
2967 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2969 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2975 protected override void EmitSymbolInfo (EmitContext ec)
2977 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2978 if ((ae != null) && (ae.Storey != null))
2979 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2981 base.EmitSymbolInfo (ec);
2985 public class SwitchLabel {
2992 Label il_label_code;
2993 bool il_label_code_set;
2995 public static readonly object NullStringCase = new object ();
2998 // if expr == null, then it is the default case.
3000 public SwitchLabel (Expression expr, Location l)
3006 public Expression Label {
3012 public Location Location {
3016 public object Converted {
3022 public Label GetILLabel (EmitContext ec)
3025 il_label = ec.DefineLabel ();
3026 il_label_set = true;
3031 public Label GetILLabelCode (EmitContext ec)
3033 if (!il_label_code_set){
3034 il_label_code = ec.DefineLabel ();
3035 il_label_code_set = true;
3037 return il_label_code;
3041 // Resolves the expression, reduces it to a literal if possible
3042 // and then converts it to the requested type.
3044 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3046 Expression e = label.Resolve (ec);
3051 Constant c = e as Constant;
3053 ec.Report.Error (150, loc, "A constant value is expected");
3057 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3058 converted = NullStringCase;
3062 if (allow_nullable && c.GetValue () == null) {
3063 converted = NullStringCase;
3067 c = c.ImplicitConversionRequired (ec, required_type, loc);
3071 converted = c.GetValue ();
3075 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3078 if (converted == null)
3080 else if (converted == NullStringCase)
3083 label = converted.ToString ();
3085 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3086 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3089 public SwitchLabel Clone (CloneContext clonectx)
3091 return new SwitchLabel (label.Clone (clonectx), loc);
3095 public class SwitchSection {
3096 public readonly List<SwitchLabel> Labels;
3097 public readonly Block Block;
3099 public SwitchSection (List<SwitchLabel> labels, Block block)
3105 public SwitchSection Clone (CloneContext clonectx)
3107 var cloned_labels = new List<SwitchLabel> ();
3109 foreach (SwitchLabel sl in cloned_labels)
3110 cloned_labels.Add (sl.Clone (clonectx));
3112 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3116 public class Switch : Statement {
3117 public List<SwitchSection> Sections;
3118 public Expression Expr;
3121 /// Maps constants whose type type SwitchType to their SwitchLabels.
3123 public IDictionary<object, SwitchLabel> Elements;
3126 /// The governing switch type
3128 public TypeSpec SwitchType;
3133 Label default_target;
3135 Expression new_expr;
3138 SwitchSection constant_section;
3139 SwitchSection default_section;
3141 ExpressionStatement string_dictionary;
3142 FieldExpr switch_cache_field;
3143 static int unique_counter;
3144 ExplicitBlock block;
3147 // Nullable Types support
3149 Nullable.Unwrap unwrap;
3151 protected bool HaveUnwrap {
3152 get { return unwrap != null; }
3156 // The types allowed to be implicitly cast from
3157 // on the governing type
3159 static TypeSpec [] allowed_types;
3161 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3169 public ExplicitBlock Block {
3175 public bool GotDefault {
3177 return default_section != null;
3181 public Label DefaultTarget {
3183 return default_target;
3188 // Determines the governing type for a switch. The returned
3189 // expression might be the expression from the switch, or an
3190 // expression that includes any potential conversions to the
3191 // integral types or to string.
3193 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3195 TypeSpec t = expr.Type;
3197 if (t == TypeManager.byte_type ||
3198 t == TypeManager.sbyte_type ||
3199 t == TypeManager.ushort_type ||
3200 t == TypeManager.short_type ||
3201 t == TypeManager.uint32_type ||
3202 t == TypeManager.int32_type ||
3203 t == TypeManager.uint64_type ||
3204 t == TypeManager.int64_type ||
3205 t == TypeManager.char_type ||
3206 t == TypeManager.string_type ||
3207 t == TypeManager.bool_type ||
3208 TypeManager.IsEnumType (t))
3211 if (allowed_types == null){
3212 allowed_types = new TypeSpec [] {
3213 TypeManager.sbyte_type,
3214 TypeManager.byte_type,
3215 TypeManager.short_type,
3216 TypeManager.ushort_type,
3217 TypeManager.int32_type,
3218 TypeManager.uint32_type,
3219 TypeManager.int64_type,
3220 TypeManager.uint64_type,
3221 TypeManager.char_type,
3222 TypeManager.string_type
3227 // Try to find a *user* defined implicit conversion.
3229 // If there is no implicit conversion, or if there are multiple
3230 // conversions, we have to report an error
3232 Expression converted = null;
3233 foreach (TypeSpec tt in allowed_types){
3236 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3241 // Ignore over-worked ImplicitUserConversions that do
3242 // an implicit conversion in addition to the user conversion.
3244 if (!(e is UserCast))
3247 if (converted != null){
3248 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3258 // Performs the basic sanity checks on the switch statement
3259 // (looks for duplicate keys and non-constant expressions).
3261 // It also returns a hashtable with the keys that we will later
3262 // use to compute the switch tables
3264 bool CheckSwitch (ResolveContext ec)
3267 Elements = new Dictionary<object, SwitchLabel> ();
3269 foreach (SwitchSection ss in Sections){
3270 foreach (SwitchLabel sl in ss.Labels){
3271 if (sl.Label == null){
3272 if (default_section != null){
3273 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3276 default_section = ss;
3280 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3285 object key = sl.Converted;
3286 if (key == SwitchLabel.NullStringCase)
3287 has_null_case = true;
3290 Elements.Add (key, sl);
3291 } catch (ArgumentException) {
3292 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3300 void EmitObjectInteger (EmitContext ec, object k)
3303 ec.EmitInt ((int) k);
3304 else if (k is Constant) {
3305 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3308 ec.EmitInt (unchecked ((int) (uint) k));
3311 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3313 ec.EmitInt ((int) (long) k);
3314 ec.Emit (OpCodes.Conv_I8);
3317 ec.EmitLong ((long) k);
3319 else if (k is ulong)
3321 ulong ul = (ulong) k;
3324 ec.EmitInt (unchecked ((int) ul));
3325 ec.Emit (OpCodes.Conv_U8);
3329 ec.EmitLong (unchecked ((long) ul));
3333 ec.EmitInt ((int) ((char) k));
3334 else if (k is sbyte)
3335 ec.EmitInt ((int) ((sbyte) k));
3337 ec.EmitInt ((int) ((byte) k));
3338 else if (k is short)
3339 ec.EmitInt ((int) ((short) k));
3340 else if (k is ushort)
3341 ec.EmitInt ((int) ((ushort) k));
3343 ec.EmitInt (((bool) k) ? 1 : 0);
3345 throw new Exception ("Unhandled case");
3348 // structure used to hold blocks of keys while calculating table switch
3349 class KeyBlock : IComparable
3351 public KeyBlock (long _first)
3353 first = last = _first;
3357 public List<object> element_keys;
3358 // how many items are in the bucket
3359 public int Size = 1;
3362 get { return (int) (last - first + 1); }
3364 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3366 return kb_last.last - kb_first.first + 1;
3368 public int CompareTo (object obj)
3370 KeyBlock kb = (KeyBlock) obj;
3371 int nLength = Length;
3372 int nLengthOther = kb.Length;
3373 if (nLengthOther == nLength)
3374 return (int) (kb.first - first);
3375 return nLength - nLengthOther;
3380 /// This method emits code for a lookup-based switch statement (non-string)
3381 /// Basically it groups the cases into blocks that are at least half full,
3382 /// and then spits out individual lookup opcodes for each block.
3383 /// It emits the longest blocks first, and short blocks are just
3384 /// handled with direct compares.
3386 /// <param name="ec"></param>
3387 /// <param name="val"></param>
3388 /// <returns></returns>
3389 void TableSwitchEmit (EmitContext ec, Expression val)
3391 int element_count = Elements.Count;
3392 object [] element_keys = new object [element_count];
3393 Elements.Keys.CopyTo (element_keys, 0);
3394 Array.Sort (element_keys);
3396 // initialize the block list with one element per key
3397 var key_blocks = new List<KeyBlock> (element_count);
3398 foreach (object key in element_keys)
3399 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3401 KeyBlock current_kb;
3402 // iteratively merge the blocks while they are at least half full
3403 // there's probably a really cool way to do this with a tree...
3404 while (key_blocks.Count > 1)
3406 var key_blocks_new = new List<KeyBlock> ();
3407 current_kb = (KeyBlock) key_blocks [0];
3408 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3410 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3411 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3414 current_kb.last = kb.last;
3415 current_kb.Size += kb.Size;
3419 // start a new block
3420 key_blocks_new.Add (current_kb);
3424 key_blocks_new.Add (current_kb);
3425 if (key_blocks.Count == key_blocks_new.Count)
3427 key_blocks = key_blocks_new;
3430 // initialize the key lists
3431 foreach (KeyBlock kb in key_blocks)
3432 kb.element_keys = new List<object> ();
3434 // fill the key lists
3436 if (key_blocks.Count > 0) {
3437 current_kb = (KeyBlock) key_blocks [0];
3438 foreach (object key in element_keys)
3440 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3441 System.Convert.ToInt64 (key) > current_kb.last;
3443 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3444 current_kb.element_keys.Add (key);
3448 // sort the blocks so we can tackle the largest ones first
3451 // okay now we can start...
3452 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3453 Label lbl_default = default_target;
3455 Type type_keys = null;
3456 if (element_keys.Length > 0)
3457 type_keys = element_keys [0].GetType (); // used for conversions
3459 TypeSpec compare_type;
3461 if (TypeManager.IsEnumType (SwitchType))
3462 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3464 compare_type = SwitchType;
3466 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3468 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3469 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3472 foreach (object key in kb.element_keys) {
3473 SwitchLabel sl = (SwitchLabel) Elements [key];
3474 if (key is int && (int) key == 0) {
3475 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3478 EmitObjectInteger (ec, key);
3479 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3485 // TODO: if all the keys in the block are the same and there are
3486 // no gaps/defaults then just use a range-check.
3487 if (compare_type == TypeManager.int64_type ||
3488 compare_type == TypeManager.uint64_type)
3490 // TODO: optimize constant/I4 cases
3492 // check block range (could be > 2^31)
3494 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3495 ec.Emit (OpCodes.Blt, lbl_default);
3497 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3498 ec.Emit (OpCodes.Bgt, lbl_default);
3504 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3505 ec.Emit (OpCodes.Sub);
3507 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3513 int first = (int) kb.first;
3517 ec.Emit (OpCodes.Sub);
3521 ec.EmitInt (-first);
3522 ec.Emit (OpCodes.Add);
3526 // first, build the list of labels for the switch
3528 int cJumps = kb.Length;
3529 Label [] switch_labels = new Label [cJumps];
3530 for (int iJump = 0; iJump < cJumps; iJump++)
3532 object key = kb.element_keys [iKey];
3533 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3535 SwitchLabel sl = (SwitchLabel) Elements [key];
3536 switch_labels [iJump] = sl.GetILLabel (ec);
3540 switch_labels [iJump] = lbl_default;
3542 // emit the switch opcode
3543 ec.Emit (OpCodes.Switch, switch_labels);
3546 // mark the default for this block
3548 ec.MarkLabel (lbl_default);
3551 // TODO: find the default case and emit it here,
3552 // to prevent having to do the following jump.
3553 // make sure to mark other labels in the default section
3555 // the last default just goes to the end
3556 if (element_keys.Length > 0)
3557 ec.Emit (OpCodes.Br, lbl_default);
3559 // now emit the code for the sections
3560 bool found_default = false;
3562 foreach (SwitchSection ss in Sections) {
3563 foreach (SwitchLabel sl in ss.Labels) {
3564 if (sl.Converted == SwitchLabel.NullStringCase) {
3565 ec.MarkLabel (null_target);
3566 } else if (sl.Label == null) {
3567 ec.MarkLabel (lbl_default);
3568 found_default = true;
3570 ec.MarkLabel (null_target);
3572 ec.MarkLabel (sl.GetILLabel (ec));
3573 ec.MarkLabel (sl.GetILLabelCode (ec));
3578 if (!found_default) {
3579 ec.MarkLabel (lbl_default);
3580 if (!has_null_case) {
3581 ec.MarkLabel (null_target);
3585 ec.MarkLabel (lbl_end);
3588 SwitchSection FindSection (SwitchLabel label)
3590 foreach (SwitchSection ss in Sections){
3591 foreach (SwitchLabel sl in ss.Labels){
3600 public static void Reset ()
3603 allowed_types = null;
3606 public override bool Resolve (BlockContext ec)
3608 Expr = Expr.Resolve (ec);
3612 new_expr = SwitchGoverningType (ec, Expr);
3614 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3615 unwrap = Nullable.Unwrap.Create (Expr, false);
3619 new_expr = SwitchGoverningType (ec, unwrap);
3622 if (new_expr == null){
3623 ec.Report.Error (151, loc,
3624 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3625 TypeManager.CSharpName (Expr.Type));
3630 SwitchType = new_expr.Type;
3632 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3633 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3637 if (!CheckSwitch (ec))
3641 Elements.Remove (SwitchLabel.NullStringCase);
3643 Switch old_switch = ec.Switch;
3645 ec.Switch.SwitchType = SwitchType;
3647 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3648 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3650 var constant = new_expr as Constant;
3651 if (constant != null) {
3653 object key = constant.GetValue ();
3655 if (Elements.TryGetValue (key, out label))
3656 constant_section = FindSection (label);
3658 if (constant_section == null)
3659 constant_section = default_section;
3664 foreach (SwitchSection ss in Sections){
3666 ec.CurrentBranching.CreateSibling (
3667 null, FlowBranching.SiblingType.SwitchSection);
3671 if (is_constant && (ss != constant_section)) {
3672 // If we're a constant switch, we're only emitting
3673 // one single section - mark all the others as
3675 ec.CurrentBranching.CurrentUsageVector.Goto ();
3676 if (!ss.Block.ResolveUnreachable (ec, true)) {
3680 if (!ss.Block.Resolve (ec))
3685 if (default_section == null)
3686 ec.CurrentBranching.CreateSibling (
3687 null, FlowBranching.SiblingType.SwitchSection);
3689 ec.EndFlowBranching ();
3690 ec.Switch = old_switch;
3692 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3697 if (SwitchType == TypeManager.string_type && !is_constant) {
3698 // TODO: Optimize single case, and single+default case
3699 ResolveStringSwitchMap (ec);
3705 void ResolveStringSwitchMap (ResolveContext ec)
3707 FullNamedExpression string_dictionary_type;
3708 if (TypeManager.generic_ienumerable_type != null) {
3709 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3710 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3712 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3714 new TypeExpression (TypeManager.string_type, loc),
3715 new TypeExpression (TypeManager.int32_type, loc)), loc);
3717 MemberAccess system_collections_generic = new MemberAccess (
3718 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3720 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3723 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3724 Field field = new Field (ctype, string_dictionary_type,
3725 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3726 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3727 if (!field.Define ())
3729 ctype.AddField (field);
3731 var init = new List<Expression> ();
3734 string value = null;
3735 foreach (SwitchSection section in Sections) {
3736 int last_count = init.Count;
3737 foreach (SwitchLabel sl in section.Labels) {
3738 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3741 value = (string) sl.Converted;
3742 var init_args = new List<Expression> (2);
3743 init_args.Add (new StringLiteral (value, sl.Location));
3744 init_args.Add (new IntConstant (counter, loc));
3745 init.Add (new CollectionElementInitializer (init_args, loc));
3749 // Don't add empty sections
3751 if (last_count == init.Count)
3754 Elements.Add (counter, section.Labels [0]);
3758 Arguments args = new Arguments (1);
3759 args.Add (new Argument (new IntConstant (init.Count, loc)));
3760 Expression initializer = new NewInitialize (string_dictionary_type, args,
3761 new CollectionOrObjectInitializers (init, loc), loc);
3763 switch_cache_field = new FieldExpr (field, loc);
3764 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3767 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3769 Label l_initialized = ec.DefineLabel ();
3772 // Skip initialization when value is null
3774 value.EmitBranchable (ec, null_target, false);
3777 // Check if string dictionary is initialized and initialize
3779 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3780 string_dictionary.EmitStatement (ec);
3781 ec.MarkLabel (l_initialized);
3783 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3785 ResolveContext rc = new ResolveContext (ec.MemberContext);
3787 if (TypeManager.generic_ienumerable_type != null) {
3788 Arguments get_value_args = new Arguments (2);
3789 get_value_args.Add (new Argument (value));
3790 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3791 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3792 if (get_item == null)
3796 // A value was not found, go to default case
3798 get_item.EmitBranchable (ec, default_target, false);
3800 Arguments get_value_args = new Arguments (1);
3801 get_value_args.Add (new Argument (value));
3803 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3804 if (get_item == null)
3807 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3808 get_item_object.EmitAssign (ec, get_item, true, false);
3809 ec.Emit (OpCodes.Brfalse, default_target);
3811 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3812 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3814 get_item_int.EmitStatement (ec);
3815 get_item_object.Release (ec);
3818 TableSwitchEmit (ec, string_switch_variable);
3819 string_switch_variable.Release (ec);
3822 protected override void DoEmit (EmitContext ec)
3825 // Needed to emit anonymous storey initialization
3826 // Otherwise it does not contain any statements for now
3830 default_target = ec.DefineLabel ();
3831 null_target = ec.DefineLabel ();
3833 // Store variable for comparission purposes
3834 // TODO: Don't duplicate non-captured VariableReference
3835 LocalTemporary value;
3837 value = new LocalTemporary (SwitchType);
3838 unwrap.EmitCheck (ec);
3839 ec.Emit (OpCodes.Brfalse, null_target);
3842 } else if (!is_constant) {
3843 value = new LocalTemporary (SwitchType);
3850 // Setup the codegen context
3852 Label old_end = ec.LoopEnd;
3853 Switch old_switch = ec.Switch;
3855 ec.LoopEnd = ec.DefineLabel ();
3860 if (constant_section != null)
3861 constant_section.Block.Emit (ec);
3862 } else if (string_dictionary != null) {
3863 DoEmitStringSwitch (value, ec);
3865 TableSwitchEmit (ec, value);
3871 // Restore context state.
3872 ec.MarkLabel (ec.LoopEnd);
3875 // Restore the previous context
3877 ec.LoopEnd = old_end;
3878 ec.Switch = old_switch;
3881 protected override void CloneTo (CloneContext clonectx, Statement t)
3883 Switch target = (Switch) t;
3885 target.Expr = Expr.Clone (clonectx);
3886 target.Sections = new List<SwitchSection> ();
3887 foreach (SwitchSection ss in Sections){
3888 target.Sections.Add (ss.Clone (clonectx));
3893 // A place where execution can restart in an iterator
3894 public abstract class ResumableStatement : Statement
3897 protected Label resume_point;
3899 public Label PrepareForEmit (EmitContext ec)
3903 resume_point = ec.DefineLabel ();
3905 return resume_point;
3908 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3912 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3917 // Base class for statements that are implemented in terms of try...finally
3918 public abstract class ExceptionStatement : ResumableStatement
3922 List<ResumableStatement> resume_points;
3923 int first_resume_pc;
3924 protected Statement stmt;
3925 Label dispose_try_block;
3926 bool prepared_for_dispose, emitted_dispose;
3928 protected ExceptionStatement (Statement stmt, Location loc)
3936 public Statement Statement {
3944 protected abstract void EmitPreTryBody (EmitContext ec);
3945 protected abstract void EmitTryBody (EmitContext ec);
3946 protected abstract void EmitFinallyBody (EmitContext ec);
3948 protected sealed override void DoEmit (EmitContext ec)
3950 EmitPreTryBody (ec);
3952 if (resume_points != null) {
3953 ec.EmitInt ((int) Iterator.State.Running);
3954 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3957 ec.BeginExceptionBlock ();
3959 if (resume_points != null) {
3960 ec.MarkLabel (resume_point);
3962 // For normal control flow, we want to fall-through the Switch
3963 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3964 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3965 ec.EmitInt (first_resume_pc);
3966 ec.Emit (OpCodes.Sub);
3968 Label [] labels = new Label [resume_points.Count];
3969 for (int i = 0; i < resume_points.Count; ++i)
3970 labels [i] = resume_points [i].PrepareForEmit (ec);
3971 ec.Emit (OpCodes.Switch, labels);
3976 ec.BeginFinallyBlock ();
3978 Label start_finally = ec.DefineLabel ();
3979 if (resume_points != null) {
3980 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3981 ec.Emit (OpCodes.Brfalse_S, start_finally);
3982 ec.Emit (OpCodes.Endfinally);
3985 ec.MarkLabel (start_finally);
3986 EmitFinallyBody (ec);
3988 ec.EndExceptionBlock ();
3991 public void SomeCodeFollows ()
3993 code_follows = true;
3996 public override bool Resolve (BlockContext ec)
3998 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3999 // So, ensure there's some IL code after this statement.
4000 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4001 ec.NeedReturnLabel ();
4003 iter = ec.CurrentIterator;
4007 public void AddResumePoint (ResumableStatement stmt, int pc)
4009 if (resume_points == null) {
4010 resume_points = new List<ResumableStatement> ();
4011 first_resume_pc = pc;
4014 if (pc != first_resume_pc + resume_points.Count)
4015 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4017 resume_points.Add (stmt);
4020 public override Label PrepareForDispose (EmitContext ec, Label end)
4022 if (!prepared_for_dispose) {
4023 prepared_for_dispose = true;
4024 dispose_try_block = ec.DefineLabel ();
4026 return dispose_try_block;
4029 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4031 if (emitted_dispose)
4034 emitted_dispose = true;
4036 Label end_of_try = ec.DefineLabel ();
4038 // Ensure that the only way we can get into this code is through a dispatcher
4039 if (have_dispatcher)
4040 ec.Emit (OpCodes.Br, end);
4042 ec.BeginExceptionBlock ();
4044 ec.MarkLabel (dispose_try_block);
4046 Label [] labels = null;
4047 for (int i = 0; i < resume_points.Count; ++i) {
4048 ResumableStatement s = (ResumableStatement) resume_points [i];
4049 Label ret = s.PrepareForDispose (ec, end_of_try);
4050 if (ret.Equals (end_of_try) && labels == null)
4052 if (labels == null) {
4053 labels = new Label [resume_points.Count];
4054 for (int j = 0; j < i; ++j)
4055 labels [j] = end_of_try;
4060 if (labels != null) {
4062 for (j = 1; j < labels.Length; ++j)
4063 if (!labels [0].Equals (labels [j]))
4065 bool emit_dispatcher = j < labels.Length;
4067 if (emit_dispatcher) {
4068 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4069 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4070 ec.EmitInt (first_resume_pc);
4071 ec.Emit (OpCodes.Sub);
4072 ec.Emit (OpCodes.Switch, labels);
4073 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4076 foreach (ResumableStatement s in resume_points)
4077 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4080 ec.MarkLabel (end_of_try);
4082 ec.BeginFinallyBlock ();
4084 EmitFinallyBody (ec);
4086 ec.EndExceptionBlock ();
4090 public class Lock : ExceptionStatement {
4092 TemporaryVariableReference expr_copy;
4093 TemporaryVariableReference lock_taken;
4095 public Lock (Expression expr, Statement stmt, Location loc)
4101 public override bool Resolve (BlockContext ec)
4103 expr = expr.Resolve (ec);
4107 if (!TypeManager.IsReferenceType (expr.Type)){
4108 ec.Report.Error (185, loc,
4109 "`{0}' is not a reference type as required by the lock statement",
4110 expr.Type.GetSignatureForError ());
4113 if (expr.Type.IsGenericParameter) {
4114 expr = Convert.ImplicitTypeParameterConversion (expr, TypeManager.object_type);
4117 VariableReference lv = expr as VariableReference;
4120 locked = lv.IsLockedByStatement;
4121 lv.IsLockedByStatement = true;
4127 ec.StartFlowBranching (this);
4128 Statement.Resolve (ec);
4129 ec.EndFlowBranching ();
4132 lv.IsLockedByStatement = locked;
4138 // Have to keep original lock value around to unlock same location
4139 // in the case the original has changed or is null
4141 expr_copy = TemporaryVariableReference.Create (TypeManager.object_type, ec.CurrentBlock.Parent, loc);
4142 expr_copy.Resolve (ec);
4145 // Ensure Monitor methods are available
4147 if (ResolvePredefinedMethods (ec) > 1) {
4148 lock_taken = TemporaryVariableReference.Create (TypeManager.bool_type, ec.CurrentBlock.Parent, loc);
4149 lock_taken.Resolve (ec);
4155 protected override void EmitPreTryBody (EmitContext ec)
4157 expr_copy.EmitAssign (ec, expr);
4159 if (lock_taken != null) {
4161 // Initialize ref variable
4163 lock_taken.EmitAssign (ec, new BoolLiteral (false, loc));
4166 // Monitor.Enter (expr_copy)
4168 expr_copy.Emit (ec);
4169 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4173 protected override void EmitTryBody (EmitContext ec)
4176 // Monitor.Enter (expr_copy, ref lock_taken)
4178 if (lock_taken != null) {
4179 expr_copy.Emit (ec);
4180 lock_taken.LocalInfo.CreateBuilder (ec);
4181 lock_taken.AddressOf (ec, AddressOp.Load);
4182 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4185 Statement.Emit (ec);
4188 protected override void EmitFinallyBody (EmitContext ec)
4191 // if (lock_taken) Monitor.Exit (expr_copy)
4193 Label skip = ec.DefineLabel ();
4195 if (lock_taken != null) {
4196 lock_taken.Emit (ec);
4197 ec.Emit (OpCodes.Brfalse_S, skip);
4200 expr_copy.Emit (ec);
4201 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4202 ec.MarkLabel (skip);
4205 int ResolvePredefinedMethods (ResolveContext rc)
4207 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4208 TypeSpec monitor_type = TypeManager.CoreLookupType (rc.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4210 if (monitor_type == null)
4213 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4214 var filter = MemberFilter.Method ("Enter", 0, new ParametersImported (
4216 new ParameterData (null, Parameter.Modifier.NONE),
4217 new ParameterData (null, Parameter.Modifier.REF)
4220 TypeManager.object_type,
4221 TypeManager.bool_type
4224 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (monitor_type, filter, true, loc);
4225 if (TypeManager.void_monitor_enter_object == null) {
4226 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4227 monitor_type, "Enter", loc, TypeManager.object_type);
4230 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4231 monitor_type, "Exit", loc, TypeManager.object_type);
4234 return TypeManager.void_monitor_enter_object.Parameters.Count;
4237 protected override void CloneTo (CloneContext clonectx, Statement t)
4239 Lock target = (Lock) t;
4241 target.expr = expr.Clone (clonectx);
4242 target.stmt = Statement.Clone (clonectx);
4246 public class Unchecked : Statement {
4249 public Unchecked (Block b, Location loc)
4256 public override bool Resolve (BlockContext ec)
4258 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4259 return Block.Resolve (ec);
4262 protected override void DoEmit (EmitContext ec)
4264 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4268 protected override void CloneTo (CloneContext clonectx, Statement t)
4270 Unchecked target = (Unchecked) t;
4272 target.Block = clonectx.LookupBlock (Block);
4276 public class Checked : Statement {
4279 public Checked (Block b, Location loc)
4282 b.Unchecked = false;
4286 public override bool Resolve (BlockContext ec)
4288 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4289 return Block.Resolve (ec);
4292 protected override void DoEmit (EmitContext ec)
4294 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4298 protected override void CloneTo (CloneContext clonectx, Statement t)
4300 Checked target = (Checked) t;
4302 target.Block = clonectx.LookupBlock (Block);
4306 public class Unsafe : Statement {
4309 public Unsafe (Block b, Location loc)
4312 Block.Unsafe = true;
4316 public override bool Resolve (BlockContext ec)
4318 if (ec.CurrentIterator != null)
4319 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4321 using (ec.Set (ResolveContext.Options.UnsafeScope))
4322 return Block.Resolve (ec);
4325 protected override void DoEmit (EmitContext ec)
4330 protected override void CloneTo (CloneContext clonectx, Statement t)
4332 Unsafe target = (Unsafe) t;
4334 target.Block = clonectx.LookupBlock (Block);
4341 public class Fixed : Statement
4343 abstract class Emitter : ShimExpression
4345 protected LocalVariable vi;
4347 protected Emitter (Expression expr, LocalVariable li)
4353 public abstract void EmitExit (EmitContext ec);
4356 class ExpressionEmitter : Emitter {
4357 public ExpressionEmitter (Expression converted, LocalVariable li) :
4358 base (converted, li)
4362 protected override Expression DoResolve (ResolveContext rc)
4364 throw new NotImplementedException ();
4367 public override void Emit (EmitContext ec) {
4369 // Store pointer in pinned location
4375 public override void EmitExit (EmitContext ec)
4377 ec.Emit (OpCodes.Ldc_I4_0);
4378 ec.Emit (OpCodes.Conv_U);
4383 class StringEmitter : Emitter
4385 LocalVariable pinned_string;
4387 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4392 protected override Expression DoResolve (ResolveContext rc)
4394 pinned_string = new LocalVariable (vi.Block, "$pinned",
4395 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4398 pinned_string.Type = TypeManager.string_type;
4400 if (TypeManager.int_get_offset_to_string_data == null) {
4401 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4402 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4405 eclass = ExprClass.Variable;
4406 type = TypeManager.int32_type;
4410 public override void Emit (EmitContext ec)
4412 pinned_string.CreateBuilder (ec);
4415 pinned_string.EmitAssign (ec);
4417 // TODO: Should use Binary::Add
4418 pinned_string.Emit (ec);
4419 ec.Emit (OpCodes.Conv_I);
4421 PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4422 //pe.InstanceExpression = pinned_string;
4423 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4425 ec.Emit (OpCodes.Add);
4429 public override void EmitExit (EmitContext ec)
4431 ec.Emit (OpCodes.Ldnull);
4432 pinned_string.EmitAssign (ec);
4436 public class VariableDeclaration : BlockVariableDeclaration
4438 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4443 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4445 if (!Variable.Type.IsPointer && li == Variable) {
4446 bc.Report.Error (209, TypeExpression.Location,
4447 "The type of locals declared in a fixed statement must be a pointer type");
4452 // The rules for the possible declarators are pretty wise,
4453 // but the production on the grammar is more concise.
4455 // So we have to enforce these rules here.
4457 // We do not resolve before doing the case 1 test,
4458 // because the grammar is explicit in that the token &
4459 // is present, so we need to test for this particular case.
4462 if (initializer is Cast) {
4463 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4467 initializer = initializer.Resolve (bc);
4469 if (initializer == null)
4475 if (initializer.Type.IsArray) {
4476 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4479 // Provided that array_type is unmanaged,
4481 if (!TypeManager.VerifyUnmanaged (bc.Compiler, array_type, loc))
4485 // and T* is implicitly convertible to the
4486 // pointer type given in the fixed statement.
4488 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4490 Expression converted = Convert.ImplicitConversionRequired (
4491 bc, array_ptr, li.Type, loc);
4492 if (converted == null)
4496 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4498 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4499 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4500 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (0, loc), loc), loc)),
4501 new NullPointer (loc),
4504 converted = converted.Resolve (bc);
4506 return new ExpressionEmitter (converted, li);
4512 if (initializer.Type == TypeManager.string_type) {
4513 return new StringEmitter (initializer, li, loc).Resolve (bc);
4516 // Case 3: fixed buffer
4517 if (initializer is FixedBufferPtr) {
4518 return new ExpressionEmitter (initializer, li);
4522 // Case 4: & object.
4524 bool already_fixed = true;
4525 Unary u = initializer as Unary;
4526 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4527 IVariableReference vr = u.Expr as IVariableReference;
4528 if (vr == null || !vr.IsFixed) {
4529 already_fixed = false;
4533 if (already_fixed) {
4534 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4537 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4538 return new ExpressionEmitter (initializer, li);
4543 VariableDeclaration decl;
4544 Statement statement;
4547 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4556 public Statement Statement {
4562 public BlockVariableDeclaration Variables {
4570 public override bool Resolve (BlockContext ec)
4572 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4573 if (!decl.Resolve (ec))
4577 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4578 bool ok = statement.Resolve (ec);
4579 bool flow_unreachable = ec.EndFlowBranching ();
4580 has_ret = flow_unreachable;
4585 protected override void DoEmit (EmitContext ec)
4587 decl.Variable.CreateBuilder (ec);
4588 decl.Initializer.Emit (ec);
4589 if (decl.Declarators != null) {
4590 foreach (var d in decl.Declarators) {
4591 d.Variable.CreateBuilder (ec);
4592 d.Initializer.Emit (ec);
4596 statement.Emit (ec);
4602 // Clear the pinned variable
4604 ((Emitter) decl.Initializer).EmitExit (ec);
4605 if (decl.Declarators != null) {
4606 foreach (var d in decl.Declarators) {
4607 ((Emitter)d.Initializer).EmitExit (ec);
4612 protected override void CloneTo (CloneContext clonectx, Statement t)
4614 Fixed target = (Fixed) t;
4616 target.decl = (VariableDeclaration) decl.Clone (clonectx);
4617 target.statement = statement.Clone (clonectx);
4621 public class Catch : Statement
4625 FullNamedExpression type_expr;
4626 CompilerAssign assign;
4629 public Catch (Block block, Location loc)
4637 public Block Block {
4643 public TypeSpec CatchType {
4649 public bool IsGeneral {
4651 return type_expr == null;
4655 public FullNamedExpression TypeExpression {
4664 public LocalVariable Variable {
4675 protected override void DoEmit (EmitContext ec)
4678 ec.BeginCatchBlock (TypeManager.object_type);
4680 ec.BeginCatchBlock (CatchType);
4683 li.CreateBuilder (ec);
4686 // Special case hoisted catch variable, we have to use a temporary variable
4687 // to pass via anonymous storey initialization with the value still on top
4690 if (li.HoistedVariant != null) {
4691 LocalTemporary lt = new LocalTemporary (li.Type);
4692 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4694 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4696 // switch to assigning from the temporary variable and not from top of the stack
4697 assign.UpdateSource (lt);
4700 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4701 ec.Emit (OpCodes.Pop);
4702 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4708 public override bool Resolve (BlockContext ec)
4710 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4711 if (type_expr != null) {
4712 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4717 if (type != TypeManager.exception_type && !TypeSpec.IsBaseClass (type, TypeManager.exception_type, false)) {
4718 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4719 } else if (li != null) {
4721 li.PrepareForFlowAnalysis (ec);
4723 // source variable is at the top of the stack
4724 Expression source = new EmptyExpression (li.Type);
4725 if (li.Type.IsGenericParameter)
4726 source = new UnboxCast (source, li.Type);
4728 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
4729 Block.AddScopeStatement (new StatementExpression (assign));
4733 return Block.Resolve (ec);
4737 protected override void CloneTo (CloneContext clonectx, Statement t)
4739 Catch target = (Catch) t;
4741 if (type_expr != null)
4742 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
4744 target.block = clonectx.LookupBlock (block);
4748 public class TryFinally : ExceptionStatement {
4751 public TryFinally (Statement stmt, Block fini, Location loc)
4757 public override bool Resolve (BlockContext ec)
4761 ec.StartFlowBranching (this);
4763 if (!stmt.Resolve (ec))
4767 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4768 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4769 if (!fini.Resolve (ec))
4773 ec.EndFlowBranching ();
4775 ok &= base.Resolve (ec);
4780 protected override void EmitPreTryBody (EmitContext ec)
4784 protected override void EmitTryBody (EmitContext ec)
4789 protected override void EmitFinallyBody (EmitContext ec)
4794 protected override void CloneTo (CloneContext clonectx, Statement t)
4796 TryFinally target = (TryFinally) t;
4798 target.stmt = (Statement) stmt.Clone (clonectx);
4800 target.fini = clonectx.LookupBlock (fini);
4804 public class TryCatch : Statement {
4806 public List<Catch> Specific;
4807 public Catch General;
4808 bool inside_try_finally, code_follows;
4810 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4813 this.Specific = catch_clauses;
4814 this.inside_try_finally = inside_try_finally;
4816 Catch c = catch_clauses [0];
4819 catch_clauses.RemoveAt (0);
4825 public override bool Resolve (BlockContext ec)
4829 ec.StartFlowBranching (this);
4831 if (!Block.Resolve (ec))
4834 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4836 foreach (Catch c in Specific){
4837 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4839 if (!c.Resolve (ec)) {
4844 TypeSpec resolved_type = c.CatchType;
4845 for (int ii = 0; ii < last_index; ++ii) {
4846 if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4847 ec.Report.Error (160, c.loc,
4848 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4849 TypeManager.CSharpName (prev_catches [ii]));
4854 prev_catches [last_index++] = resolved_type;
4857 if (General != null) {
4858 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4859 foreach (Catch c in Specific){
4860 if (c.CatchType == TypeManager.exception_type && ec.Compiler.PredefinedAttributes.RuntimeCompatibility.IsDefined) {
4861 ec.Report.Warning (1058, 1, c.loc,
4862 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4867 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4869 if (!General.Resolve (ec))
4873 ec.EndFlowBranching ();
4875 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4876 // So, ensure there's some IL code after this statement
4877 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4878 ec.NeedReturnLabel ();
4883 public void SomeCodeFollows ()
4885 code_follows = true;
4888 protected override void DoEmit (EmitContext ec)
4890 if (!inside_try_finally)
4891 ec.BeginExceptionBlock ();
4895 foreach (Catch c in Specific)
4898 if (General != null)
4901 if (!inside_try_finally)
4902 ec.EndExceptionBlock ();
4905 protected override void CloneTo (CloneContext clonectx, Statement t)
4907 TryCatch target = (TryCatch) t;
4909 target.Block = clonectx.LookupBlock (Block);
4910 if (General != null)
4911 target.General = (Catch) General.Clone (clonectx);
4912 if (Specific != null){
4913 target.Specific = new List<Catch> ();
4914 foreach (Catch c in Specific)
4915 target.Specific.Add ((Catch) c.Clone (clonectx));
4920 public class Using : ExceptionStatement
4922 public class VariableDeclaration : BlockVariableDeclaration
4924 Statement dispose_call;
4926 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4931 public VariableDeclaration (LocalVariable li, Location loc)
4937 public VariableDeclaration (Expression expr)
4940 loc = expr.Location;
4946 public bool IsNested { get; private set; }
4950 public void EmitDispose (EmitContext ec)
4952 dispose_call.Emit (ec);
4955 public override bool Resolve (BlockContext bc)
4960 return base.Resolve (bc);
4963 public Expression ResolveExpression (BlockContext bc)
4965 var e = Initializer.Resolve (bc);
4969 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
4970 Initializer = ResolveInitializer (bc, Variable, e);
4974 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4976 if (li.Type == InternalType.Dynamic) {
4977 initializer = initializer.Resolve (bc);
4978 if (initializer == null)
4981 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
4982 Arguments args = new Arguments (1);
4983 args.Add (new Argument (initializer));
4984 initializer = new DynamicConversion (TypeManager.idisposable_type, 0, args, initializer.Location).Resolve (bc);
4985 if (initializer == null)
4988 var var = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
4989 dispose_call = CreateDisposeCall (bc, var);
4990 dispose_call.Resolve (bc);
4992 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
4995 if (li == Variable) {
4996 CheckIDiposableConversion (bc, li, initializer);
4997 dispose_call = CreateDisposeCall (bc, li);
4998 dispose_call.Resolve (bc);
5001 return base.ResolveInitializer (bc, li, initializer);
5004 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5008 if (type != TypeManager.idisposable_type && !type.ImplementsInterface (TypeManager.idisposable_type, false)) {
5009 if (TypeManager.IsNullableType (type)) {
5010 // it's handled in CreateDisposeCall
5014 bc.Report.SymbolRelatedToPreviousError (type);
5015 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5016 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5017 type.GetSignatureForError ());
5023 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5025 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5027 var loc = lv.Location;
5029 if (TypeManager.void_dispose_void == null) {
5030 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5031 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5034 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5035 dispose_mg.InstanceExpression = TypeManager.IsNullableType (type) ?
5036 new Cast (new TypeExpression (TypeManager.idisposable_type, loc), lvr, loc).Resolve (bc) :
5039 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5041 // Add conditional call when disposing possible null variable
5042 if (!type.IsStruct || TypeManager.IsNullableType (type))
5043 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
5048 public Statement RewriteForDeclarators (BlockContext bc, Statement stmt)
5050 for (int i = declarators.Count - 1; i >= 0; --i) {
5051 var d = declarators [i];
5052 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5053 vd.Initializer = d.Initializer;
5055 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5056 vd.dispose_call.Resolve (bc);
5058 stmt = new Using (vd, stmt, d.Variable.Location);
5066 VariableDeclaration decl;
5068 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5074 public Using (Expression expr, Statement stmt, Location loc)
5077 this.decl = new VariableDeclaration (expr);
5082 public Expression Expression {
5084 return decl.Variable == null ? decl.Initializer : null;
5088 public BlockVariableDeclaration Variables {
5096 protected override void EmitPreTryBody (EmitContext ec)
5101 protected override void EmitTryBody (EmitContext ec)
5106 protected override void EmitFinallyBody (EmitContext ec)
5108 decl.EmitDispose (ec);
5111 public override bool Resolve (BlockContext ec)
5113 VariableReference vr;
5114 bool vr_locked = false;
5116 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5117 if (decl.Variable == null) {
5118 vr = decl.ResolveExpression (ec) as VariableReference;
5120 vr_locked = vr.IsLockedByStatement;
5121 vr.IsLockedByStatement = true;
5124 if (!decl.Resolve (ec))
5127 if (decl.Declarators != null) {
5128 stmt = decl.RewriteForDeclarators (ec, stmt);
5135 ec.StartFlowBranching (this);
5139 ec.EndFlowBranching ();
5142 vr.IsLockedByStatement = vr_locked;
5149 protected override void CloneTo (CloneContext clonectx, Statement t)
5151 Using target = (Using) t;
5153 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5154 target.stmt = stmt.Clone (clonectx);
5159 /// Implementation of the foreach C# statement
5161 public class Foreach : Statement {
5163 sealed class ArrayForeach : Statement
5165 readonly Foreach for_each;
5166 readonly Statement statement;
5169 TemporaryVariableReference[] lengths;
5170 Expression [] length_exprs;
5171 StatementExpression[] counter;
5172 TemporaryVariableReference[] variables;
5174 TemporaryVariableReference copy;
5176 LocalVariableReference variable;
5178 public ArrayForeach (Foreach @foreach, int rank)
5180 for_each = @foreach;
5181 statement = for_each.statement;
5183 variable = new LocalVariableReference (for_each.variable, loc);
5185 counter = new StatementExpression[rank];
5186 variables = new TemporaryVariableReference[rank];
5187 length_exprs = new Expression [rank];
5190 // Only use temporary length variables when dealing with
5191 // multi-dimensional arrays
5194 lengths = new TemporaryVariableReference [rank];
5197 protected override void CloneTo (CloneContext clonectx, Statement target)
5199 throw new NotImplementedException ();
5202 public override bool Resolve (BlockContext ec)
5204 Block variables_block = variable.local_info.Block;
5205 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5208 int rank = length_exprs.Length;
5209 Arguments list = new Arguments (rank);
5210 for (int i = 0; i < rank; i++) {
5211 var v = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5213 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5214 counter[i].Resolve (ec);
5217 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5219 lengths[i] = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5220 lengths[i].Resolve (ec);
5222 Arguments args = new Arguments (1);
5223 args.Add (new Argument (new IntConstant (i, loc)));
5224 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5227 list.Add (new Argument (v));
5230 access = new ElementAccess (copy, list, loc).Resolve (ec);
5234 Expression var_type = for_each.type;
5235 VarExpr ve = var_type as VarExpr;
5237 // Infer implicitly typed local variable from foreach array type
5238 var_type = new TypeExpression (access.Type, ve.Location);
5241 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5242 if (var_type == null)
5245 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5251 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5252 ec.CurrentBranching.CreateSibling ();
5254 variable.local_info.Type = conv.Type;
5255 variable.Resolve (ec);
5257 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5258 if (!statement.Resolve (ec))
5260 ec.EndFlowBranching ();
5262 // There's no direct control flow from the end of the embedded statement to the end of the loop
5263 ec.CurrentBranching.CurrentUsageVector.Goto ();
5265 ec.EndFlowBranching ();
5270 protected override void DoEmit (EmitContext ec)
5272 copy.EmitAssign (ec, for_each.expr);
5274 int rank = length_exprs.Length;
5275 Label[] test = new Label [rank];
5276 Label[] loop = new Label [rank];
5278 for (int i = 0; i < rank; i++) {
5279 test [i] = ec.DefineLabel ();
5280 loop [i] = ec.DefineLabel ();
5282 if (lengths != null)
5283 lengths [i].EmitAssign (ec, length_exprs [i]);
5286 IntConstant zero = new IntConstant (0, loc);
5287 for (int i = 0; i < rank; i++) {
5288 variables [i].EmitAssign (ec, zero);
5290 ec.Emit (OpCodes.Br, test [i]);
5291 ec.MarkLabel (loop [i]);
5294 variable.local_info.CreateBuilder (ec);
5295 variable.EmitAssign (ec, conv, false, false);
5297 statement.Emit (ec);
5299 ec.MarkLabel (ec.LoopBegin);
5301 for (int i = rank - 1; i >= 0; i--){
5302 counter [i].Emit (ec);
5304 ec.MarkLabel (test [i]);
5305 variables [i].Emit (ec);
5307 if (lengths != null)
5308 lengths [i].Emit (ec);
5310 length_exprs [i].Emit (ec);
5312 ec.Emit (OpCodes.Blt, loop [i]);
5315 ec.MarkLabel (ec.LoopEnd);
5319 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5321 class Body : Statement
5324 LocalVariableReference variable;
5325 Expression current, conv;
5326 Statement statement;
5328 public Body (TypeSpec type, LocalVariable variable,
5329 Expression current, Statement statement,
5333 this.variable = new LocalVariableReference (variable, loc);
5334 this.current = current;
5335 this.statement = statement;
5339 protected override void CloneTo (CloneContext clonectx, Statement target)
5341 throw new NotImplementedException ();
5344 public override bool Resolve (BlockContext ec)
5346 current = current.Resolve (ec);
5347 if (current == null)
5350 conv = Convert.ExplicitConversion (ec, current, type, loc);
5354 variable.local_info.Type = conv.Type;
5355 variable.Resolve (ec);
5357 if (!statement.Resolve (ec))
5363 protected override void DoEmit (EmitContext ec)
5365 variable.local_info.CreateBuilder (ec);
5366 variable.EmitAssign (ec, conv, false, false);
5368 statement.Emit (ec);
5372 class RuntimeDispose : Using.VariableDeclaration
5374 public RuntimeDispose (LocalVariable lv, Location loc)
5379 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5381 // Defered to runtime check
5384 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5386 if (TypeManager.void_dispose_void == null) {
5387 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5388 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5392 // Fabricates code like
5394 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5397 var dispose_variable = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
5399 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5400 dispose_variable.CreateReferenceExpression (bc, loc),
5401 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5402 loc), new NullLiteral (loc), loc);
5404 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5405 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5407 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5408 return new If (idisaposable_test, dispose, loc);
5412 LocalVariable variable;
5414 Statement statement;
5415 Expression var_type;
5416 ExpressionStatement init;
5417 TemporaryVariableReference enumerator_variable;
5418 bool ambiguous_getenumerator_name;
5420 public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5422 this.var_type = var_type;
5423 this.variable = var;
5429 protected override void CloneTo (CloneContext clonectx, Statement target)
5431 throw new NotImplementedException ();
5434 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5436 rc.Report.SymbolRelatedToPreviousError (enumerator);
5437 rc.Report.Error (202, loc,
5438 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5439 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5442 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5445 // Option 1: Try to match by name GetEnumerator first
5447 var mexpr = Expression.MemberLookup (rc, rc.CurrentType, expr.Type,
5448 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5450 var mg = mexpr as MethodGroupExpr;
5452 mg.InstanceExpression = expr;
5453 Arguments args = new Arguments (0);
5454 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5456 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5457 if (ambiguous_getenumerator_name)
5460 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5466 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5468 TypeSpec iface_candidate = null;
5469 for (TypeSpec t = expr.Type; t != null && t != TypeManager.object_type; t = t.BaseType) {
5470 var ifaces = t.Interfaces;
5471 if (ifaces != null) {
5472 foreach (var iface in ifaces) {
5473 if (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) {
5474 if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) {
5475 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5476 rc.Report.Error (1640, loc,
5477 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5478 expr.Type.GetSignatureForError (), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5483 iface_candidate = iface;
5487 if (iface == TypeManager.ienumerable_type && iface_candidate == null) {
5488 iface_candidate = iface;
5494 if (iface_candidate == null) {
5495 rc.Report.Error (1579, loc,
5496 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5497 expr.Type.GetSignatureForError (), "GetEnumerator");
5502 var method = TypeManager.GetPredefinedMethod (iface_candidate,
5503 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null), loc);
5508 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5509 mg.InstanceExpression = expr;
5513 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5515 var ms = MemberCache.FindMember (enumerator.ReturnType,
5516 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5517 BindingRestriction.InstanceOnly) as MethodSpec;
5519 if (ms == null || !ms.IsPublic) {
5520 Error_WrongEnumerator (rc, enumerator);
5524 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5527 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5529 var ps = MemberCache.FindMember (enumerator.ReturnType,
5530 MemberFilter.Property ("Current", null),
5531 BindingRestriction.InstanceOnly) as PropertySpec;
5533 if (ps == null || !ps.IsPublic) {
5534 Error_WrongEnumerator (rc, enumerator);
5541 public override bool Resolve (BlockContext ec)
5543 bool is_dynamic = expr.Type == InternalType.Dynamic;
5546 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5547 } else if (TypeManager.IsNullableType (expr.Type)) {
5548 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
5551 var get_enumerator_mg = ResolveGetEnumerator (ec);
5552 if (get_enumerator_mg == null) {
5556 var get_enumerator = get_enumerator_mg.BestCandidate;
5557 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
5558 enumerator_variable.Resolve (ec);
5560 // Prepare bool MoveNext ()
5561 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5562 if (move_next_mg == null) {
5566 move_next_mg.InstanceExpression = enumerator_variable;
5568 // Prepare ~T~ Current { get; }
5569 var current_prop = ResolveCurrent (ec, get_enumerator);
5570 if (current_prop == null) {
5574 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
5575 if (current_pe == null)
5578 VarExpr ve = var_type as VarExpr;
5581 // Source type is dynamic, set element type to dynamic too
5582 var_type = new TypeExpression (InternalType.Dynamic, var_type.Location);
5584 // Infer implicitly typed local variable from foreach enumerable type
5585 var_type = new TypeExpression (current_pe.Type, var_type.Location);
5587 } else if (is_dynamic) {
5588 // Explicit cast of dynamic collection elements has to be done at runtime
5589 current_pe = EmptyCast.Create (current_pe, InternalType.Dynamic);
5592 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5593 if (var_type == null)
5596 variable.Type = var_type.Type;
5598 var init = new Invocation (get_enumerator_mg, null);
5600 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5601 new Body (var_type.Type, variable, current_pe, statement, loc), loc);
5603 var enum_type = enumerator_variable.Type;
5606 // Add Dispose method call when enumerator can be IDisposable
5608 if (!enum_type.ImplementsInterface (TypeManager.idisposable_type, false)) {
5609 if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) {
5611 // Runtime Dispose check
5613 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
5614 vd.Initializer = init;
5615 statement = new Using (vd, statement, loc);
5618 // No Dispose call needed
5620 this.init = new SimpleAssign (enumerator_variable, init);
5621 this.init.Resolve (ec);
5625 // Static Dispose check
5627 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
5628 vd.Initializer = init;
5629 statement = new Using (vd, statement, loc);
5632 return statement.Resolve (ec);
5635 protected override void DoEmit (EmitContext ec)
5637 enumerator_variable.LocalInfo.CreateBuilder (ec);
5640 init.EmitStatement (ec);
5642 statement.Emit (ec);
5645 #region IErrorHandler Members
5647 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5649 ec.Report.SymbolRelatedToPreviousError (best);
5650 ec.Report.Warning (278, 2, loc,
5651 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5652 expr.Type.GetSignatureForError (), "enumerable",
5653 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5655 ambiguous_getenumerator_name = true;
5659 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5664 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5669 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5678 LocalVariable variable;
5680 Statement statement;
5682 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
5685 this.variable = var;
5691 public Statement Statement {
5692 get { return statement; }
5695 public override bool Resolve (BlockContext ec)
5697 expr = expr.Resolve (ec);
5702 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5706 if (expr.Type == TypeManager.string_type) {
5707 statement = new ArrayForeach (this, 1);
5708 } else if (expr.Type is ArrayContainer) {
5709 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5711 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5712 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5713 expr.ExprClassName);
5717 statement = new CollectionForeach (type, variable, expr, statement, loc);
5720 return statement.Resolve (ec);
5723 protected override void DoEmit (EmitContext ec)
5725 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5726 ec.LoopBegin = ec.DefineLabel ();
5727 ec.LoopEnd = ec.DefineLabel ();
5729 statement.Emit (ec);
5731 ec.LoopBegin = old_begin;
5732 ec.LoopEnd = old_end;
5735 protected override void CloneTo (CloneContext clonectx, Statement t)
5737 Foreach target = (Foreach) t;
5739 target.type = type.Clone (clonectx);
5740 target.expr = expr.Clone (clonectx);
5741 target.statement = statement.Clone (clonectx);