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,
1470 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1474 readonly string name;
1475 readonly Location loc;
1476 readonly Block block;
1478 Constant const_value;
1480 public VariableInfo VariableInfo;
1481 HoistedVariable hoisted_variant;
1483 LocalBuilder builder;
1485 public LocalVariable (Block block, string name, Location loc)
1492 public LocalVariable (Block block, string name, Flags flags, Location loc)
1493 : this (block, name, loc)
1499 // Used by variable declarators
1501 public LocalVariable (LocalVariable li, string name, Location loc)
1502 : this (li.block, name, li.flags, loc)
1508 public Block Block {
1514 public Constant ConstantValue {
1519 const_value = value;
1524 // Hoisted local variable variant
1526 public HoistedVariable HoistedVariant {
1528 return hoisted_variant;
1531 hoisted_variant = value;
1535 public bool IsDeclared {
1537 return type != null;
1541 public bool IsConstant {
1543 return (flags & Flags.Constant) != 0;
1547 public bool IsThis {
1549 return (flags & Flags.IsThis) != 0;
1553 public bool IsFixed {
1555 return (flags & Flags.FixedVariable) != 0;
1559 public bool IsReadonly {
1561 return (flags & Flags.ReadonlyMask) != 0;
1565 public Location Location {
1571 public string Name {
1577 public TypeSpec Type {
1588 public void CreateBuilder (EmitContext ec)
1590 if ((flags & Flags.Used) == 0) {
1591 if (VariableInfo == null) {
1592 // Missing flow analysis or wrong variable flags
1593 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1596 if (VariableInfo.IsEverAssigned)
1597 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1599 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1602 if (HoistedVariant != null)
1605 if (builder != null) {
1606 if ((flags & Flags.CompilerGenerated) != 0)
1609 // To avoid Used warning duplicates
1610 throw new InternalErrorException ("Already created variable `{0}'", name);
1614 // All fixed variabled are pinned, a slot has to be alocated
1616 builder = ec.DeclareLocal (Type, IsFixed);
1617 if (SymbolWriter.HasSymbolWriter)
1618 ec.DefineLocalVariable (name, builder);
1621 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1623 LocalVariable li = new LocalVariable (block, "<$$>", Flags.CompilerGenerated | Flags.Used, loc);
1628 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1630 if (IsConstant && const_value != null)
1631 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc).Resolve (rc);
1633 return new LocalVariableReference (this, loc);
1636 public void Emit (EmitContext ec)
1638 // TODO: Need something better for temporary variables
1639 if ((flags & Flags.CompilerGenerated) != 0)
1642 ec.Emit (OpCodes.Ldloc, builder);
1645 public void EmitAssign (EmitContext ec)
1647 // TODO: Need something better for temporary variables
1648 if ((flags & Flags.CompilerGenerated) != 0)
1651 ec.Emit (OpCodes.Stloc, builder);
1654 public void EmitAddressOf (EmitContext ec)
1656 ec.Emit (OpCodes.Ldloca, builder);
1659 public bool IsThisAssigned (BlockContext ec, Block block)
1661 if (VariableInfo == null)
1662 throw new Exception ();
1664 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1667 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1670 public bool IsAssigned (BlockContext ec)
1672 if (VariableInfo == null)
1673 throw new Exception ();
1675 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1678 public void PrepareForFlowAnalysis (BlockContext bc)
1681 // No need for definitely assigned check for these guys
1683 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1686 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1687 bc.FlowOffset += VariableInfo.Length;
1691 // Mark the variables as referenced in the user code
1693 public void SetIsUsed ()
1695 flags |= Flags.Used;
1698 public bool AddressTaken {
1699 get { return (flags & Flags.AddressTaken) != 0; }
1700 set { flags |= Flags.AddressTaken; }
1703 public override string ToString ()
1705 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
1708 public string GetReadOnlyContext ()
1710 switch (flags & Flags.ReadonlyMask) {
1711 case Flags.FixedVariable:
1712 return "fixed variable";
1713 case Flags.ForeachVariable:
1714 return "foreach iteration variable";
1715 case Flags.UsingVariable:
1716 return "using variable";
1719 throw new InternalErrorException ("Variable is not readonly");
1724 /// Block represents a C# block.
1728 /// This class is used in a number of places: either to represent
1729 /// explicit blocks that the programmer places or implicit blocks.
1731 /// Implicit blocks are used as labels or to introduce variable
1734 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1735 /// they contain extra information that is not necessary on normal blocks.
1737 public class Block : Statement {
1745 HasCapturedVariable = 64,
1746 HasCapturedThis = 1 << 7,
1747 IsExpressionTree = 1 << 8,
1748 CompilerGenerated = 1 << 9
1751 public Block Parent;
1752 public Location StartLocation;
1753 public Location EndLocation;
1755 public ExplicitBlock Explicit;
1756 public ParametersBlock ParametersBlock;
1758 protected Flags flags;
1761 // The statements in this block
1763 protected List<Statement> statements;
1765 protected List<Statement> scope_initializers;
1767 int? resolving_init_idx;
1773 public int ID = id++;
1775 static int clone_id_counter;
1779 // int assignable_slots;
1780 bool unreachable_shown;
1783 public Block (Block parent, Location start, Location end)
1784 : this (parent, 0, start, end)
1788 public Block (Block parent, Flags flags, Location start, Location end)
1790 if (parent != null) {
1791 // the appropriate constructors will fixup these fields
1792 ParametersBlock = parent.ParametersBlock;
1793 Explicit = parent.Explicit;
1796 this.Parent = parent;
1798 this.StartLocation = start;
1799 this.EndLocation = end;
1801 statements = new List<Statement> (4);
1803 this.original = this;
1808 public bool HasRet {
1809 get { return (flags & Flags.HasRet) != 0; }
1812 public Block Original {
1818 public bool IsCompilerGenerated {
1819 get { return (flags & Flags.CompilerGenerated) != 0; }
1820 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
1823 public bool Unchecked {
1824 get { return (flags & Flags.Unchecked) != 0; }
1825 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1828 public bool Unsafe {
1829 get { return (flags & Flags.Unsafe) != 0; }
1830 set { flags |= Flags.Unsafe; }
1835 public Block CreateSwitchBlock (Location start)
1837 // FIXME: Only explicit block should be created
1838 var new_block = new Block (this, start, start);
1839 new_block.IsCompilerGenerated = true;
1843 public void SetEndLocation (Location loc)
1848 public void AddLabel (LabeledStatement target)
1850 ParametersBlock.TopBlock.AddLabel (target.Name, target);
1853 public void AddLocalName (LocalVariable li)
1855 AddLocalName (li.Name, li);
1858 public virtual void AddLocalName (string name, INamedBlockVariable li)
1860 ParametersBlock.TopBlock.AddLocalName (name, li);
1863 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
1865 if (reason == null) {
1866 Error_AlreadyDeclared (name, variable);
1870 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
1871 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
1872 "to `{0}', which is already used in a `{1}' scope to denote something else",
1876 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
1878 var pi = variable as ParametersBlock.ParameterInfo;
1880 var p = pi.Parameter;
1881 if (p is AnonymousTypeClass.GeneratedParameter) {
1882 ParametersBlock.TopBlock.Report.Error (833, p.Location, "`{0}': An anonymous type cannot have multiple properties with the same name",
1885 ParametersBlock.TopBlock.Report.Error (100, p.Location, "The parameter name `{0}' is a duplicate", p.Name);
1891 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
1892 "A local variable named `{0}' is already defined in this scope", name);
1895 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
1897 ParametersBlock.TopBlock.Report.Error (412, loc,
1898 "The type parameter name `{0}' is the same as local variable or parameter name",
1903 // It should be used by expressions which require to
1904 // register a statement during resolve process.
1906 public void AddScopeStatement (Statement s)
1908 if (scope_initializers == null)
1909 scope_initializers = new List<Statement> ();
1912 // Simple recursive helper, when resolve scope initializer another
1913 // new scope initializer can be added, this ensures it's initialized
1914 // before existing one. For now this can happen with expression trees
1915 // in base ctor initializer only
1917 if (resolving_init_idx.HasValue) {
1918 scope_initializers.Insert (resolving_init_idx.Value, s);
1919 ++resolving_init_idx;
1921 scope_initializers.Add (s);
1925 public void AddStatement (Statement s)
1930 public int AssignableSlots {
1932 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
1934 // return assignable_slots;
1938 public LabeledStatement LookupLabel (string name)
1940 return ParametersBlock.TopBlock.GetLabel (name, this);
1943 public override bool Resolve (BlockContext ec)
1945 Block prev_block = ec.CurrentBlock;
1948 ec.CurrentBlock = this;
1949 ec.StartFlowBranching (this);
1951 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
1954 // Compiler generated scope statements
1956 if (scope_initializers != null) {
1957 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
1958 scope_initializers[resolving_init_idx.Value].Resolve (ec);
1961 resolving_init_idx = null;
1965 // This flag is used to notate nested statements as unreachable from the beginning of this block.
1966 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
1967 // from the beginning of the function. The outer Resolve() that detected the unreachability is
1968 // responsible for handling the situation.
1970 int statement_count = statements.Count;
1971 for (int ix = 0; ix < statement_count; ix++){
1972 Statement s = statements [ix];
1975 // Warn if we detect unreachable code.
1978 if (s is EmptyStatement)
1981 if (!unreachable_shown && !(s is LabeledStatement)) {
1982 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
1983 unreachable_shown = true;
1986 Block c_block = s as Block;
1987 if (c_block != null)
1988 c_block.unreachable = c_block.unreachable_shown = true;
1992 // Note that we're not using ResolveUnreachable() for unreachable
1993 // statements here. ResolveUnreachable() creates a temporary
1994 // flow branching and kills it afterwards. This leads to problems
1995 // if you have two unreachable statements where the first one
1996 // assigns a variable and the second one tries to access it.
1999 if (!s.Resolve (ec)) {
2001 if (ec.IsInProbingMode)
2004 statements [ix] = new EmptyStatement (s.loc);
2008 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2009 statements [ix] = new EmptyStatement (s.loc);
2011 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2012 if (unreachable && s is LabeledStatement)
2013 throw new InternalErrorException ("should not happen");
2016 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2017 ec.CurrentBranching, statement_count);
2019 while (ec.CurrentBranching is FlowBranchingLabeled)
2020 ec.EndFlowBranching ();
2022 bool flow_unreachable = ec.EndFlowBranching ();
2024 ec.CurrentBlock = prev_block;
2026 if (flow_unreachable)
2027 flags |= Flags.HasRet;
2029 // If we're a non-static `struct' constructor which doesn't have an
2030 // initializer, then we must initialize all of the struct's fields.
2031 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2037 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2039 unreachable_shown = true;
2043 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2045 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2046 bool ok = Resolve (ec);
2047 ec.KillFlowBranching ();
2052 protected override void DoEmit (EmitContext ec)
2054 for (int ix = 0; ix < statements.Count; ix++){
2055 statements [ix].Emit (ec);
2059 public override void Emit (EmitContext ec)
2061 if (scope_initializers != null)
2062 EmitScopeInitializers (ec);
2064 ec.Mark (StartLocation);
2067 if (SymbolWriter.HasSymbolWriter)
2068 EmitSymbolInfo (ec);
2071 protected void EmitScopeInitializers (EmitContext ec)
2073 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2075 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2076 foreach (Statement s in scope_initializers)
2080 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2083 protected virtual void EmitSymbolInfo (EmitContext ec)
2088 public override string ToString ()
2090 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2094 protected override void CloneTo (CloneContext clonectx, Statement t)
2096 Block target = (Block) t;
2098 target.clone_id = clone_id_counter++;
2101 clonectx.AddBlockMap (this, target);
2103 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2104 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2107 target.Parent = clonectx.RemapBlockCopy (Parent);
2109 target.statements = new List<Statement> (statements.Count);
2110 foreach (Statement s in statements)
2111 target.statements.Add (s.Clone (clonectx));
2115 public class ExplicitBlock : Block
2117 protected AnonymousMethodStorey am_storey;
2119 public ExplicitBlock (Block parent, Location start, Location end)
2120 : this (parent, (Flags) 0, start, end)
2124 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2125 : base (parent, flags, start, end)
2127 this.Explicit = this;
2132 public AnonymousMethodStorey AnonymousMethodStorey {
2138 public bool HasCapturedThis {
2139 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2141 return (flags & Flags.HasCapturedThis) != 0;
2145 public bool HasCapturedVariable {
2146 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2148 return (flags & Flags.HasCapturedVariable) != 0;
2155 // Creates anonymous method storey in current block
2157 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2160 // An iterator has only 1 storey block
2162 if (ec.CurrentIterator != null)
2163 return ec.CurrentIterator.Storey;
2166 // When referencing a variable in iterator storey from children anonymous method
2168 if (ParametersBlock.am_storey is IteratorStorey) {
2169 return ParametersBlock.am_storey;
2172 if (am_storey == null) {
2173 MemberBase mc = ec.MemberContext as MemberBase;
2176 // Creates anonymous method storey for this block
2178 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2184 public override void Emit (EmitContext ec)
2186 if (am_storey != null) {
2187 DefineAnonymousStorey (ec);
2188 am_storey.EmitStoreyInstantiation (ec, this);
2191 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2192 if (emit_debug_info)
2197 if (emit_debug_info)
2201 void DefineAnonymousStorey (EmitContext ec)
2204 // Creates anonymous method storey
2206 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2208 // Creates parent storey reference when hoisted this is accessible
2210 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2211 ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2214 // Hoisted this exists in top-level parent storey only
2216 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2217 parent = parent.Parent.Explicit;
2219 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2222 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2224 // TODO MemberCache: Review
2225 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2228 am_storey.CreateType ();
2229 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2230 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2232 am_storey.DefineType ();
2233 am_storey.ResolveTypeParameters ();
2235 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2236 if (ref_blocks != null) {
2237 foreach (ExplicitBlock ref_block in ref_blocks) {
2238 for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2239 if (b.am_storey != null) {
2240 b.am_storey.AddParentStoreyReference (ec, am_storey);
2242 // Stop propagation inside same top block
2243 if (b.ParametersBlock.am_storey == ParametersBlock.am_storey)
2246 b = b.ParametersBlock;
2249 b.HasCapturedVariable = true;
2254 am_storey.Define ();
2255 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2258 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2260 tryBlock.statements = statements;
2261 statements = new List<Statement> (1);
2262 statements.Add (tf);
2267 // ParametersBlock was introduced to support anonymous methods
2268 // and lambda expressions
2270 public class ParametersBlock : ExplicitBlock
2272 public class ParameterInfo : INamedBlockVariable
2274 readonly ParametersBlock block;
2276 public VariableInfo VariableInfo;
2278 public ParameterInfo (ParametersBlock block, int index)
2286 public Block Block {
2292 public bool IsDeclared {
2298 public Location Location {
2300 return Parameter.Location;
2304 public Parameter Parameter {
2306 return block.Parameters [index];
2310 public TypeSpec ParameterType {
2312 return Parameter.Type;
2318 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2320 return new ParameterReference (this, loc);
2325 // Block is converted to an expression
2327 sealed class BlockScopeExpression : Expression
2330 readonly ParametersBlock block;
2332 public BlockScopeExpression (Expression child, ParametersBlock block)
2338 public override Expression CreateExpressionTree (ResolveContext ec)
2340 throw new NotSupportedException ();
2343 protected override Expression DoResolve (ResolveContext ec)
2348 child = child.Resolve (ec);
2352 eclass = child.eclass;
2357 public override void Emit (EmitContext ec)
2359 block.EmitScopeInitializers (ec);
2364 protected ParametersCompiled parameters;
2365 protected ParameterInfo[] parameter_info;
2367 protected bool unreachable;
2368 protected ToplevelBlock top_block;
2370 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2371 : base (parent, 0, start, start)
2373 if (parameters == null)
2374 throw new ArgumentNullException ("parameters");
2376 this.parameters = parameters;
2377 ParametersBlock = this;
2379 this.top_block = parent.ParametersBlock.top_block;
2380 ProcessParameters ();
2383 protected ParametersBlock (ParametersCompiled parameters, Location start)
2384 : base (null, 0, start, start)
2386 if (parameters == null)
2387 throw new ArgumentNullException ("parameters");
2389 this.parameters = parameters;
2390 ParametersBlock = this;
2393 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2394 : base (null, 0, source.StartLocation, source.EndLocation)
2396 this.parameters = parameters;
2397 this.statements = source.statements;
2398 this.scope_initializers = source.scope_initializers;
2400 this.resolved = true;
2401 this.unreachable = source.unreachable;
2402 this.am_storey = source.am_storey;
2404 ParametersBlock = this;
2410 // Block has been converted to expression tree
2412 public bool IsExpressionTree {
2414 return (flags & Flags.IsExpressionTree) != 0;
2419 // The parameters for the block.
2421 public ParametersCompiled Parameters {
2427 public ToplevelBlock TopBlock {
2433 public bool Resolved {
2442 // Check whether all `out' parameters have been assigned.
2444 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2446 if (vector.IsUnreachable)
2449 int n = parameter_info == null ? 0 : parameter_info.Length;
2451 for (int i = 0; i < n; i++) {
2452 VariableInfo var = parameter_info[i].VariableInfo;
2457 if (vector.IsAssigned (var, false))
2460 TopBlock.Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2465 public override Expression CreateExpressionTree (ResolveContext ec)
2467 if (statements.Count == 1) {
2468 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2469 if (scope_initializers != null)
2470 expr = new BlockScopeExpression (expr, this);
2475 return base.CreateExpressionTree (ec);
2478 public ParameterInfo GetParameterInfo (Parameter p)
2480 for (int i = 0; i < parameters.Count; ++i) {
2481 if (parameters[i] == p)
2482 return parameter_info[i];
2485 throw new ArgumentException ("Invalid parameter");
2488 public Expression GetParameterReference (int index, Location loc)
2490 return new ParameterReference (parameter_info[index], loc);
2493 protected void ProcessParameters ()
2495 if (parameters.Count == 0)
2498 parameter_info = new ParameterInfo[parameters.Count];
2499 for (int i = 0; i < parameter_info.Length; ++i) {
2500 var p = parameters.FixedParameters[i];
2504 // TODO: Should use Parameter only and more block there
2505 parameter_info[i] = new ParameterInfo (this, i);
2506 AddLocalName (p.Name, parameter_info[i]);
2510 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2517 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2518 flags |= Flags.IsExpressionTree;
2523 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2524 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2529 unreachable = top_level.End ();
2531 } catch (Exception e) {
2532 if (e is CompletionResult || rc.Report.IsDisabled)
2535 if (rc.CurrentBlock != null) {
2536 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2538 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2541 if (Report.DebugFlags > 0)
2545 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2546 if (rc.CurrentAnonymousMethod == null) {
2547 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2548 if (md is IteratorMethod) {
2551 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2555 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2556 rc.CurrentAnonymousMethod.GetSignatureForError ());
2564 void ResolveMeta (BlockContext ec)
2566 int orig_count = parameters.Count;
2568 for (int i = 0; i < orig_count; ++i) {
2569 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2571 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2574 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2575 parameter_info[i].VariableInfo = vi;
2576 ec.FlowOffset += vi.Length;
2580 public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
2582 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2583 pb.EndLocation = EndLocation;
2584 pb.statements = statements;
2586 var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2587 am_storey = new IteratorStorey (iterator);
2589 statements = new List<Statement> (1);
2590 AddStatement (new Return (iterator, iterator.Location));
2597 public class ToplevelBlock : ParametersBlock
2599 LocalVariable this_variable;
2600 CompilerContext compiler;
2601 Dictionary<string, object> names;
2602 Dictionary<string, object> labels;
2604 public HoistedVariable HoistedThisVariable;
2606 public Report Report {
2607 get { return compiler.Report; }
2610 public ToplevelBlock (CompilerContext ctx, Location loc)
2611 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2615 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2616 : base (parameters, start)
2618 this.compiler = ctx;
2621 ProcessParameters ();
2625 // Recreates a top level block from parameters block. Used for
2626 // compiler generated methods where the original block comes from
2627 // explicit child block. This works for already resolved blocks
2628 // only to ensure we resolve them in the correct flow order
2630 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
2631 : base (source, parameters)
2633 this.compiler = source.TopBlock.compiler;
2637 public override void AddLocalName (string name, INamedBlockVariable li)
2640 names = new Dictionary<string, object> ();
2643 if (!names.TryGetValue (name, out value)) {
2644 names.Add (name, li);
2648 INamedBlockVariable existing = value as INamedBlockVariable;
2649 List<INamedBlockVariable> existing_list;
2650 if (existing != null) {
2651 existing_list = new List<INamedBlockVariable> ();
2652 existing_list.Add (existing);
2653 names[name] = existing_list;
2655 existing_list = (List<INamedBlockVariable>) value;
2659 // A collision checking between local names
2661 for (int i = 0; i < existing_list.Count; ++i) {
2662 existing = existing_list[i];
2663 Block b = existing.Block;
2665 // Collision at same level
2666 if (li.Block == b) {
2667 li.Block.Error_AlreadyDeclared (name, li);
2671 // Collision with parent
2673 while ((b = b.Parent) != null) {
2674 if (existing.Block == b) {
2675 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
2676 i = existing_list.Count;
2681 // Collision with with children
2683 while ((b = b.Parent) != null) {
2684 if (li.Block == b) {
2685 li.Block.Error_AlreadyDeclared (name, li, "child");
2686 i = existing_list.Count;
2692 existing_list.Add (li);
2695 public void AddLabel (string name, LabeledStatement label)
2698 labels = new Dictionary<string, object> ();
2701 if (!labels.TryGetValue (name, out value)) {
2702 labels.Add (name, label);
2706 LabeledStatement existing = value as LabeledStatement;
2707 List<LabeledStatement> existing_list;
2708 if (existing != null) {
2709 existing_list = new List<LabeledStatement> ();
2710 existing_list.Add (existing);
2711 labels[name] = existing_list;
2713 existing_list = (List<LabeledStatement>) value;
2717 // A collision checking between labels
2719 for (int i = 0; i < existing_list.Count; ++i) {
2720 existing = existing_list[i];
2721 Block b = existing.Block;
2723 // Collision at same level
2724 if (label.Block == b) {
2725 Report.SymbolRelatedToPreviousError (existing.loc, name);
2726 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
2730 // Collision with parent
2732 while ((b = b.Parent) != null) {
2733 if (existing.Block == b) {
2734 Report.Error (158, label.loc,
2735 "The label `{0}' shadows another label by the same name in a contained scope", name);
2736 i = existing_list.Count;
2741 // Collision with with children
2743 while ((b = b.Parent) != null) {
2744 if (label.Block == b) {
2745 Report.Error (158, label.loc,
2746 "The label `{0}' shadows another label by the same name in a contained scope", name);
2747 i = existing_list.Count;
2753 existing_list.Add (label);
2757 // Creates an arguments set from all parameters, useful for method proxy calls
2759 public Arguments GetAllParametersArguments ()
2761 int count = parameters.Count;
2762 Arguments args = new Arguments (count);
2763 for (int i = 0; i < count; ++i) {
2764 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
2765 args.Add (new Argument (arg_expr));
2772 // Lookup inside a block, the returned value can represent 3 states
2774 // true+variable: A local name was found and it's valid
2775 // false+variable: A local name was found in a child block only
2776 // false+null: No local name was found
2778 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
2784 if (!names.TryGetValue (name, out value))
2787 variable = value as INamedBlockVariable;
2789 if (variable != null) {
2791 if (variable.Block == b.Original)
2795 } while (b != null);
2803 } while (b != null);
2805 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
2806 for (int i = 0; i < list.Count; ++i) {
2809 if (variable.Block == b.Original)
2813 } while (b != null);
2821 } while (b != null);
2831 public LabeledStatement GetLabel (string name, Block block)
2837 if (!labels.TryGetValue (name, out value)) {
2841 var label = value as LabeledStatement;
2843 if (label != null) {
2844 if (label.Block == b)
2847 // TODO: Temporary workaround for the switch block implicit label block
2848 if (label.Block.IsCompilerGenerated && label.Block.Parent == b)
2851 List<LabeledStatement> list = (List<LabeledStatement>) value;
2852 for (int i = 0; i < list.Count; ++i) {
2854 if (label.Block == b)
2857 // TODO: Temporary workaround for the switch block implicit label block
2858 if (label.Block.IsCompilerGenerated && label.Block.Parent == b)
2867 // Returns the "this" instance variable of this block.
2868 // See AddThisVariable() for more information.
2870 public LocalVariable ThisVariable {
2871 get { return this_variable; }
2875 // This is used by non-static `struct' constructors which do not have an
2876 // initializer - in this case, the constructor must initialize all of the
2877 // struct's fields. To do this, we add a "this" variable and use the flow
2878 // analysis code to ensure that it's been fully initialized before control
2879 // leaves the constructor.
2881 public LocalVariable AddThisVariable (BlockContext bc, TypeContainer ds, Location l)
2883 if (this_variable == null) {
2884 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, l);
2885 this_variable.Type = ds.CurrentType;
2886 this_variable.PrepareForFlowAnalysis (bc);
2889 return this_variable;
2892 public bool IsIterator {
2893 get { return (flags & Flags.IsIterator) != 0; }
2894 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2897 public bool IsThisAssigned (BlockContext ec)
2899 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2902 public override void Emit (EmitContext ec)
2904 if (Report.Errors > 0)
2910 if (ec.HasReturnLabel)
2911 ec.ReturnLabel = ec.DefineLabel ();
2915 ec.Mark (EndLocation);
2917 if (ec.HasReturnLabel)
2918 ec.MarkLabel (ec.ReturnLabel);
2920 if (ec.return_value != null) {
2921 ec.Emit (OpCodes.Ldloc, ec.return_value);
2922 ec.Emit (OpCodes.Ret);
2925 // If `HasReturnLabel' is set, then we already emitted a
2926 // jump to the end of the method, so we must emit a `ret'
2929 // Unfortunately, System.Reflection.Emit automatically emits
2930 // a leave to the end of a finally block. This is a problem
2931 // if no code is following the try/finally block since we may
2932 // jump to a point after the end of the method.
2933 // As a workaround, we're always creating a return label in
2937 if (ec.HasReturnLabel || !unreachable) {
2938 if (ec.ReturnType != TypeManager.void_type)
2939 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2940 ec.Emit (OpCodes.Ret);
2945 } catch (Exception e){
2946 Console.WriteLine ("Exception caught by the compiler while emitting:");
2947 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2949 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2955 protected override void EmitSymbolInfo (EmitContext ec)
2957 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2958 if ((ae != null) && (ae.Storey != null))
2959 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2961 base.EmitSymbolInfo (ec);
2965 public class SwitchLabel {
2972 Label il_label_code;
2973 bool il_label_code_set;
2975 public static readonly object NullStringCase = new object ();
2978 // if expr == null, then it is the default case.
2980 public SwitchLabel (Expression expr, Location l)
2986 public Expression Label {
2992 public Location Location {
2996 public object Converted {
3002 public Label GetILLabel (EmitContext ec)
3005 il_label = ec.DefineLabel ();
3006 il_label_set = true;
3011 public Label GetILLabelCode (EmitContext ec)
3013 if (!il_label_code_set){
3014 il_label_code = ec.DefineLabel ();
3015 il_label_code_set = true;
3017 return il_label_code;
3021 // Resolves the expression, reduces it to a literal if possible
3022 // and then converts it to the requested type.
3024 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3026 Expression e = label.Resolve (ec);
3031 Constant c = e as Constant;
3033 ec.Report.Error (150, loc, "A constant value is expected");
3037 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3038 converted = NullStringCase;
3042 if (allow_nullable && c.GetValue () == null) {
3043 converted = NullStringCase;
3047 c = c.ImplicitConversionRequired (ec, required_type, loc);
3051 converted = c.GetValue ();
3055 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3058 if (converted == null)
3060 else if (converted == NullStringCase)
3063 label = converted.ToString ();
3065 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3066 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3069 public SwitchLabel Clone (CloneContext clonectx)
3071 return new SwitchLabel (label.Clone (clonectx), loc);
3075 public class SwitchSection {
3076 public readonly List<SwitchLabel> Labels;
3077 public readonly Block Block;
3079 public SwitchSection (List<SwitchLabel> labels, Block block)
3085 public SwitchSection Clone (CloneContext clonectx)
3087 var cloned_labels = new List<SwitchLabel> ();
3089 foreach (SwitchLabel sl in cloned_labels)
3090 cloned_labels.Add (sl.Clone (clonectx));
3092 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3096 public class Switch : Statement {
3097 public List<SwitchSection> Sections;
3098 public Expression Expr;
3101 /// Maps constants whose type type SwitchType to their SwitchLabels.
3103 public IDictionary<object, SwitchLabel> Elements;
3106 /// The governing switch type
3108 public TypeSpec SwitchType;
3113 Label default_target;
3115 Expression new_expr;
3118 SwitchSection constant_section;
3119 SwitchSection default_section;
3121 ExpressionStatement string_dictionary;
3122 FieldExpr switch_cache_field;
3123 static int unique_counter;
3124 ExplicitBlock block;
3127 // Nullable Types support
3129 Nullable.Unwrap unwrap;
3131 protected bool HaveUnwrap {
3132 get { return unwrap != null; }
3136 // The types allowed to be implicitly cast from
3137 // on the governing type
3139 static TypeSpec [] allowed_types;
3141 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3149 public ExplicitBlock Block {
3155 public bool GotDefault {
3157 return default_section != null;
3161 public Label DefaultTarget {
3163 return default_target;
3168 // Determines the governing type for a switch. The returned
3169 // expression might be the expression from the switch, or an
3170 // expression that includes any potential conversions to the
3171 // integral types or to string.
3173 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3175 TypeSpec t = expr.Type;
3177 if (t == TypeManager.byte_type ||
3178 t == TypeManager.sbyte_type ||
3179 t == TypeManager.ushort_type ||
3180 t == TypeManager.short_type ||
3181 t == TypeManager.uint32_type ||
3182 t == TypeManager.int32_type ||
3183 t == TypeManager.uint64_type ||
3184 t == TypeManager.int64_type ||
3185 t == TypeManager.char_type ||
3186 t == TypeManager.string_type ||
3187 t == TypeManager.bool_type ||
3188 TypeManager.IsEnumType (t))
3191 if (allowed_types == null){
3192 allowed_types = new TypeSpec [] {
3193 TypeManager.sbyte_type,
3194 TypeManager.byte_type,
3195 TypeManager.short_type,
3196 TypeManager.ushort_type,
3197 TypeManager.int32_type,
3198 TypeManager.uint32_type,
3199 TypeManager.int64_type,
3200 TypeManager.uint64_type,
3201 TypeManager.char_type,
3202 TypeManager.string_type
3207 // Try to find a *user* defined implicit conversion.
3209 // If there is no implicit conversion, or if there are multiple
3210 // conversions, we have to report an error
3212 Expression converted = null;
3213 foreach (TypeSpec tt in allowed_types){
3216 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3221 // Ignore over-worked ImplicitUserConversions that do
3222 // an implicit conversion in addition to the user conversion.
3224 if (!(e is UserCast))
3227 if (converted != null){
3228 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3238 // Performs the basic sanity checks on the switch statement
3239 // (looks for duplicate keys and non-constant expressions).
3241 // It also returns a hashtable with the keys that we will later
3242 // use to compute the switch tables
3244 bool CheckSwitch (ResolveContext ec)
3247 Elements = new Dictionary<object, SwitchLabel> ();
3249 foreach (SwitchSection ss in Sections){
3250 foreach (SwitchLabel sl in ss.Labels){
3251 if (sl.Label == null){
3252 if (default_section != null){
3253 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3256 default_section = ss;
3260 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3265 object key = sl.Converted;
3266 if (key == SwitchLabel.NullStringCase)
3267 has_null_case = true;
3270 Elements.Add (key, sl);
3271 } catch (ArgumentException) {
3272 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3280 void EmitObjectInteger (EmitContext ec, object k)
3283 ec.EmitInt ((int) k);
3284 else if (k is Constant) {
3285 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3288 ec.EmitInt (unchecked ((int) (uint) k));
3291 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3293 ec.EmitInt ((int) (long) k);
3294 ec.Emit (OpCodes.Conv_I8);
3297 ec.EmitLong ((long) k);
3299 else if (k is ulong)
3301 ulong ul = (ulong) k;
3304 ec.EmitInt (unchecked ((int) ul));
3305 ec.Emit (OpCodes.Conv_U8);
3309 ec.EmitLong (unchecked ((long) ul));
3313 ec.EmitInt ((int) ((char) k));
3314 else if (k is sbyte)
3315 ec.EmitInt ((int) ((sbyte) k));
3317 ec.EmitInt ((int) ((byte) k));
3318 else if (k is short)
3319 ec.EmitInt ((int) ((short) k));
3320 else if (k is ushort)
3321 ec.EmitInt ((int) ((ushort) k));
3323 ec.EmitInt (((bool) k) ? 1 : 0);
3325 throw new Exception ("Unhandled case");
3328 // structure used to hold blocks of keys while calculating table switch
3329 class KeyBlock : IComparable
3331 public KeyBlock (long _first)
3333 first = last = _first;
3337 public List<object> element_keys;
3338 // how many items are in the bucket
3339 public int Size = 1;
3342 get { return (int) (last - first + 1); }
3344 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3346 return kb_last.last - kb_first.first + 1;
3348 public int CompareTo (object obj)
3350 KeyBlock kb = (KeyBlock) obj;
3351 int nLength = Length;
3352 int nLengthOther = kb.Length;
3353 if (nLengthOther == nLength)
3354 return (int) (kb.first - first);
3355 return nLength - nLengthOther;
3360 /// This method emits code for a lookup-based switch statement (non-string)
3361 /// Basically it groups the cases into blocks that are at least half full,
3362 /// and then spits out individual lookup opcodes for each block.
3363 /// It emits the longest blocks first, and short blocks are just
3364 /// handled with direct compares.
3366 /// <param name="ec"></param>
3367 /// <param name="val"></param>
3368 /// <returns></returns>
3369 void TableSwitchEmit (EmitContext ec, Expression val)
3371 int element_count = Elements.Count;
3372 object [] element_keys = new object [element_count];
3373 Elements.Keys.CopyTo (element_keys, 0);
3374 Array.Sort (element_keys);
3376 // initialize the block list with one element per key
3377 var key_blocks = new List<KeyBlock> (element_count);
3378 foreach (object key in element_keys)
3379 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3381 KeyBlock current_kb;
3382 // iteratively merge the blocks while they are at least half full
3383 // there's probably a really cool way to do this with a tree...
3384 while (key_blocks.Count > 1)
3386 var key_blocks_new = new List<KeyBlock> ();
3387 current_kb = (KeyBlock) key_blocks [0];
3388 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3390 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3391 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3394 current_kb.last = kb.last;
3395 current_kb.Size += kb.Size;
3399 // start a new block
3400 key_blocks_new.Add (current_kb);
3404 key_blocks_new.Add (current_kb);
3405 if (key_blocks.Count == key_blocks_new.Count)
3407 key_blocks = key_blocks_new;
3410 // initialize the key lists
3411 foreach (KeyBlock kb in key_blocks)
3412 kb.element_keys = new List<object> ();
3414 // fill the key lists
3416 if (key_blocks.Count > 0) {
3417 current_kb = (KeyBlock) key_blocks [0];
3418 foreach (object key in element_keys)
3420 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3421 System.Convert.ToInt64 (key) > current_kb.last;
3423 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3424 current_kb.element_keys.Add (key);
3428 // sort the blocks so we can tackle the largest ones first
3431 // okay now we can start...
3432 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3433 Label lbl_default = default_target;
3435 Type type_keys = null;
3436 if (element_keys.Length > 0)
3437 type_keys = element_keys [0].GetType (); // used for conversions
3439 TypeSpec compare_type;
3441 if (TypeManager.IsEnumType (SwitchType))
3442 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3444 compare_type = SwitchType;
3446 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3448 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3449 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3452 foreach (object key in kb.element_keys) {
3453 SwitchLabel sl = (SwitchLabel) Elements [key];
3454 if (key is int && (int) key == 0) {
3455 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3458 EmitObjectInteger (ec, key);
3459 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3465 // TODO: if all the keys in the block are the same and there are
3466 // no gaps/defaults then just use a range-check.
3467 if (compare_type == TypeManager.int64_type ||
3468 compare_type == TypeManager.uint64_type)
3470 // TODO: optimize constant/I4 cases
3472 // check block range (could be > 2^31)
3474 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3475 ec.Emit (OpCodes.Blt, lbl_default);
3477 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3478 ec.Emit (OpCodes.Bgt, lbl_default);
3484 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3485 ec.Emit (OpCodes.Sub);
3487 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3493 int first = (int) kb.first;
3497 ec.Emit (OpCodes.Sub);
3501 ec.EmitInt (-first);
3502 ec.Emit (OpCodes.Add);
3506 // first, build the list of labels for the switch
3508 int cJumps = kb.Length;
3509 Label [] switch_labels = new Label [cJumps];
3510 for (int iJump = 0; iJump < cJumps; iJump++)
3512 object key = kb.element_keys [iKey];
3513 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3515 SwitchLabel sl = (SwitchLabel) Elements [key];
3516 switch_labels [iJump] = sl.GetILLabel (ec);
3520 switch_labels [iJump] = lbl_default;
3522 // emit the switch opcode
3523 ec.Emit (OpCodes.Switch, switch_labels);
3526 // mark the default for this block
3528 ec.MarkLabel (lbl_default);
3531 // TODO: find the default case and emit it here,
3532 // to prevent having to do the following jump.
3533 // make sure to mark other labels in the default section
3535 // the last default just goes to the end
3536 if (element_keys.Length > 0)
3537 ec.Emit (OpCodes.Br, lbl_default);
3539 // now emit the code for the sections
3540 bool found_default = false;
3542 foreach (SwitchSection ss in Sections) {
3543 foreach (SwitchLabel sl in ss.Labels) {
3544 if (sl.Converted == SwitchLabel.NullStringCase) {
3545 ec.MarkLabel (null_target);
3546 } else if (sl.Label == null) {
3547 ec.MarkLabel (lbl_default);
3548 found_default = true;
3550 ec.MarkLabel (null_target);
3552 ec.MarkLabel (sl.GetILLabel (ec));
3553 ec.MarkLabel (sl.GetILLabelCode (ec));
3558 if (!found_default) {
3559 ec.MarkLabel (lbl_default);
3560 if (!has_null_case) {
3561 ec.MarkLabel (null_target);
3565 ec.MarkLabel (lbl_end);
3568 SwitchSection FindSection (SwitchLabel label)
3570 foreach (SwitchSection ss in Sections){
3571 foreach (SwitchLabel sl in ss.Labels){
3580 public static void Reset ()
3583 allowed_types = null;
3586 public override bool Resolve (BlockContext ec)
3588 Expr = Expr.Resolve (ec);
3592 new_expr = SwitchGoverningType (ec, Expr);
3594 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3595 unwrap = Nullable.Unwrap.Create (Expr, false);
3599 new_expr = SwitchGoverningType (ec, unwrap);
3602 if (new_expr == null){
3603 ec.Report.Error (151, loc,
3604 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3605 TypeManager.CSharpName (Expr.Type));
3610 SwitchType = new_expr.Type;
3612 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3613 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3617 if (!CheckSwitch (ec))
3621 Elements.Remove (SwitchLabel.NullStringCase);
3623 Switch old_switch = ec.Switch;
3625 ec.Switch.SwitchType = SwitchType;
3627 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3628 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3630 var constant = new_expr as Constant;
3631 if (constant != null) {
3633 object key = constant.GetValue ();
3635 if (Elements.TryGetValue (key, out label))
3636 constant_section = FindSection (label);
3638 if (constant_section == null)
3639 constant_section = default_section;
3644 foreach (SwitchSection ss in Sections){
3646 ec.CurrentBranching.CreateSibling (
3647 null, FlowBranching.SiblingType.SwitchSection);
3651 if (is_constant && (ss != constant_section)) {
3652 // If we're a constant switch, we're only emitting
3653 // one single section - mark all the others as
3655 ec.CurrentBranching.CurrentUsageVector.Goto ();
3656 if (!ss.Block.ResolveUnreachable (ec, true)) {
3660 if (!ss.Block.Resolve (ec))
3665 if (default_section == null)
3666 ec.CurrentBranching.CreateSibling (
3667 null, FlowBranching.SiblingType.SwitchSection);
3669 ec.EndFlowBranching ();
3670 ec.Switch = old_switch;
3672 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3677 if (SwitchType == TypeManager.string_type && !is_constant) {
3678 // TODO: Optimize single case, and single+default case
3679 ResolveStringSwitchMap (ec);
3685 void ResolveStringSwitchMap (ResolveContext ec)
3687 FullNamedExpression string_dictionary_type;
3688 if (TypeManager.generic_ienumerable_type != null) {
3689 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3690 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3692 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3694 new TypeExpression (TypeManager.string_type, loc),
3695 new TypeExpression (TypeManager.int32_type, loc)), loc);
3697 MemberAccess system_collections_generic = new MemberAccess (
3698 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3700 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3703 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3704 Field field = new Field (ctype, string_dictionary_type,
3705 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3706 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3707 if (!field.Define ())
3709 ctype.AddField (field);
3711 var init = new List<Expression> ();
3714 string value = null;
3715 foreach (SwitchSection section in Sections) {
3716 int last_count = init.Count;
3717 foreach (SwitchLabel sl in section.Labels) {
3718 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3721 value = (string) sl.Converted;
3722 var init_args = new List<Expression> (2);
3723 init_args.Add (new StringLiteral (value, sl.Location));
3724 init_args.Add (new IntConstant (counter, loc));
3725 init.Add (new CollectionElementInitializer (init_args, loc));
3729 // Don't add empty sections
3731 if (last_count == init.Count)
3734 Elements.Add (counter, section.Labels [0]);
3738 Arguments args = new Arguments (1);
3739 args.Add (new Argument (new IntConstant (init.Count, loc)));
3740 Expression initializer = new NewInitialize (string_dictionary_type, args,
3741 new CollectionOrObjectInitializers (init, loc), loc);
3743 switch_cache_field = new FieldExpr (field, loc);
3744 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3747 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3749 Label l_initialized = ec.DefineLabel ();
3752 // Skip initialization when value is null
3754 value.EmitBranchable (ec, null_target, false);
3757 // Check if string dictionary is initialized and initialize
3759 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3760 string_dictionary.EmitStatement (ec);
3761 ec.MarkLabel (l_initialized);
3763 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3765 ResolveContext rc = new ResolveContext (ec.MemberContext);
3767 if (TypeManager.generic_ienumerable_type != null) {
3768 Arguments get_value_args = new Arguments (2);
3769 get_value_args.Add (new Argument (value));
3770 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3771 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3772 if (get_item == null)
3776 // A value was not found, go to default case
3778 get_item.EmitBranchable (ec, default_target, false);
3780 Arguments get_value_args = new Arguments (1);
3781 get_value_args.Add (new Argument (value));
3783 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3784 if (get_item == null)
3787 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3788 get_item_object.EmitAssign (ec, get_item, true, false);
3789 ec.Emit (OpCodes.Brfalse, default_target);
3791 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3792 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3794 get_item_int.EmitStatement (ec);
3795 get_item_object.Release (ec);
3798 TableSwitchEmit (ec, string_switch_variable);
3799 string_switch_variable.Release (ec);
3802 protected override void DoEmit (EmitContext ec)
3805 // Needed to emit anonymous storey initialization
3806 // Otherwise it does not contain any statements for now
3810 default_target = ec.DefineLabel ();
3811 null_target = ec.DefineLabel ();
3813 // Store variable for comparission purposes
3814 // TODO: Don't duplicate non-captured VariableReference
3815 LocalTemporary value;
3817 value = new LocalTemporary (SwitchType);
3818 unwrap.EmitCheck (ec);
3819 ec.Emit (OpCodes.Brfalse, null_target);
3822 } else if (!is_constant) {
3823 value = new LocalTemporary (SwitchType);
3830 // Setup the codegen context
3832 Label old_end = ec.LoopEnd;
3833 Switch old_switch = ec.Switch;
3835 ec.LoopEnd = ec.DefineLabel ();
3840 if (constant_section != null)
3841 constant_section.Block.Emit (ec);
3842 } else if (string_dictionary != null) {
3843 DoEmitStringSwitch (value, ec);
3845 TableSwitchEmit (ec, value);
3851 // Restore context state.
3852 ec.MarkLabel (ec.LoopEnd);
3855 // Restore the previous context
3857 ec.LoopEnd = old_end;
3858 ec.Switch = old_switch;
3861 protected override void CloneTo (CloneContext clonectx, Statement t)
3863 Switch target = (Switch) t;
3865 target.Expr = Expr.Clone (clonectx);
3866 target.Sections = new List<SwitchSection> ();
3867 foreach (SwitchSection ss in Sections){
3868 target.Sections.Add (ss.Clone (clonectx));
3873 // A place where execution can restart in an iterator
3874 public abstract class ResumableStatement : Statement
3877 protected Label resume_point;
3879 public Label PrepareForEmit (EmitContext ec)
3883 resume_point = ec.DefineLabel ();
3885 return resume_point;
3888 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3892 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3897 // Base class for statements that are implemented in terms of try...finally
3898 public abstract class ExceptionStatement : ResumableStatement
3902 List<ResumableStatement> resume_points;
3903 int first_resume_pc;
3904 protected Statement stmt;
3905 Label dispose_try_block;
3906 bool prepared_for_dispose, emitted_dispose;
3908 protected ExceptionStatement (Statement stmt, Location loc)
3916 public Statement Statement {
3924 protected abstract void EmitPreTryBody (EmitContext ec);
3925 protected abstract void EmitTryBody (EmitContext ec);
3926 protected abstract void EmitFinallyBody (EmitContext ec);
3928 protected sealed override void DoEmit (EmitContext ec)
3930 EmitPreTryBody (ec);
3932 if (resume_points != null) {
3933 ec.EmitInt ((int) Iterator.State.Running);
3934 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3937 ec.BeginExceptionBlock ();
3939 if (resume_points != null) {
3940 ec.MarkLabel (resume_point);
3942 // For normal control flow, we want to fall-through the Switch
3943 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3944 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3945 ec.EmitInt (first_resume_pc);
3946 ec.Emit (OpCodes.Sub);
3948 Label [] labels = new Label [resume_points.Count];
3949 for (int i = 0; i < resume_points.Count; ++i)
3950 labels [i] = resume_points [i].PrepareForEmit (ec);
3951 ec.Emit (OpCodes.Switch, labels);
3956 ec.BeginFinallyBlock ();
3958 Label start_finally = ec.DefineLabel ();
3959 if (resume_points != null) {
3960 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3961 ec.Emit (OpCodes.Brfalse_S, start_finally);
3962 ec.Emit (OpCodes.Endfinally);
3965 ec.MarkLabel (start_finally);
3966 EmitFinallyBody (ec);
3968 ec.EndExceptionBlock ();
3971 public void SomeCodeFollows ()
3973 code_follows = true;
3976 public override bool Resolve (BlockContext ec)
3978 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3979 // So, ensure there's some IL code after this statement.
3980 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3981 ec.NeedReturnLabel ();
3983 iter = ec.CurrentIterator;
3987 public void AddResumePoint (ResumableStatement stmt, int pc)
3989 if (resume_points == null) {
3990 resume_points = new List<ResumableStatement> ();
3991 first_resume_pc = pc;
3994 if (pc != first_resume_pc + resume_points.Count)
3995 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3997 resume_points.Add (stmt);
4000 public override Label PrepareForDispose (EmitContext ec, Label end)
4002 if (!prepared_for_dispose) {
4003 prepared_for_dispose = true;
4004 dispose_try_block = ec.DefineLabel ();
4006 return dispose_try_block;
4009 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4011 if (emitted_dispose)
4014 emitted_dispose = true;
4016 Label end_of_try = ec.DefineLabel ();
4018 // Ensure that the only way we can get into this code is through a dispatcher
4019 if (have_dispatcher)
4020 ec.Emit (OpCodes.Br, end);
4022 ec.BeginExceptionBlock ();
4024 ec.MarkLabel (dispose_try_block);
4026 Label [] labels = null;
4027 for (int i = 0; i < resume_points.Count; ++i) {
4028 ResumableStatement s = (ResumableStatement) resume_points [i];
4029 Label ret = s.PrepareForDispose (ec, end_of_try);
4030 if (ret.Equals (end_of_try) && labels == null)
4032 if (labels == null) {
4033 labels = new Label [resume_points.Count];
4034 for (int j = 0; j < i; ++j)
4035 labels [j] = end_of_try;
4040 if (labels != null) {
4042 for (j = 1; j < labels.Length; ++j)
4043 if (!labels [0].Equals (labels [j]))
4045 bool emit_dispatcher = j < labels.Length;
4047 if (emit_dispatcher) {
4048 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4049 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4050 ec.EmitInt (first_resume_pc);
4051 ec.Emit (OpCodes.Sub);
4052 ec.Emit (OpCodes.Switch, labels);
4053 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4056 foreach (ResumableStatement s in resume_points)
4057 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4060 ec.MarkLabel (end_of_try);
4062 ec.BeginFinallyBlock ();
4064 EmitFinallyBody (ec);
4066 ec.EndExceptionBlock ();
4070 public class Lock : ExceptionStatement {
4072 TemporaryVariableReference temp;
4074 public Lock (Expression expr, Statement stmt, Location loc)
4080 public override bool Resolve (BlockContext ec)
4082 expr = expr.Resolve (ec);
4086 if (!TypeManager.IsReferenceType (expr.Type)){
4087 ec.Report.Error (185, loc,
4088 "`{0}' is not a reference type as required by the lock statement",
4089 TypeManager.CSharpName (expr.Type));
4093 ec.StartFlowBranching (this);
4094 bool ok = Statement.Resolve (ec);
4095 ec.EndFlowBranching ();
4097 ok &= base.Resolve (ec);
4099 temp = TemporaryVariableReference.Create (expr.Type, ec.CurrentBlock.Parent, loc);
4102 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4103 TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4104 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4105 monitor_type, "Enter", loc, TypeManager.object_type);
4106 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4107 monitor_type, "Exit", loc, TypeManager.object_type);
4113 protected override void EmitPreTryBody (EmitContext ec)
4115 temp.EmitAssign (ec, expr);
4117 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4120 protected override void EmitTryBody (EmitContext ec)
4122 Statement.Emit (ec);
4125 protected override void EmitFinallyBody (EmitContext ec)
4128 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4131 protected override void CloneTo (CloneContext clonectx, Statement t)
4133 Lock target = (Lock) t;
4135 target.expr = expr.Clone (clonectx);
4136 target.stmt = Statement.Clone (clonectx);
4140 public class Unchecked : Statement {
4143 public Unchecked (Block b, Location loc)
4150 public override bool Resolve (BlockContext ec)
4152 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4153 return Block.Resolve (ec);
4156 protected override void DoEmit (EmitContext ec)
4158 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4162 protected override void CloneTo (CloneContext clonectx, Statement t)
4164 Unchecked target = (Unchecked) t;
4166 target.Block = clonectx.LookupBlock (Block);
4170 public class Checked : Statement {
4173 public Checked (Block b, Location loc)
4176 b.Unchecked = false;
4180 public override bool Resolve (BlockContext ec)
4182 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4183 return Block.Resolve (ec);
4186 protected override void DoEmit (EmitContext ec)
4188 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4192 protected override void CloneTo (CloneContext clonectx, Statement t)
4194 Checked target = (Checked) t;
4196 target.Block = clonectx.LookupBlock (Block);
4200 public class Unsafe : Statement {
4203 public Unsafe (Block b, Location loc)
4206 Block.Unsafe = true;
4210 public override bool Resolve (BlockContext ec)
4212 if (ec.CurrentIterator != null)
4213 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4215 using (ec.Set (ResolveContext.Options.UnsafeScope))
4216 return Block.Resolve (ec);
4219 protected override void DoEmit (EmitContext ec)
4224 protected override void CloneTo (CloneContext clonectx, Statement t)
4226 Unsafe target = (Unsafe) t;
4228 target.Block = clonectx.LookupBlock (Block);
4235 public class Fixed : Statement
4237 abstract class Emitter : ShimExpression
4239 protected LocalVariable vi;
4241 protected Emitter (Expression expr, LocalVariable li)
4247 public abstract void EmitExit (EmitContext ec);
4250 class ExpressionEmitter : Emitter {
4251 public ExpressionEmitter (Expression converted, LocalVariable li) :
4252 base (converted, li)
4256 protected override Expression DoResolve (ResolveContext rc)
4258 throw new NotImplementedException ();
4261 public override void Emit (EmitContext ec) {
4263 // Store pointer in pinned location
4269 public override void EmitExit (EmitContext ec)
4271 ec.Emit (OpCodes.Ldc_I4_0);
4272 ec.Emit (OpCodes.Conv_U);
4277 class StringEmitter : Emitter
4279 LocalVariable pinned_string;
4281 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4286 protected override Expression DoResolve (ResolveContext rc)
4288 pinned_string = new LocalVariable (vi.Block, "$pinned",
4289 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4292 pinned_string.Type = TypeManager.string_type;
4294 if (TypeManager.int_get_offset_to_string_data == null) {
4295 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4296 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4299 eclass = ExprClass.Variable;
4300 type = TypeManager.int32_type;
4304 public override void Emit (EmitContext ec)
4306 pinned_string.CreateBuilder (ec);
4309 pinned_string.EmitAssign (ec);
4311 // TODO: Should use Binary::Add
4312 pinned_string.Emit (ec);
4313 ec.Emit (OpCodes.Conv_I);
4315 PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4316 //pe.InstanceExpression = pinned_string;
4317 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4319 ec.Emit (OpCodes.Add);
4323 public override void EmitExit (EmitContext ec)
4325 ec.Emit (OpCodes.Ldnull);
4326 pinned_string.EmitAssign (ec);
4330 public class VariableDeclaration : BlockVariableDeclaration
4332 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4337 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4339 if (!Variable.Type.IsPointer && li == Variable) {
4340 bc.Report.Error (209, TypeExpression.Location,
4341 "The type of locals declared in a fixed statement must be a pointer type");
4346 // The rules for the possible declarators are pretty wise,
4347 // but the production on the grammar is more concise.
4349 // So we have to enforce these rules here.
4351 // We do not resolve before doing the case 1 test,
4352 // because the grammar is explicit in that the token &
4353 // is present, so we need to test for this particular case.
4356 if (initializer is Cast) {
4357 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4361 initializer = initializer.Resolve (bc);
4363 if (initializer == null)
4369 if (initializer.Type.IsArray) {
4370 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4373 // Provided that array_type is unmanaged,
4375 if (!TypeManager.VerifyUnmanaged (bc.Compiler, array_type, loc))
4379 // and T* is implicitly convertible to the
4380 // pointer type given in the fixed statement.
4382 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4384 Expression converted = Convert.ImplicitConversionRequired (
4385 bc, array_ptr, li.Type, loc);
4386 if (converted == null)
4390 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4392 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4393 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4394 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (0, loc), loc), loc)),
4395 new NullPointer (loc),
4398 converted = converted.Resolve (bc);
4400 return new ExpressionEmitter (converted, li);
4406 if (initializer.Type == TypeManager.string_type) {
4407 return new StringEmitter (initializer, li, loc).Resolve (bc);
4410 // Case 3: fixed buffer
4411 if (initializer is FixedBufferPtr) {
4412 return new ExpressionEmitter (initializer, li);
4416 // Case 4: & object.
4418 bool already_fixed = true;
4419 Unary u = initializer as Unary;
4420 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4421 IVariableReference vr = u.Expr as IVariableReference;
4422 if (vr == null || !vr.IsFixed) {
4423 already_fixed = false;
4427 if (already_fixed) {
4428 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4431 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4432 return new ExpressionEmitter (initializer, li);
4437 VariableDeclaration decl;
4438 Statement statement;
4441 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4450 public Statement Statement {
4456 public BlockVariableDeclaration Variables {
4464 public override bool Resolve (BlockContext ec)
4466 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4467 if (!decl.Resolve (ec))
4471 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4472 bool ok = statement.Resolve (ec);
4473 bool flow_unreachable = ec.EndFlowBranching ();
4474 has_ret = flow_unreachable;
4479 protected override void DoEmit (EmitContext ec)
4481 decl.Variable.CreateBuilder (ec);
4482 decl.Initializer.Emit (ec);
4483 if (decl.Declarators != null) {
4484 foreach (var d in decl.Declarators) {
4485 d.Variable.CreateBuilder (ec);
4486 d.Initializer.Emit (ec);
4490 statement.Emit (ec);
4496 // Clear the pinned variable
4498 ((Emitter) decl.Initializer).EmitExit (ec);
4499 if (decl.Declarators != null) {
4500 foreach (var d in decl.Declarators) {
4501 ((Emitter)d.Initializer).EmitExit (ec);
4506 protected override void CloneTo (CloneContext clonectx, Statement t)
4508 Fixed target = (Fixed) t;
4510 target.decl = (VariableDeclaration) decl.Clone (clonectx);
4511 target.statement = statement.Clone (clonectx);
4515 public class Catch : Statement
4519 FullNamedExpression type_expr;
4520 CompilerAssign assign;
4523 public Catch (Block block, Location loc)
4531 public Block Block {
4537 public TypeSpec CatchType {
4543 public bool IsGeneral {
4545 return type_expr == null;
4549 public FullNamedExpression TypeExpression {
4558 public LocalVariable Variable {
4569 protected override void DoEmit (EmitContext ec)
4572 ec.BeginCatchBlock (TypeManager.object_type);
4574 ec.BeginCatchBlock (CatchType);
4577 li.CreateBuilder (ec);
4580 // Special case hoisted catch variable, we have to use a temporary variable
4581 // to pass via anonymous storey initialization with the value still on top
4584 if (li.HoistedVariant != null) {
4585 LocalTemporary lt = new LocalTemporary (li.Type);
4586 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4588 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4590 // switch to assigning from the temporary variable and not from top of the stack
4591 assign.UpdateSource (lt);
4594 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4595 ec.Emit (OpCodes.Pop);
4596 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4602 public override bool Resolve (BlockContext ec)
4604 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4605 if (type_expr != null) {
4606 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4611 if (type != TypeManager.exception_type && !TypeSpec.IsBaseClass (type, TypeManager.exception_type, false)) {
4612 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4613 } else if (li != null) {
4615 li.PrepareForFlowAnalysis (ec);
4617 // source variable is at the top of the stack
4618 Expression source = new EmptyExpression (li.Type);
4619 if (li.Type.IsGenericParameter)
4620 source = new UnboxCast (source, li.Type);
4622 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
4623 Block.AddScopeStatement (new StatementExpression (assign));
4627 return Block.Resolve (ec);
4631 protected override void CloneTo (CloneContext clonectx, Statement t)
4633 Catch target = (Catch) t;
4635 if (type_expr != null)
4636 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
4638 target.block = clonectx.LookupBlock (block);
4642 public class TryFinally : ExceptionStatement {
4645 public TryFinally (Statement stmt, Block fini, Location loc)
4651 public override bool Resolve (BlockContext ec)
4655 ec.StartFlowBranching (this);
4657 if (!stmt.Resolve (ec))
4661 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4662 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4663 if (!fini.Resolve (ec))
4667 ec.EndFlowBranching ();
4669 ok &= base.Resolve (ec);
4674 protected override void EmitPreTryBody (EmitContext ec)
4678 protected override void EmitTryBody (EmitContext ec)
4683 protected override void EmitFinallyBody (EmitContext ec)
4688 protected override void CloneTo (CloneContext clonectx, Statement t)
4690 TryFinally target = (TryFinally) t;
4692 target.stmt = (Statement) stmt.Clone (clonectx);
4694 target.fini = clonectx.LookupBlock (fini);
4698 public class TryCatch : Statement {
4700 public List<Catch> Specific;
4701 public Catch General;
4702 bool inside_try_finally, code_follows;
4704 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4707 this.Specific = catch_clauses;
4708 this.inside_try_finally = inside_try_finally;
4710 Catch c = catch_clauses [0];
4713 catch_clauses.RemoveAt (0);
4719 public override bool Resolve (BlockContext ec)
4723 ec.StartFlowBranching (this);
4725 if (!Block.Resolve (ec))
4728 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4730 foreach (Catch c in Specific){
4731 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4733 if (!c.Resolve (ec)) {
4738 TypeSpec resolved_type = c.CatchType;
4739 for (int ii = 0; ii < last_index; ++ii) {
4740 if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4741 ec.Report.Error (160, c.loc,
4742 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4743 TypeManager.CSharpName (prev_catches [ii]));
4748 prev_catches [last_index++] = resolved_type;
4751 if (General != null) {
4752 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4753 foreach (Catch c in Specific){
4754 if (c.CatchType == TypeManager.exception_type && ec.Compiler.PredefinedAttributes.RuntimeCompatibility.IsDefined) {
4755 ec.Report.Warning (1058, 1, c.loc,
4756 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4761 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4763 if (!General.Resolve (ec))
4767 ec.EndFlowBranching ();
4769 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4770 // So, ensure there's some IL code after this statement
4771 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4772 ec.NeedReturnLabel ();
4777 public void SomeCodeFollows ()
4779 code_follows = true;
4782 protected override void DoEmit (EmitContext ec)
4784 if (!inside_try_finally)
4785 ec.BeginExceptionBlock ();
4789 foreach (Catch c in Specific)
4792 if (General != null)
4795 if (!inside_try_finally)
4796 ec.EndExceptionBlock ();
4799 protected override void CloneTo (CloneContext clonectx, Statement t)
4801 TryCatch target = (TryCatch) t;
4803 target.Block = clonectx.LookupBlock (Block);
4804 if (General != null)
4805 target.General = (Catch) General.Clone (clonectx);
4806 if (Specific != null){
4807 target.Specific = new List<Catch> ();
4808 foreach (Catch c in Specific)
4809 target.Specific.Add ((Catch) c.Clone (clonectx));
4814 public class Using : ExceptionStatement
4816 public class VariableDeclaration : BlockVariableDeclaration
4818 Statement dispose_call;
4820 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4825 public VariableDeclaration (LocalVariable li, Location loc)
4831 public VariableDeclaration (Expression expr)
4834 loc = expr.Location;
4840 public bool IsNested { get; private set; }
4844 public void EmitDispose (EmitContext ec)
4846 dispose_call.Emit (ec);
4849 public override bool Resolve (BlockContext bc)
4854 return base.Resolve (bc);
4857 public void ResolveExpression (BlockContext bc)
4859 var e = Initializer.Resolve (bc);
4863 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
4864 Initializer = ResolveInitializer (bc, Variable, e);
4867 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4869 if (li.Type == InternalType.Dynamic) {
4870 initializer = initializer.Resolve (bc);
4871 if (initializer == null)
4874 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
4875 Arguments args = new Arguments (1);
4876 args.Add (new Argument (initializer));
4877 initializer = new DynamicConversion (TypeManager.idisposable_type, 0, args, initializer.Location).Resolve (bc);
4878 if (initializer == null)
4881 var var = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
4882 dispose_call = CreateDisposeCall (bc, var);
4883 dispose_call.Resolve (bc);
4885 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
4888 if (li == Variable) {
4889 CheckIDiposableConversion (bc, li, initializer);
4890 dispose_call = CreateDisposeCall (bc, li);
4891 dispose_call.Resolve (bc);
4894 return base.ResolveInitializer (bc, li, initializer);
4897 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
4901 if (type != TypeManager.idisposable_type && !type.ImplementsInterface (TypeManager.idisposable_type, false)) {
4902 if (TypeManager.IsNullableType (type)) {
4903 // it's handled in CreateDisposeCall
4907 bc.Report.SymbolRelatedToPreviousError (type);
4908 var loc = type_expr == null ? initializer.Location : type_expr.Location;
4909 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4910 type.GetSignatureForError ());
4916 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
4918 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
4920 var loc = lv.Location;
4922 if (TypeManager.void_dispose_void == null) {
4923 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4924 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4927 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
4928 dispose_mg.InstanceExpression = TypeManager.IsNullableType (type) ?
4929 new Cast (new TypeExpression (TypeManager.idisposable_type, loc), lvr, loc).Resolve (bc) :
4932 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
4934 // Add conditional call when disposing possible null variable
4935 if (!type.IsStruct || TypeManager.IsNullableType (type))
4936 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
4941 public Statement RewriteForDeclarators (BlockContext bc, Statement stmt)
4943 for (int i = declarators.Count - 1; i >= 0; --i) {
4944 var d = declarators [i];
4945 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
4946 vd.Initializer = d.Initializer;
4948 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
4949 vd.dispose_call.Resolve (bc);
4951 stmt = new Using (vd, stmt, d.Variable.Location);
4959 VariableDeclaration decl;
4961 public Using (VariableDeclaration decl, Statement stmt, Location loc)
4967 public Using (Expression expr, Statement stmt, Location loc)
4970 this.decl = new VariableDeclaration (expr);
4975 public Expression Expression {
4977 return decl.Variable == null ? decl.Initializer : null;
4981 public BlockVariableDeclaration Variables {
4989 protected override void EmitPreTryBody (EmitContext ec)
4994 protected override void EmitTryBody (EmitContext ec)
4999 protected override void EmitFinallyBody (EmitContext ec)
5001 decl.EmitDispose (ec);
5004 public override bool Resolve (BlockContext ec)
5006 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5007 if (decl.Variable == null) {
5008 decl.ResolveExpression (ec);
5010 if (!decl.Resolve (ec))
5013 if (decl.Declarators != null) {
5014 stmt = decl.RewriteForDeclarators (ec, stmt);
5019 ec.StartFlowBranching (this);
5021 bool ok = stmt.Resolve (ec);
5023 ec.EndFlowBranching ();
5025 ok &= base.Resolve (ec);
5030 protected override void CloneTo (CloneContext clonectx, Statement t)
5032 Using target = (Using) t;
5034 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5035 target.stmt = stmt.Clone (clonectx);
5040 /// Implementation of the foreach C# statement
5042 public class Foreach : Statement {
5044 sealed class ArrayForeach : Statement
5046 readonly Foreach for_each;
5047 readonly Statement statement;
5050 TemporaryVariableReference[] lengths;
5051 Expression [] length_exprs;
5052 StatementExpression[] counter;
5053 TemporaryVariableReference[] variables;
5055 TemporaryVariableReference copy;
5057 LocalVariableReference variable;
5059 public ArrayForeach (Foreach @foreach, int rank)
5061 for_each = @foreach;
5062 statement = for_each.statement;
5064 variable = new LocalVariableReference (for_each.variable, loc);
5066 counter = new StatementExpression[rank];
5067 variables = new TemporaryVariableReference[rank];
5068 length_exprs = new Expression [rank];
5071 // Only use temporary length variables when dealing with
5072 // multi-dimensional arrays
5075 lengths = new TemporaryVariableReference [rank];
5078 protected override void CloneTo (CloneContext clonectx, Statement target)
5080 throw new NotImplementedException ();
5083 public override bool Resolve (BlockContext ec)
5085 Block variables_block = variable.local_info.Block;
5086 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5089 int rank = length_exprs.Length;
5090 Arguments list = new Arguments (rank);
5091 for (int i = 0; i < rank; i++) {
5092 var v = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5094 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5095 counter[i].Resolve (ec);
5098 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5100 lengths[i] = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5101 lengths[i].Resolve (ec);
5103 Arguments args = new Arguments (1);
5104 args.Add (new Argument (new IntConstant (i, loc)));
5105 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5108 list.Add (new Argument (v));
5111 access = new ElementAccess (copy, list, loc).Resolve (ec);
5115 Expression var_type = for_each.type;
5116 VarExpr ve = var_type as VarExpr;
5118 // Infer implicitly typed local variable from foreach array type
5119 var_type = new TypeExpression (access.Type, ve.Location);
5122 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5123 if (var_type == null)
5126 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5132 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5133 ec.CurrentBranching.CreateSibling ();
5135 variable.local_info.Type = conv.Type;
5136 variable.Resolve (ec);
5138 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5139 if (!statement.Resolve (ec))
5141 ec.EndFlowBranching ();
5143 // There's no direct control flow from the end of the embedded statement to the end of the loop
5144 ec.CurrentBranching.CurrentUsageVector.Goto ();
5146 ec.EndFlowBranching ();
5151 protected override void DoEmit (EmitContext ec)
5153 copy.EmitAssign (ec, for_each.expr);
5155 int rank = length_exprs.Length;
5156 Label[] test = new Label [rank];
5157 Label[] loop = new Label [rank];
5159 for (int i = 0; i < rank; i++) {
5160 test [i] = ec.DefineLabel ();
5161 loop [i] = ec.DefineLabel ();
5163 if (lengths != null)
5164 lengths [i].EmitAssign (ec, length_exprs [i]);
5167 IntConstant zero = new IntConstant (0, loc);
5168 for (int i = 0; i < rank; i++) {
5169 variables [i].EmitAssign (ec, zero);
5171 ec.Emit (OpCodes.Br, test [i]);
5172 ec.MarkLabel (loop [i]);
5175 variable.local_info.CreateBuilder (ec);
5176 variable.EmitAssign (ec, conv, false, false);
5178 statement.Emit (ec);
5180 ec.MarkLabel (ec.LoopBegin);
5182 for (int i = rank - 1; i >= 0; i--){
5183 counter [i].Emit (ec);
5185 ec.MarkLabel (test [i]);
5186 variables [i].Emit (ec);
5188 if (lengths != null)
5189 lengths [i].Emit (ec);
5191 length_exprs [i].Emit (ec);
5193 ec.Emit (OpCodes.Blt, loop [i]);
5196 ec.MarkLabel (ec.LoopEnd);
5200 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5202 class Body : Statement
5205 LocalVariableReference variable;
5206 Expression current, conv;
5207 Statement statement;
5209 public Body (TypeSpec type, LocalVariable variable,
5210 Expression current, Statement statement,
5214 this.variable = new LocalVariableReference (variable, loc);
5215 this.current = current;
5216 this.statement = statement;
5220 protected override void CloneTo (CloneContext clonectx, Statement target)
5222 throw new NotImplementedException ();
5225 public override bool Resolve (BlockContext ec)
5227 current = current.Resolve (ec);
5228 if (current == null)
5231 conv = Convert.ExplicitConversion (ec, current, type, loc);
5235 variable.local_info.Type = conv.Type;
5236 variable.Resolve (ec);
5238 if (!statement.Resolve (ec))
5244 protected override void DoEmit (EmitContext ec)
5246 variable.local_info.CreateBuilder (ec);
5247 variable.EmitAssign (ec, conv, false, false);
5249 statement.Emit (ec);
5253 class RuntimeDispose : Using.VariableDeclaration
5255 public RuntimeDispose (LocalVariable lv, Location loc)
5260 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5262 // Defered to runtime check
5265 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5267 if (TypeManager.void_dispose_void == null) {
5268 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5269 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5273 // Fabricates code like
5275 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5278 var dispose_variable = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
5280 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5281 dispose_variable.CreateReferenceExpression (bc, loc),
5282 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5283 loc), new NullLiteral (loc), loc);
5285 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5286 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5288 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5289 return new If (idisaposable_test, dispose, loc);
5293 LocalVariable variable;
5295 Statement statement;
5296 Expression var_type;
5297 ExpressionStatement init;
5298 TemporaryVariableReference enumerator_variable;
5299 bool ambiguous_getenumerator_name;
5301 public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5303 this.var_type = var_type;
5304 this.variable = var;
5310 protected override void CloneTo (CloneContext clonectx, Statement target)
5312 throw new NotImplementedException ();
5315 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5317 rc.Report.SymbolRelatedToPreviousError (enumerator);
5318 rc.Report.Error (202, loc,
5319 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5320 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5323 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5326 // Option 1: Try to match by name GetEnumerator first
5328 var mexpr = Expression.MemberLookup (rc, rc.CurrentType, expr.Type,
5329 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5331 var mg = mexpr as MethodGroupExpr;
5333 mg.InstanceExpression = expr;
5334 Arguments args = new Arguments (0);
5335 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5337 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5338 if (ambiguous_getenumerator_name)
5341 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5347 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5349 TypeSpec iface_candidate = null;
5350 for (TypeSpec t = expr.Type; t != null && t != TypeManager.object_type; t = t.BaseType) {
5351 var ifaces = t.Interfaces;
5352 if (ifaces != null) {
5353 foreach (var iface in ifaces) {
5354 if (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) {
5355 if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) {
5356 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5357 rc.Report.Error (1640, loc,
5358 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5359 expr.Type.GetSignatureForError (), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5364 iface_candidate = iface;
5368 if (iface == TypeManager.ienumerable_type && iface_candidate == null) {
5369 iface_candidate = iface;
5375 if (iface_candidate == null) {
5376 rc.Report.Error (1579, loc,
5377 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5378 expr.Type.GetSignatureForError (), "GetEnumerator");
5383 var method = TypeManager.GetPredefinedMethod (iface_candidate,
5384 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null), loc);
5389 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5390 mg.InstanceExpression = expr;
5394 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5396 var ms = MemberCache.FindMember (enumerator.ReturnType,
5397 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5398 BindingRestriction.InstanceOnly) as MethodSpec;
5400 if (ms == null || !ms.IsPublic) {
5401 Error_WrongEnumerator (rc, enumerator);
5405 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5408 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5410 var ps = MemberCache.FindMember (enumerator.ReturnType,
5411 MemberFilter.Property ("Current", null),
5412 BindingRestriction.InstanceOnly) as PropertySpec;
5414 if (ps == null || !ps.IsPublic) {
5415 Error_WrongEnumerator (rc, enumerator);
5422 public override bool Resolve (BlockContext ec)
5424 bool is_dynamic = expr.Type == InternalType.Dynamic;
5427 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5428 } else if (TypeManager.IsNullableType (expr.Type)) {
5429 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
5432 var get_enumerator_mg = ResolveGetEnumerator (ec);
5433 if (get_enumerator_mg == null) {
5437 var get_enumerator = get_enumerator_mg.BestCandidate;
5438 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
5439 enumerator_variable.Resolve (ec);
5441 // Prepare bool MoveNext ()
5442 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5443 if (move_next_mg == null) {
5447 move_next_mg.InstanceExpression = enumerator_variable;
5449 // Prepare ~T~ Current { get; }
5450 var current_prop = ResolveCurrent (ec, get_enumerator);
5451 if (current_prop == null) {
5455 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
5456 if (current_pe == null)
5459 VarExpr ve = var_type as VarExpr;
5462 // Source type is dynamic, set element type to dynamic too
5463 var_type = new TypeExpression (InternalType.Dynamic, var_type.Location);
5465 // Infer implicitly typed local variable from foreach enumerable type
5466 var_type = new TypeExpression (current_pe.Type, var_type.Location);
5468 } else if (is_dynamic) {
5469 // Explicit cast of dynamic collection elements has to be done at runtime
5470 current_pe = EmptyCast.Create (current_pe, InternalType.Dynamic);
5473 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5474 if (var_type == null)
5477 variable.Type = var_type.Type;
5479 var init = new Invocation (get_enumerator_mg, null);
5481 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5482 new Body (var_type.Type, variable, current_pe, statement, loc), loc);
5484 var enum_type = enumerator_variable.Type;
5487 // Add Dispose method call when enumerator can be IDisposable
5489 if (!enum_type.ImplementsInterface (TypeManager.idisposable_type, false)) {
5490 if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) {
5492 // Runtime Dispose check
5494 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
5495 vd.Initializer = init;
5496 statement = new Using (vd, statement, loc);
5499 // No Dispose call needed
5501 this.init = new SimpleAssign (enumerator_variable, init);
5502 this.init.Resolve (ec);
5506 // Static Dispose check
5508 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
5509 vd.Initializer = init;
5510 statement = new Using (vd, statement, loc);
5513 return statement.Resolve (ec);
5516 protected override void DoEmit (EmitContext ec)
5518 enumerator_variable.LocalInfo.CreateBuilder (ec);
5521 init.EmitStatement (ec);
5523 statement.Emit (ec);
5526 #region IErrorHandler Members
5528 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5530 ec.Report.SymbolRelatedToPreviousError (best);
5531 ec.Report.Warning (278, 2, loc,
5532 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5533 expr.Type.GetSignatureForError (), "enumerable",
5534 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5536 ambiguous_getenumerator_name = true;
5540 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5545 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5550 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5559 LocalVariable variable;
5561 Statement statement;
5563 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
5566 this.variable = var;
5572 public Statement Statement {
5573 get { return statement; }
5576 public override bool Resolve (BlockContext ec)
5578 expr = expr.Resolve (ec);
5583 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5587 if (expr.Type == TypeManager.string_type) {
5588 statement = new ArrayForeach (this, 1);
5589 } else if (expr.Type is ArrayContainer) {
5590 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5592 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5593 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5594 expr.ExprClassName);
5598 statement = new CollectionForeach (type, variable, expr, statement, loc);
5601 return statement.Resolve (ec);
5604 protected override void DoEmit (EmitContext ec)
5606 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5607 ec.LoopBegin = ec.DefineLabel ();
5608 ec.LoopEnd = ec.DefineLabel ();
5610 statement.Emit (ec);
5612 ec.LoopBegin = old_begin;
5613 ec.LoopEnd = old_end;
5616 protected override void CloneTo (CloneContext clonectx, Statement t)
5618 Foreach target = (Foreach) t;
5620 target.type = type.Clone (clonectx);
5621 target.expr = expr.Clone (clonectx);
5622 target.statement = statement.Clone (clonectx);