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 {
864 FlowBranching.UsageVector vectors;
866 public LabeledStatement (string name, Location l)
872 public Label LabelTarget (EmitContext ec)
877 label = ec.DefineLabel ();
886 public bool IsDefined {
887 get { return defined; }
890 public bool HasBeenReferenced {
891 get { return referenced; }
894 public FlowBranching.UsageVector JumpOrigins {
895 get { return vectors; }
898 public void AddUsageVector (FlowBranching.UsageVector vector)
900 vector = vector.Clone ();
901 vector.Next = vectors;
905 protected override void CloneTo (CloneContext clonectx, Statement target)
910 public override bool Resolve (BlockContext ec)
912 // this flow-branching will be terminated when the surrounding block ends
913 ec.StartFlowBranching (this);
917 protected override void DoEmit (EmitContext ec)
920 ec.MarkLabel (label);
923 public void AddReference ()
931 /// `goto default' statement
933 public class GotoDefault : Statement {
935 public GotoDefault (Location l)
940 protected override void CloneTo (CloneContext clonectx, Statement target)
945 public override bool Resolve (BlockContext ec)
947 ec.CurrentBranching.CurrentUsageVector.Goto ();
949 if (ec.Switch == null) {
950 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
954 if (!ec.Switch.GotDefault) {
955 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
962 protected override void DoEmit (EmitContext ec)
964 ec.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
969 /// `goto case' statement
971 public class GotoCase : Statement {
975 public GotoCase (Expression e, Location l)
981 public override bool Resolve (BlockContext ec)
983 if (ec.Switch == null){
984 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
988 ec.CurrentBranching.CurrentUsageVector.Goto ();
990 expr = expr.Resolve (ec);
994 Constant c = expr as Constant;
996 ec.Report.Error (150, expr.Location, "A constant value is expected");
1000 TypeSpec type = ec.Switch.SwitchType;
1001 Constant res = c.TryReduce (ec, type, c.Location);
1003 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1007 if (!Convert.ImplicitStandardConversionExists (c, type))
1008 ec.Report.Warning (469, 2, loc,
1009 "The `goto case' value is not implicitly convertible to type `{0}'",
1010 TypeManager.CSharpName (type));
1012 object val = res.GetValue ();
1014 val = SwitchLabel.NullStringCase;
1016 if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
1017 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1018 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1025 protected override void DoEmit (EmitContext ec)
1027 ec.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1030 protected override void CloneTo (CloneContext clonectx, Statement t)
1032 GotoCase target = (GotoCase) t;
1034 target.expr = expr.Clone (clonectx);
1038 public class Throw : Statement {
1041 public Throw (Expression expr, Location l)
1047 public override bool Resolve (BlockContext ec)
1050 ec.CurrentBranching.CurrentUsageVector.Goto ();
1051 return ec.CurrentBranching.CheckRethrow (loc);
1054 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1055 ec.CurrentBranching.CurrentUsageVector.Goto ();
1060 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1061 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1063 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1068 protected override void DoEmit (EmitContext ec)
1071 ec.Emit (OpCodes.Rethrow);
1075 ec.Emit (OpCodes.Throw);
1079 protected override void CloneTo (CloneContext clonectx, Statement t)
1081 Throw target = (Throw) t;
1084 target.expr = expr.Clone (clonectx);
1088 public class Break : Statement {
1090 public Break (Location l)
1095 bool unwind_protect;
1097 public override bool Resolve (BlockContext ec)
1099 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1100 ec.CurrentBranching.CurrentUsageVector.Goto ();
1104 protected override void DoEmit (EmitContext ec)
1106 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1109 protected override void CloneTo (CloneContext clonectx, Statement t)
1115 public class Continue : Statement {
1117 public Continue (Location l)
1122 bool unwind_protect;
1124 public override bool Resolve (BlockContext ec)
1126 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1127 ec.CurrentBranching.CurrentUsageVector.Goto ();
1131 protected override void DoEmit (EmitContext ec)
1133 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1136 protected override void CloneTo (CloneContext clonectx, Statement t)
1142 public interface ILocalVariable
1144 void Emit (EmitContext ec);
1145 void EmitAssign (EmitContext ec);
1146 void EmitAddressOf (EmitContext ec);
1149 public interface INamedBlockVariable
1151 Block Block { get; }
1152 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1153 bool IsDeclared { get; }
1154 Location Location { get; }
1157 public class BlockVariableDeclaration : Statement
1159 public class Declarator
1162 Expression initializer;
1164 public Declarator (LocalVariable li, Expression initializer)
1166 if (li.Type != null)
1167 throw new ArgumentException ("Expected null variable type");
1170 this.initializer = initializer;
1175 public LocalVariable Variable {
1181 public Expression Initializer {
1186 initializer = value;
1193 Expression initializer;
1194 protected FullNamedExpression type_expr;
1196 protected List<Declarator> declarators;
1198 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1200 this.type_expr = type;
1202 this.loc = type_expr.Location;
1205 protected BlockVariableDeclaration (LocalVariable li)
1212 public List<Declarator> Declarators {
1218 public Expression Initializer {
1223 initializer = value;
1227 public FullNamedExpression TypeExpression {
1233 public LocalVariable Variable {
1241 public void AddDeclarator (Declarator decl)
1243 if (declarators == null)
1244 declarators = new List<Declarator> ();
1246 declarators.Add (decl);
1249 void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1251 var container = bc.CurrentMemberDefinition.Parent;
1253 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1254 new MemberName (li.Name, li.Location), null);
1256 container.AddField (f);
1258 Evaluator.QueueField (f);
1260 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1264 public override bool Resolve (BlockContext bc)
1266 TypeSpec type = null;
1267 if (type_expr is VarExpr) {
1269 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1270 // same name exists or as a keyword when no type was found
1272 var texpr = type_expr.ResolveAsTypeTerminal (bc, true);
1273 if (texpr == null) {
1274 if (RootContext.Version < LanguageVersion.V_3)
1275 bc.Report.FeatureIsNotAvailable (loc, "implicitly typed local variable");
1278 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1282 if (li.IsConstant) {
1283 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1287 if (Initializer == null) {
1288 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1292 if (declarators != null) {
1293 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1297 Initializer = Initializer.Resolve (bc);
1298 if (Initializer != null) {
1299 ((VarExpr) type_expr).InferType (bc, Initializer);
1300 type = type_expr.Type;
1306 var texpr = type_expr.ResolveAsTypeTerminal (bc, false);
1312 if (li.IsConstant && !type.IsConstantCompatible) {
1313 Const.Error_InvalidConstantType (type, loc, bc.Report);
1318 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1320 if (type.IsPointer && !bc.IsUnsafe)
1321 Expression.UnsafeError (bc, loc);
1325 bool eval_global = RootContext.StatementMode && bc.CurrentBlock is ToplevelBlock;
1327 CreateEvaluatorVariable (bc, li);
1329 li.PrepareForFlowAnalysis (bc);
1332 if (initializer != null) {
1333 initializer = ResolveInitializer (bc, li, initializer);
1334 // li.Variable.DefinitelyAssigned
1337 if (declarators != null) {
1338 foreach (var d in declarators) {
1339 d.Variable.Type = type;
1341 CreateEvaluatorVariable (bc, d.Variable);
1343 d.Variable.PrepareForFlowAnalysis (bc);
1346 if (d.Initializer != null) {
1347 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1348 // d.Variable.DefinitelyAssigned
1356 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1358 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1359 return a.ResolveStatement (bc);
1362 protected override void DoEmit (EmitContext ec)
1367 li.CreateBuilder (ec);
1369 if (Initializer != null)
1370 ((ExpressionStatement) Initializer).EmitStatement (ec);
1372 if (declarators != null) {
1373 foreach (var d in declarators) {
1374 d.Variable.CreateBuilder (ec);
1375 if (d.Initializer != null)
1376 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1381 protected override void CloneTo (CloneContext clonectx, Statement target)
1383 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1385 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1386 if (initializer != null)
1387 t.initializer = initializer.Clone (clonectx);
1389 if (declarators != null) {
1390 t.declarators = null;
1391 foreach (var d in declarators)
1392 t.AddDeclarator (new Declarator (d.Variable, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1397 class BlockConstantDeclaration : BlockVariableDeclaration
1399 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1404 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1406 initializer = initializer.Resolve (bc);
1407 if (initializer == null)
1410 var c = initializer as Constant;
1412 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1416 c = c.ConvertImplicitly (bc, li.Type);
1418 if (TypeManager.IsReferenceType (li.Type))
1419 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1421 initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
1426 li.ConstantValue = c;
1432 // The information about a user-perceived local variable
1434 public class LocalVariable : INamedBlockVariable, ILocalVariable
1441 AddressTaken = 1 << 2,
1442 CompilerGenerated = 1 << 3,
1444 ForeachVariable = 1 << 5,
1445 FixedVariable = 1 << 6,
1446 UsingVariable = 1 << 7,
1447 // DefinitelyAssigned = 1 << 8,
1449 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1453 readonly string name;
1454 readonly Location loc;
1455 readonly Block block;
1457 Constant const_value;
1459 public VariableInfo VariableInfo;
1460 HoistedVariable hoisted_variant;
1462 LocalBuilder builder;
1464 public LocalVariable (Block block, string name, Location loc)
1471 public LocalVariable (Block block, string name, Flags flags, Location loc)
1472 : this (block, name, loc)
1478 // Used by variable declarators
1480 public LocalVariable (LocalVariable li, string name, Location loc)
1481 : this (li.block, name, li.flags, loc)
1487 public Block Block {
1493 public Constant ConstantValue {
1498 const_value = value;
1503 // Hoisted local variable variant
1505 public HoistedVariable HoistedVariant {
1507 return hoisted_variant;
1510 hoisted_variant = value;
1514 public bool IsDeclared {
1516 return type != null;
1520 public bool IsConstant {
1522 return (flags & Flags.Constant) != 0;
1526 public bool IsThis {
1528 return (flags & Flags.IsThis) != 0;
1532 public bool IsFixed {
1534 return (flags & Flags.FixedVariable) != 0;
1538 public bool IsReadonly {
1540 return (flags & Flags.ReadonlyMask) != 0;
1544 public Location Location {
1550 public string Name {
1556 public TypeSpec Type {
1567 public void CreateBuilder (EmitContext ec)
1569 if ((flags & Flags.Used) == 0) {
1570 if (VariableInfo == null) {
1571 // Missing flow analysis or wrong variable flags
1572 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1575 if (VariableInfo.IsEverAssigned)
1576 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1578 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1581 if (HoistedVariant != null)
1584 if (builder != null) {
1585 if ((flags & Flags.CompilerGenerated) != 0)
1588 // To avoid Used warning duplicates
1589 throw new InternalErrorException ("Already created variable `{0}'", name);
1593 // All fixed variabled are pinned, a slot has to be alocated
1595 builder = ec.DeclareLocal (Type, IsFixed);
1596 if (SymbolWriter.HasSymbolWriter)
1597 ec.DefineLocalVariable (name, builder);
1600 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1602 LocalVariable li = new LocalVariable (block, "<$$>", Flags.CompilerGenerated | Flags.Used, loc);
1607 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1609 if (IsConstant && const_value != null)
1610 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc).Resolve (rc);
1612 return new LocalVariableReference (this, loc);
1615 public void Emit (EmitContext ec)
1617 ec.Emit (OpCodes.Ldloc, builder);
1620 public void EmitAssign (EmitContext ec)
1622 // TODO: Need something better for temporary variables
1623 if ((flags & Flags.CompilerGenerated) != 0)
1626 ec.Emit (OpCodes.Stloc, builder);
1629 public void EmitAddressOf (EmitContext ec)
1631 ec.Emit (OpCodes.Ldloca, builder);
1634 public bool IsThisAssigned (BlockContext ec, Block block)
1636 if (VariableInfo == null)
1637 throw new Exception ();
1639 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1642 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1645 public bool IsAssigned (BlockContext ec)
1647 if (VariableInfo == null)
1648 throw new Exception ();
1650 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1653 public void PrepareForFlowAnalysis (BlockContext bc)
1656 // No need for definitely assigned check for these guys
1658 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1661 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1662 bc.FlowOffset += VariableInfo.Length;
1666 // Mark the variables as referenced in the user code
1668 public void SetIsUsed ()
1670 flags |= Flags.Used;
1673 public bool AddressTaken {
1674 get { return (flags & Flags.AddressTaken) != 0; }
1675 set { flags |= Flags.AddressTaken; }
1678 public override string ToString ()
1680 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
1683 public string GetReadOnlyContext ()
1685 switch (flags & Flags.ReadonlyMask) {
1686 case Flags.FixedVariable:
1687 return "fixed variable";
1688 case Flags.ForeachVariable:
1689 return "foreach iteration variable";
1690 case Flags.UsingVariable:
1691 return "using variable";
1694 throw new InternalErrorException ("Variable is not readonly");
1699 /// Block represents a C# block.
1703 /// This class is used in a number of places: either to represent
1704 /// explicit blocks that the programmer places or implicit blocks.
1706 /// Implicit blocks are used as labels or to introduce variable
1709 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1710 /// they contain extra information that is not necessary on normal blocks.
1712 public class Block : Statement {
1720 HasCapturedVariable = 64,
1721 HasCapturedThis = 1 << 7,
1722 IsExpressionTree = 1 << 8,
1723 CompilerGenerated = 1 << 9
1726 public Block Parent;
1727 public Location StartLocation;
1728 public Location EndLocation;
1730 public ExplicitBlock Explicit;
1731 public ParametersBlock ParametersBlock;
1733 protected Flags flags;
1736 // The statements in this block
1738 protected List<Statement> statements;
1741 // Labels. (label, block) pairs.
1743 protected Dictionary<string, LabeledStatement> labels;
1746 // If this is a switch section, the enclosing switch block.
1748 protected ExplicitBlock switch_block;
1750 protected List<Statement> scope_initializers;
1752 int? resolving_init_idx;
1758 public int ID = id++;
1760 static int clone_id_counter;
1764 // int assignable_slots;
1765 bool unreachable_shown;
1768 public Block (Block parent, Location start, Location end)
1769 : this (parent, 0, start, end)
1773 public Block (Block parent, Flags flags, Location start, Location end)
1775 if (parent != null) {
1776 // the appropriate constructors will fixup these fields
1777 ParametersBlock = parent.ParametersBlock;
1778 Explicit = parent.Explicit;
1781 this.Parent = parent;
1783 this.StartLocation = start;
1784 this.EndLocation = end;
1786 statements = new List<Statement> (4);
1788 this.original = this;
1793 public bool HasRet {
1794 get { return (flags & Flags.HasRet) != 0; }
1797 public Block Original {
1803 public bool IsCompilerGenerated {
1804 get { return (flags & Flags.CompilerGenerated) != 0; }
1805 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
1808 public bool Unchecked {
1809 get { return (flags & Flags.Unchecked) != 0; }
1810 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1813 public bool Unsafe {
1814 get { return (flags & Flags.Unsafe) != 0; }
1815 set { flags |= Flags.Unsafe; }
1820 public ExplicitBlock CreateSwitchBlock (Location start)
1822 // FIXME: Only explicit block should be created
1823 var new_block = new ExplicitBlock (this, start, start);
1824 new_block.switch_block = Explicit;
1828 public void SetEndLocation (Location loc)
1833 protected void Error_158 (string name, Location loc)
1835 ParametersBlock.TopBlock.Report.Error (158, loc, "The label `{0}' shadows another label " +
1836 "by the same name in a contained scope", name);
1840 /// Adds a label to the current block.
1844 /// false if the name already exists in this block. true
1848 public bool AddLabel (LabeledStatement target)
1850 if (switch_block != null)
1851 return switch_block.AddLabel (target);
1853 string name = target.Name;
1856 while (cur != null) {
1857 LabeledStatement s = cur.DoLookupLabel (name);
1859 ParametersBlock.TopBlock.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1860 ParametersBlock.TopBlock.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1864 if (this == Explicit)
1870 while (cur != null) {
1871 if (cur.DoLookupLabel (name) != null) {
1872 Error_158 (name, target.loc);
1876 if (children != null) {
1877 foreach (Block b in children) {
1878 LabeledStatement s = b.DoLookupLabel (name);
1882 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1883 Error_158 (name, target.loc);
1891 ParametersBlock.TopBlock.CheckError158 (name, target.loc);
1894 labels = new Dictionary<string, LabeledStatement> ();
1896 labels.Add (name, target);
1900 public LabeledStatement LookupLabel (string name)
1902 LabeledStatement s = DoLookupLabel (name);
1906 if (children == null)
1909 foreach (Block child in children) {
1910 if (Explicit != child.Explicit)
1913 s = child.LookupLabel (name);
1921 LabeledStatement DoLookupLabel (string name)
1923 if (switch_block != null)
1924 return switch_block.LookupLabel (name);
1927 if (labels.ContainsKey (name))
1928 return labels [name];
1933 public void AddLocalName (LocalVariable li)
1935 AddLocalName (li.Name, li);
1938 public virtual void AddLocalName (string name, INamedBlockVariable li)
1940 ParametersBlock.TopBlock.AddLocalName (name, li);
1943 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
1945 if (reason == null) {
1946 Error_AlreadyDeclared (name, variable);
1950 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
1951 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
1952 "to `{0}', which is already used in a `{1}' scope to denote something else",
1956 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
1958 var pi = variable as ParametersBlock.ParameterInfo;
1960 var p = pi.Parameter;
1961 if (p is AnonymousTypeClass.GeneratedParameter) {
1962 ParametersBlock.TopBlock.Report.Error (833, p.Location, "`{0}': An anonymous type cannot have multiple properties with the same name",
1965 ParametersBlock.TopBlock.Report.Error (100, p.Location, "The parameter name `{0}' is a duplicate", p.Name);
1971 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
1972 "A local variable named `{0}' is already defined in this scope", name);
1975 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
1977 ParametersBlock.TopBlock.Report.Error (412, loc,
1978 "The type parameter name `{0}' is the same as local variable or parameter name",
1983 // It should be used by expressions which require to
1984 // register a statement during resolve process.
1986 public void AddScopeStatement (Statement s)
1988 if (scope_initializers == null)
1989 scope_initializers = new List<Statement> ();
1992 // Simple recursive helper, when resolve scope initializer another
1993 // new scope initializer can be added, this ensures it's initialized
1994 // before existing one. For now this can happen with expression trees
1995 // in base ctor initializer only
1997 if (resolving_init_idx.HasValue) {
1998 scope_initializers.Insert (resolving_init_idx.Value, s);
1999 ++resolving_init_idx;
2001 scope_initializers.Add (s);
2005 public void AddStatement (Statement s)
2010 public int AssignableSlots {
2012 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2014 // return assignable_slots;
2018 public override bool Resolve (BlockContext ec)
2020 Block prev_block = ec.CurrentBlock;
2023 ec.CurrentBlock = this;
2024 ec.StartFlowBranching (this);
2026 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2029 // Compiler generated scope statements
2031 if (scope_initializers != null) {
2032 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2033 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2036 resolving_init_idx = null;
2040 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2041 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2042 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2043 // responsible for handling the situation.
2045 int statement_count = statements.Count;
2046 for (int ix = 0; ix < statement_count; ix++){
2047 Statement s = statements [ix];
2050 // Warn if we detect unreachable code.
2053 if (s is EmptyStatement)
2056 if (!unreachable_shown && !(s is LabeledStatement)) {
2057 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2058 unreachable_shown = true;
2061 Block c_block = s as Block;
2062 if (c_block != null)
2063 c_block.unreachable = c_block.unreachable_shown = true;
2067 // Note that we're not using ResolveUnreachable() for unreachable
2068 // statements here. ResolveUnreachable() creates a temporary
2069 // flow branching and kills it afterwards. This leads to problems
2070 // if you have two unreachable statements where the first one
2071 // assigns a variable and the second one tries to access it.
2074 if (!s.Resolve (ec)) {
2076 if (ec.IsInProbingMode)
2079 statements [ix] = new EmptyStatement (s.loc);
2083 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2084 statements [ix] = new EmptyStatement (s.loc);
2086 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2087 if (unreachable && s is LabeledStatement)
2088 throw new InternalErrorException ("should not happen");
2091 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2092 ec.CurrentBranching, statement_count);
2094 while (ec.CurrentBranching is FlowBranchingLabeled)
2095 ec.EndFlowBranching ();
2097 bool flow_unreachable = ec.EndFlowBranching ();
2099 ec.CurrentBlock = prev_block;
2101 if (flow_unreachable)
2102 flags |= Flags.HasRet;
2104 // If we're a non-static `struct' constructor which doesn't have an
2105 // initializer, then we must initialize all of the struct's fields.
2106 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2109 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2110 foreach (LabeledStatement label in labels.Values)
2111 if (!label.HasBeenReferenced)
2112 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2118 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2120 unreachable_shown = true;
2124 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2126 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2127 bool ok = Resolve (ec);
2128 ec.KillFlowBranching ();
2133 protected override void DoEmit (EmitContext ec)
2135 for (int ix = 0; ix < statements.Count; ix++){
2136 statements [ix].Emit (ec);
2140 public override void Emit (EmitContext ec)
2142 if (scope_initializers != null)
2143 EmitScopeInitializers (ec);
2145 ec.Mark (StartLocation);
2148 if (SymbolWriter.HasSymbolWriter)
2149 EmitSymbolInfo (ec);
2152 protected void EmitScopeInitializers (EmitContext ec)
2154 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2156 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2157 foreach (Statement s in scope_initializers)
2161 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2164 protected virtual void EmitSymbolInfo (EmitContext ec)
2169 public override string ToString ()
2171 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2175 protected override void CloneTo (CloneContext clonectx, Statement t)
2177 Block target = (Block) t;
2179 target.clone_id = clone_id_counter++;
2182 clonectx.AddBlockMap (this, target);
2184 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2185 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2188 target.Parent = clonectx.RemapBlockCopy (Parent);
2190 target.statements = new List<Statement> (statements.Count);
2191 foreach (Statement s in statements)
2192 target.statements.Add (s.Clone (clonectx));
2196 public class ExplicitBlock : Block
2198 protected AnonymousMethodStorey am_storey;
2200 public ExplicitBlock (Block parent, Location start, Location end)
2201 : this (parent, (Flags) 0, start, end)
2205 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2206 : base (parent, flags, start, end)
2208 this.Explicit = this;
2213 public AnonymousMethodStorey AnonymousMethodStorey {
2219 public bool HasCapturedThis {
2220 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2222 return (flags & Flags.HasCapturedThis) != 0;
2226 public bool HasCapturedVariable {
2227 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2229 return (flags & Flags.HasCapturedVariable) != 0;
2236 // Creates anonymous method storey in current block
2238 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2241 // An iterator has only 1 storey block
2243 if (ec.CurrentIterator != null)
2244 return ec.CurrentIterator.Storey;
2247 // When referencing a variable in iterator storey from children anonymous method
2249 if (ParametersBlock.am_storey is IteratorStorey) {
2250 return ParametersBlock.am_storey;
2254 // Switch block does not follow sequential flow and we cannot emit
2255 // storey initialization inside the block because it can be jumped over
2256 // for all non-first cases. Instead we push it up to the parent block to be
2257 // always initialized
2259 if (switch_block != null)
2260 return switch_block.CreateAnonymousMethodStorey (ec);
2262 if (am_storey == null) {
2263 MemberBase mc = ec.MemberContext as MemberBase;
2266 // Creates anonymous method storey for this block
2268 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2274 public override void Emit (EmitContext ec)
2276 if (am_storey != null) {
2277 DefineAnonymousStorey (ec);
2278 am_storey.EmitStoreyInstantiation (ec);
2281 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2282 if (emit_debug_info)
2287 if (emit_debug_info)
2291 void DefineAnonymousStorey (EmitContext ec)
2294 // Creates anonymous method storey
2296 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2298 // Creates parent storey reference when hoisted this is accessible
2300 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2301 ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2304 // Hoisted this exists in top-level parent storey only
2306 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2307 parent = parent.Parent.Explicit;
2309 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2312 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2314 // TODO MemberCache: Review
2315 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2318 am_storey.CreateType ();
2319 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2320 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2322 am_storey.DefineType ();
2323 am_storey.ResolveTypeParameters ();
2325 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2326 if (ref_blocks != null) {
2327 foreach (ExplicitBlock ref_block in ref_blocks) {
2328 for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2329 if (b.am_storey != null) {
2330 b.am_storey.AddParentStoreyReference (ec, am_storey);
2332 // Stop propagation inside same top block
2333 if (b.ParametersBlock.am_storey == ParametersBlock.am_storey)
2336 b = b.ParametersBlock;
2339 b.HasCapturedVariable = true;
2344 am_storey.Define ();
2345 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2348 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2350 tryBlock.statements = statements;
2351 statements = new List<Statement> (1);
2352 statements.Add (tf);
2357 // ParametersBlock was introduced to support anonymous methods
2358 // and lambda expressions
2360 public class ParametersBlock : ExplicitBlock
2362 public class ParameterInfo : INamedBlockVariable
2364 readonly ParametersBlock block;
2366 public VariableInfo VariableInfo;
2368 public ParameterInfo (ParametersBlock block, int index)
2376 public Block Block {
2382 public bool IsDeclared {
2388 public Location Location {
2390 return Parameter.Location;
2394 public Parameter Parameter {
2396 return block.Parameters [index];
2400 public TypeSpec ParameterType {
2402 return Parameter.Type;
2408 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2410 return new ParameterReference (this, loc);
2415 // Block is converted to an expression
2417 sealed class BlockScopeExpression : Expression
2420 readonly ParametersBlock block;
2422 public BlockScopeExpression (Expression child, ParametersBlock block)
2428 public override Expression CreateExpressionTree (ResolveContext ec)
2430 throw new NotSupportedException ();
2433 protected override Expression DoResolve (ResolveContext ec)
2438 child = child.Resolve (ec);
2442 eclass = child.eclass;
2447 public override void Emit (EmitContext ec)
2449 block.EmitScopeInitializers (ec);
2454 protected ParametersCompiled parameters;
2455 protected ParameterInfo[] parameter_info;
2457 protected bool unreachable;
2458 protected ToplevelBlock top_block;
2460 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2461 : base (parent, 0, start, start)
2463 if (parameters == null)
2464 throw new ArgumentNullException ("parameters");
2466 this.parameters = parameters;
2467 ParametersBlock = this;
2469 this.top_block = parent.ParametersBlock.top_block;
2470 ProcessParameters ();
2473 protected ParametersBlock (ParametersCompiled parameters, Location start)
2474 : base (null, 0, start, start)
2476 if (parameters == null)
2477 throw new ArgumentNullException ("parameters");
2479 this.parameters = parameters;
2480 ParametersBlock = this;
2483 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2484 : base (null, 0, source.StartLocation, source.EndLocation)
2486 this.parameters = parameters;
2487 this.statements = source.statements;
2488 this.scope_initializers = source.scope_initializers;
2489 this.switch_block = source.switch_block;
2491 this.resolved = true;
2492 this.unreachable = source.unreachable;
2493 this.am_storey = source.am_storey;
2495 ParametersBlock = this;
2501 // Block has been converted to expression tree
2503 public bool IsExpressionTree {
2505 return (flags & Flags.IsExpressionTree) != 0;
2510 // The parameters for the block.
2512 public ParametersCompiled Parameters {
2518 public ToplevelBlock TopBlock {
2524 public bool Resolved {
2533 // Check whether all `out' parameters have been assigned.
2535 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2537 if (vector.IsUnreachable)
2540 int n = parameter_info == null ? 0 : parameter_info.Length;
2542 for (int i = 0; i < n; i++) {
2543 VariableInfo var = parameter_info[i].VariableInfo;
2548 if (vector.IsAssigned (var, false))
2551 TopBlock.Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2556 public override Expression CreateExpressionTree (ResolveContext ec)
2558 if (statements.Count == 1) {
2559 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2560 if (scope_initializers != null)
2561 expr = new BlockScopeExpression (expr, this);
2566 return base.CreateExpressionTree (ec);
2569 public ParameterInfo GetParameterInfo (Parameter p)
2571 for (int i = 0; i < parameters.Count; ++i) {
2572 if (parameters[i] == p)
2573 return parameter_info[i];
2576 throw new ArgumentException ("Invalid parameter");
2579 public Expression GetParameterReference (int index, Location loc)
2581 return new ParameterReference (parameter_info[index], loc);
2584 protected void ProcessParameters ()
2586 if (parameters.Count == 0)
2589 parameter_info = new ParameterInfo[parameters.Count];
2590 for (int i = 0; i < parameter_info.Length; ++i) {
2591 var p = parameters.FixedParameters[i];
2595 // TODO: Should use Parameter only and more block there
2596 parameter_info[i] = new ParameterInfo (this, i);
2597 AddLocalName (p.Name, parameter_info[i]);
2601 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2608 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2609 flags |= Flags.IsExpressionTree;
2614 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2615 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2620 unreachable = top_level.End ();
2622 } catch (Exception e) {
2623 if (e is CompletionResult || rc.Report.IsDisabled)
2626 if (rc.CurrentBlock != null) {
2627 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2629 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2632 if (Report.DebugFlags > 0)
2636 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2637 if (rc.CurrentAnonymousMethod == null) {
2638 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2639 if (md is IteratorMethod) {
2642 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2646 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2647 rc.CurrentAnonymousMethod.GetSignatureForError ());
2655 void ResolveMeta (BlockContext ec)
2657 int orig_count = parameters.Count;
2659 for (int i = 0; i < orig_count; ++i) {
2660 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2662 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2665 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2666 parameter_info[i].VariableInfo = vi;
2667 ec.FlowOffset += vi.Length;
2671 public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
2673 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2674 pb.EndLocation = EndLocation;
2675 pb.statements = statements;
2677 var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2678 am_storey = new IteratorStorey (iterator);
2680 statements = new List<Statement> (1);
2681 AddStatement (new Return (iterator, iterator.Location));
2688 public class ToplevelBlock : ParametersBlock
2690 LocalVariable this_variable;
2691 CompilerContext compiler;
2692 Dictionary<string, object> names;
2694 public HoistedVariable HoistedThisVariable;
2696 public Report Report {
2697 get { return compiler.Report; }
2700 public ToplevelBlock (CompilerContext ctx, Location loc)
2701 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2705 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2706 : base (parameters, start)
2708 this.compiler = ctx;
2711 ProcessParameters ();
2715 // Recreates a top level block from parameters block. Used for
2716 // compiler generated methods where the original block comes from
2717 // explicit child block. This works for already resolved blocks
2718 // only to ensure we resolve them in the correct flow order
2720 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
2721 : base (source, parameters)
2723 this.compiler = source.TopBlock.compiler;
2727 public override void AddLocalName (string name, INamedBlockVariable li)
2730 names = new Dictionary<string, object> ();
2733 if (!names.TryGetValue (name, out value)) {
2734 names.Add (name, li);
2738 INamedBlockVariable existing = value as INamedBlockVariable;
2739 List<INamedBlockVariable> existing_list;
2740 if (existing != null) {
2741 existing_list = new List<INamedBlockVariable> ();
2742 existing_list.Add (existing);
2743 names[name] = existing_list;
2745 existing_list = (List<INamedBlockVariable>) value;
2749 // A collision checking between local names
2751 for (int i = 0; i < existing_list.Count; ++i) {
2752 existing = existing_list[i];
2753 Block b = existing.Block;
2755 // Collision at same level
2756 if (li.Block == b) {
2757 li.Block.Error_AlreadyDeclared (name, li);
2761 // Collision with parent
2763 while ((b = b.Parent) != null) {
2764 if (existing.Block == b) {
2765 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
2766 i = existing_list.Count;
2771 // Collision with with children
2773 while ((b = b.Parent) != null) {
2774 if (li.Block == b) {
2775 li.Block.Error_AlreadyDeclared (name, li, "child");
2776 i = existing_list.Count;
2782 existing_list.Add (li);
2785 public bool CheckError158 (string name, Location loc)
2788 if (AnonymousChildren != null) {
2789 foreach (ToplevelBlock child in AnonymousChildren) {
2790 if (!child.CheckError158 (name, loc))
2795 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2796 if (!c.DoCheckError158 (name, loc))
2803 bool DoCheckError158 (string name, Location loc)
2805 LabeledStatement s = LookupLabel (name);
2807 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2808 Error_158 (name, loc);
2816 // Creates an arguments set from all parameters, useful for method proxy calls
2818 public Arguments GetAllParametersArguments ()
2820 int count = parameters.Count;
2821 Arguments args = new Arguments (count);
2822 for (int i = 0; i < count; ++i) {
2823 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
2824 args.Add (new Argument (arg_expr));
2831 // Lookup inside a block, the returned value can represent 3 states
2833 // true+variable: A local name was found and it's valid
2834 // false+variable: A local name was found in a child block only
2835 // false+null: No local name was found
2837 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
2843 if (!names.TryGetValue (name, out value))
2846 variable = value as INamedBlockVariable;
2848 if (variable != null) {
2850 if (variable.Block == b)
2854 } while (b != null);
2862 } while (b != null);
2864 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
2865 for (int i = 0; i < list.Count; ++i) {
2868 if (variable.Block == b)
2872 } while (b != null);
2880 } while (b != null);
2891 // Returns the "this" instance variable of this block.
2892 // See AddThisVariable() for more information.
2894 public LocalVariable ThisVariable {
2895 get { return this_variable; }
2899 // This is used by non-static `struct' constructors which do not have an
2900 // initializer - in this case, the constructor must initialize all of the
2901 // struct's fields. To do this, we add a "this" variable and use the flow
2902 // analysis code to ensure that it's been fully initialized before control
2903 // leaves the constructor.
2905 public LocalVariable AddThisVariable (BlockContext bc, TypeContainer ds, Location l)
2907 if (this_variable == null) {
2908 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, l);
2909 this_variable.Type = ds.CurrentType;
2910 this_variable.PrepareForFlowAnalysis (bc);
2913 return this_variable;
2916 public bool IsIterator {
2917 get { return (flags & Flags.IsIterator) != 0; }
2918 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2921 public bool IsThisAssigned (BlockContext ec)
2923 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2926 public override void Emit (EmitContext ec)
2928 if (Report.Errors > 0)
2934 if (ec.HasReturnLabel)
2935 ec.ReturnLabel = ec.DefineLabel ();
2939 ec.Mark (EndLocation);
2941 if (ec.HasReturnLabel)
2942 ec.MarkLabel (ec.ReturnLabel);
2944 if (ec.return_value != null) {
2945 ec.Emit (OpCodes.Ldloc, ec.return_value);
2946 ec.Emit (OpCodes.Ret);
2949 // If `HasReturnLabel' is set, then we already emitted a
2950 // jump to the end of the method, so we must emit a `ret'
2953 // Unfortunately, System.Reflection.Emit automatically emits
2954 // a leave to the end of a finally block. This is a problem
2955 // if no code is following the try/finally block since we may
2956 // jump to a point after the end of the method.
2957 // As a workaround, we're always creating a return label in
2961 if (ec.HasReturnLabel || !unreachable) {
2962 if (ec.ReturnType != TypeManager.void_type)
2963 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2964 ec.Emit (OpCodes.Ret);
2969 } catch (Exception e){
2970 Console.WriteLine ("Exception caught by the compiler while emitting:");
2971 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2973 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2979 protected override void EmitSymbolInfo (EmitContext ec)
2981 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2982 if ((ae != null) && (ae.Storey != null))
2983 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2985 base.EmitSymbolInfo (ec);
2989 public class SwitchLabel {
2996 Label il_label_code;
2997 bool il_label_code_set;
2999 public static readonly object NullStringCase = new object ();
3002 // if expr == null, then it is the default case.
3004 public SwitchLabel (Expression expr, Location l)
3010 public Expression Label {
3016 public Location Location {
3020 public object Converted {
3026 public Label GetILLabel (EmitContext ec)
3029 il_label = ec.DefineLabel ();
3030 il_label_set = true;
3035 public Label GetILLabelCode (EmitContext ec)
3037 if (!il_label_code_set){
3038 il_label_code = ec.DefineLabel ();
3039 il_label_code_set = true;
3041 return il_label_code;
3045 // Resolves the expression, reduces it to a literal if possible
3046 // and then converts it to the requested type.
3048 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3050 Expression e = label.Resolve (ec);
3055 Constant c = e as Constant;
3057 ec.Report.Error (150, loc, "A constant value is expected");
3061 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3062 converted = NullStringCase;
3066 if (allow_nullable && c.GetValue () == null) {
3067 converted = NullStringCase;
3071 c = c.ImplicitConversionRequired (ec, required_type, loc);
3075 converted = c.GetValue ();
3079 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3082 if (converted == null)
3084 else if (converted == NullStringCase)
3087 label = converted.ToString ();
3089 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3090 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3093 public SwitchLabel Clone (CloneContext clonectx)
3095 return new SwitchLabel (label.Clone (clonectx), loc);
3099 public class SwitchSection {
3100 // An array of SwitchLabels.
3101 public readonly List<SwitchLabel> Labels;
3102 public readonly Block Block;
3104 public SwitchSection (List<SwitchLabel> labels, Block block)
3110 public SwitchSection Clone (CloneContext clonectx)
3112 var cloned_labels = new List<SwitchLabel> ();
3114 foreach (SwitchLabel sl in cloned_labels)
3115 cloned_labels.Add (sl.Clone (clonectx));
3117 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3121 public class Switch : Statement {
3122 public List<SwitchSection> Sections;
3123 public Expression Expr;
3126 /// Maps constants whose type type SwitchType to their SwitchLabels.
3128 public IDictionary<object, SwitchLabel> Elements;
3131 /// The governing switch type
3133 public TypeSpec SwitchType;
3138 Label default_target;
3140 Expression new_expr;
3143 SwitchSection constant_section;
3144 SwitchSection default_section;
3146 ExpressionStatement string_dictionary;
3147 FieldExpr switch_cache_field;
3148 static int unique_counter;
3151 // Nullable Types support
3153 Nullable.Unwrap unwrap;
3155 protected bool HaveUnwrap {
3156 get { return unwrap != null; }
3160 // The types allowed to be implicitly cast from
3161 // on the governing type
3163 static TypeSpec [] allowed_types;
3165 public Switch (Expression e, List<SwitchSection> sects, Location l)
3172 public bool GotDefault {
3174 return default_section != null;
3178 public Label DefaultTarget {
3180 return default_target;
3185 // Determines the governing type for a switch. The returned
3186 // expression might be the expression from the switch, or an
3187 // expression that includes any potential conversions to the
3188 // integral types or to string.
3190 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3192 TypeSpec t = expr.Type;
3194 if (t == TypeManager.byte_type ||
3195 t == TypeManager.sbyte_type ||
3196 t == TypeManager.ushort_type ||
3197 t == TypeManager.short_type ||
3198 t == TypeManager.uint32_type ||
3199 t == TypeManager.int32_type ||
3200 t == TypeManager.uint64_type ||
3201 t == TypeManager.int64_type ||
3202 t == TypeManager.char_type ||
3203 t == TypeManager.string_type ||
3204 t == TypeManager.bool_type ||
3205 TypeManager.IsEnumType (t))
3208 if (allowed_types == null){
3209 allowed_types = new TypeSpec [] {
3210 TypeManager.sbyte_type,
3211 TypeManager.byte_type,
3212 TypeManager.short_type,
3213 TypeManager.ushort_type,
3214 TypeManager.int32_type,
3215 TypeManager.uint32_type,
3216 TypeManager.int64_type,
3217 TypeManager.uint64_type,
3218 TypeManager.char_type,
3219 TypeManager.string_type
3224 // Try to find a *user* defined implicit conversion.
3226 // If there is no implicit conversion, or if there are multiple
3227 // conversions, we have to report an error
3229 Expression converted = null;
3230 foreach (TypeSpec tt in allowed_types){
3233 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3238 // Ignore over-worked ImplicitUserConversions that do
3239 // an implicit conversion in addition to the user conversion.
3241 if (!(e is UserCast))
3244 if (converted != null){
3245 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3255 // Performs the basic sanity checks on the switch statement
3256 // (looks for duplicate keys and non-constant expressions).
3258 // It also returns a hashtable with the keys that we will later
3259 // use to compute the switch tables
3261 bool CheckSwitch (ResolveContext ec)
3264 Elements = new Dictionary<object, SwitchLabel> ();
3266 foreach (SwitchSection ss in Sections){
3267 foreach (SwitchLabel sl in ss.Labels){
3268 if (sl.Label == null){
3269 if (default_section != null){
3270 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3273 default_section = ss;
3277 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3282 object key = sl.Converted;
3283 if (key == SwitchLabel.NullStringCase)
3284 has_null_case = true;
3287 Elements.Add (key, sl);
3288 } catch (ArgumentException) {
3289 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3297 void EmitObjectInteger (EmitContext ec, object k)
3300 ec.EmitInt ((int) k);
3301 else if (k is Constant) {
3302 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3305 ec.EmitInt (unchecked ((int) (uint) k));
3308 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3310 ec.EmitInt ((int) (long) k);
3311 ec.Emit (OpCodes.Conv_I8);
3314 ec.EmitLong ((long) k);
3316 else if (k is ulong)
3318 ulong ul = (ulong) k;
3321 ec.EmitInt (unchecked ((int) ul));
3322 ec.Emit (OpCodes.Conv_U8);
3326 ec.EmitLong (unchecked ((long) ul));
3330 ec.EmitInt ((int) ((char) k));
3331 else if (k is sbyte)
3332 ec.EmitInt ((int) ((sbyte) k));
3334 ec.EmitInt ((int) ((byte) k));
3335 else if (k is short)
3336 ec.EmitInt ((int) ((short) k));
3337 else if (k is ushort)
3338 ec.EmitInt ((int) ((ushort) k));
3340 ec.EmitInt (((bool) k) ? 1 : 0);
3342 throw new Exception ("Unhandled case");
3345 // structure used to hold blocks of keys while calculating table switch
3346 class KeyBlock : IComparable
3348 public KeyBlock (long _first)
3350 first = last = _first;
3354 public List<object> element_keys;
3355 // how many items are in the bucket
3356 public int Size = 1;
3359 get { return (int) (last - first + 1); }
3361 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3363 return kb_last.last - kb_first.first + 1;
3365 public int CompareTo (object obj)
3367 KeyBlock kb = (KeyBlock) obj;
3368 int nLength = Length;
3369 int nLengthOther = kb.Length;
3370 if (nLengthOther == nLength)
3371 return (int) (kb.first - first);
3372 return nLength - nLengthOther;
3377 /// This method emits code for a lookup-based switch statement (non-string)
3378 /// Basically it groups the cases into blocks that are at least half full,
3379 /// and then spits out individual lookup opcodes for each block.
3380 /// It emits the longest blocks first, and short blocks are just
3381 /// handled with direct compares.
3383 /// <param name="ec"></param>
3384 /// <param name="val"></param>
3385 /// <returns></returns>
3386 void TableSwitchEmit (EmitContext ec, Expression val)
3388 int element_count = Elements.Count;
3389 object [] element_keys = new object [element_count];
3390 Elements.Keys.CopyTo (element_keys, 0);
3391 Array.Sort (element_keys);
3393 // initialize the block list with one element per key
3394 var key_blocks = new List<KeyBlock> (element_count);
3395 foreach (object key in element_keys)
3396 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3398 KeyBlock current_kb;
3399 // iteratively merge the blocks while they are at least half full
3400 // there's probably a really cool way to do this with a tree...
3401 while (key_blocks.Count > 1)
3403 var key_blocks_new = new List<KeyBlock> ();
3404 current_kb = (KeyBlock) key_blocks [0];
3405 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3407 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3408 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3411 current_kb.last = kb.last;
3412 current_kb.Size += kb.Size;
3416 // start a new block
3417 key_blocks_new.Add (current_kb);
3421 key_blocks_new.Add (current_kb);
3422 if (key_blocks.Count == key_blocks_new.Count)
3424 key_blocks = key_blocks_new;
3427 // initialize the key lists
3428 foreach (KeyBlock kb in key_blocks)
3429 kb.element_keys = new List<object> ();
3431 // fill the key lists
3433 if (key_blocks.Count > 0) {
3434 current_kb = (KeyBlock) key_blocks [0];
3435 foreach (object key in element_keys)
3437 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3438 System.Convert.ToInt64 (key) > current_kb.last;
3440 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3441 current_kb.element_keys.Add (key);
3445 // sort the blocks so we can tackle the largest ones first
3448 // okay now we can start...
3449 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3450 Label lbl_default = default_target;
3452 Type type_keys = null;
3453 if (element_keys.Length > 0)
3454 type_keys = element_keys [0].GetType (); // used for conversions
3456 TypeSpec compare_type;
3458 if (TypeManager.IsEnumType (SwitchType))
3459 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3461 compare_type = SwitchType;
3463 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3465 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3466 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3469 foreach (object key in kb.element_keys) {
3470 SwitchLabel sl = (SwitchLabel) Elements [key];
3471 if (key is int && (int) key == 0) {
3472 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3475 EmitObjectInteger (ec, key);
3476 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3482 // TODO: if all the keys in the block are the same and there are
3483 // no gaps/defaults then just use a range-check.
3484 if (compare_type == TypeManager.int64_type ||
3485 compare_type == TypeManager.uint64_type)
3487 // TODO: optimize constant/I4 cases
3489 // check block range (could be > 2^31)
3491 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3492 ec.Emit (OpCodes.Blt, lbl_default);
3494 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3495 ec.Emit (OpCodes.Bgt, lbl_default);
3501 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3502 ec.Emit (OpCodes.Sub);
3504 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3510 int first = (int) kb.first;
3514 ec.Emit (OpCodes.Sub);
3518 ec.EmitInt (-first);
3519 ec.Emit (OpCodes.Add);
3523 // first, build the list of labels for the switch
3525 int cJumps = kb.Length;
3526 Label [] switch_labels = new Label [cJumps];
3527 for (int iJump = 0; iJump < cJumps; iJump++)
3529 object key = kb.element_keys [iKey];
3530 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3532 SwitchLabel sl = (SwitchLabel) Elements [key];
3533 switch_labels [iJump] = sl.GetILLabel (ec);
3537 switch_labels [iJump] = lbl_default;
3539 // emit the switch opcode
3540 ec.Emit (OpCodes.Switch, switch_labels);
3543 // mark the default for this block
3545 ec.MarkLabel (lbl_default);
3548 // TODO: find the default case and emit it here,
3549 // to prevent having to do the following jump.
3550 // make sure to mark other labels in the default section
3552 // the last default just goes to the end
3553 if (element_keys.Length > 0)
3554 ec.Emit (OpCodes.Br, lbl_default);
3556 // now emit the code for the sections
3557 bool found_default = false;
3559 foreach (SwitchSection ss in Sections) {
3560 foreach (SwitchLabel sl in ss.Labels) {
3561 if (sl.Converted == SwitchLabel.NullStringCase) {
3562 ec.MarkLabel (null_target);
3563 } else if (sl.Label == null) {
3564 ec.MarkLabel (lbl_default);
3565 found_default = true;
3567 ec.MarkLabel (null_target);
3569 ec.MarkLabel (sl.GetILLabel (ec));
3570 ec.MarkLabel (sl.GetILLabelCode (ec));
3575 if (!found_default) {
3576 ec.MarkLabel (lbl_default);
3577 if (!has_null_case) {
3578 ec.MarkLabel (null_target);
3582 ec.MarkLabel (lbl_end);
3585 SwitchSection FindSection (SwitchLabel label)
3587 foreach (SwitchSection ss in Sections){
3588 foreach (SwitchLabel sl in ss.Labels){
3597 public static void Reset ()
3600 allowed_types = null;
3603 public override bool Resolve (BlockContext ec)
3605 Expr = Expr.Resolve (ec);
3609 new_expr = SwitchGoverningType (ec, Expr);
3611 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3612 unwrap = Nullable.Unwrap.Create (Expr, false);
3616 new_expr = SwitchGoverningType (ec, unwrap);
3619 if (new_expr == null){
3620 ec.Report.Error (151, loc,
3621 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3622 TypeManager.CSharpName (Expr.Type));
3627 SwitchType = new_expr.Type;
3629 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3630 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3634 if (!CheckSwitch (ec))
3638 Elements.Remove (SwitchLabel.NullStringCase);
3640 Switch old_switch = ec.Switch;
3642 ec.Switch.SwitchType = SwitchType;
3644 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3645 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3647 var constant = new_expr as Constant;
3648 if (constant != null) {
3650 object key = constant.GetValue ();
3652 if (Elements.TryGetValue (key, out label))
3653 constant_section = FindSection (label);
3655 if (constant_section == null)
3656 constant_section = default_section;
3661 foreach (SwitchSection ss in Sections){
3663 ec.CurrentBranching.CreateSibling (
3664 null, FlowBranching.SiblingType.SwitchSection);
3668 if (is_constant && (ss != constant_section)) {
3669 // If we're a constant switch, we're only emitting
3670 // one single section - mark all the others as
3672 ec.CurrentBranching.CurrentUsageVector.Goto ();
3673 if (!ss.Block.ResolveUnreachable (ec, true)) {
3677 if (!ss.Block.Resolve (ec))
3682 if (default_section == null)
3683 ec.CurrentBranching.CreateSibling (
3684 null, FlowBranching.SiblingType.SwitchSection);
3686 ec.EndFlowBranching ();
3687 ec.Switch = old_switch;
3689 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3694 if (SwitchType == TypeManager.string_type && !is_constant) {
3695 // TODO: Optimize single case, and single+default case
3696 ResolveStringSwitchMap (ec);
3702 void ResolveStringSwitchMap (ResolveContext ec)
3704 FullNamedExpression string_dictionary_type;
3705 if (TypeManager.generic_ienumerable_type != null) {
3706 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3707 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3709 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3711 new TypeExpression (TypeManager.string_type, loc),
3712 new TypeExpression (TypeManager.int32_type, loc)), loc);
3714 MemberAccess system_collections_generic = new MemberAccess (
3715 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3717 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3720 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3721 Field field = new Field (ctype, string_dictionary_type,
3722 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3723 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3724 if (!field.Define ())
3726 ctype.AddField (field);
3728 var init = new List<Expression> ();
3731 string value = null;
3732 foreach (SwitchSection section in Sections) {
3733 int last_count = init.Count;
3734 foreach (SwitchLabel sl in section.Labels) {
3735 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3738 value = (string) sl.Converted;
3739 var init_args = new List<Expression> (2);
3740 init_args.Add (new StringLiteral (value, sl.Location));
3741 init_args.Add (new IntConstant (counter, loc));
3742 init.Add (new CollectionElementInitializer (init_args, loc));
3746 // Don't add empty sections
3748 if (last_count == init.Count)
3751 Elements.Add (counter, section.Labels [0]);
3755 Arguments args = new Arguments (1);
3756 args.Add (new Argument (new IntConstant (init.Count, loc)));
3757 Expression initializer = new NewInitialize (string_dictionary_type, args,
3758 new CollectionOrObjectInitializers (init, loc), loc);
3760 switch_cache_field = new FieldExpr (field, loc);
3761 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3764 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3766 Label l_initialized = ec.DefineLabel ();
3769 // Skip initialization when value is null
3771 value.EmitBranchable (ec, null_target, false);
3774 // Check if string dictionary is initialized and initialize
3776 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3777 string_dictionary.EmitStatement (ec);
3778 ec.MarkLabel (l_initialized);
3780 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3782 ResolveContext rc = new ResolveContext (ec.MemberContext);
3784 if (TypeManager.generic_ienumerable_type != null) {
3785 Arguments get_value_args = new Arguments (2);
3786 get_value_args.Add (new Argument (value));
3787 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3788 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3789 if (get_item == null)
3793 // A value was not found, go to default case
3795 get_item.EmitBranchable (ec, default_target, false);
3797 Arguments get_value_args = new Arguments (1);
3798 get_value_args.Add (new Argument (value));
3800 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3801 if (get_item == null)
3804 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3805 get_item_object.EmitAssign (ec, get_item, true, false);
3806 ec.Emit (OpCodes.Brfalse, default_target);
3808 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3809 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3811 get_item_int.EmitStatement (ec);
3812 get_item_object.Release (ec);
3815 TableSwitchEmit (ec, string_switch_variable);
3816 string_switch_variable.Release (ec);
3819 protected override void DoEmit (EmitContext ec)
3821 default_target = ec.DefineLabel ();
3822 null_target = ec.DefineLabel ();
3824 // Store variable for comparission purposes
3825 // TODO: Don't duplicate non-captured VariableReference
3826 LocalTemporary value;
3828 value = new LocalTemporary (SwitchType);
3829 unwrap.EmitCheck (ec);
3830 ec.Emit (OpCodes.Brfalse, null_target);
3833 } else if (!is_constant) {
3834 value = new LocalTemporary (SwitchType);
3841 // Setup the codegen context
3843 Label old_end = ec.LoopEnd;
3844 Switch old_switch = ec.Switch;
3846 ec.LoopEnd = ec.DefineLabel ();
3851 if (constant_section != null)
3852 constant_section.Block.Emit (ec);
3853 } else if (string_dictionary != null) {
3854 DoEmitStringSwitch (value, ec);
3856 TableSwitchEmit (ec, value);
3862 // Restore context state.
3863 ec.MarkLabel (ec.LoopEnd);
3866 // Restore the previous context
3868 ec.LoopEnd = old_end;
3869 ec.Switch = old_switch;
3872 protected override void CloneTo (CloneContext clonectx, Statement t)
3874 Switch target = (Switch) t;
3876 target.Expr = Expr.Clone (clonectx);
3877 target.Sections = new List<SwitchSection> ();
3878 foreach (SwitchSection ss in Sections){
3879 target.Sections.Add (ss.Clone (clonectx));
3884 // A place where execution can restart in an iterator
3885 public abstract class ResumableStatement : Statement
3888 protected Label resume_point;
3890 public Label PrepareForEmit (EmitContext ec)
3894 resume_point = ec.DefineLabel ();
3896 return resume_point;
3899 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3903 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3908 // Base class for statements that are implemented in terms of try...finally
3909 public abstract class ExceptionStatement : ResumableStatement
3913 List<ResumableStatement> resume_points;
3914 int first_resume_pc;
3915 protected Statement stmt;
3916 Label dispose_try_block;
3917 bool prepared_for_dispose, emitted_dispose;
3919 protected ExceptionStatement (Statement stmt, Location loc)
3927 public Statement Statement {
3935 protected abstract void EmitPreTryBody (EmitContext ec);
3936 protected abstract void EmitTryBody (EmitContext ec);
3937 protected abstract void EmitFinallyBody (EmitContext ec);
3939 protected sealed override void DoEmit (EmitContext ec)
3941 EmitPreTryBody (ec);
3943 if (resume_points != null) {
3944 ec.EmitInt ((int) Iterator.State.Running);
3945 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3948 ec.BeginExceptionBlock ();
3950 if (resume_points != null) {
3951 ec.MarkLabel (resume_point);
3953 // For normal control flow, we want to fall-through the Switch
3954 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3955 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3956 ec.EmitInt (first_resume_pc);
3957 ec.Emit (OpCodes.Sub);
3959 Label [] labels = new Label [resume_points.Count];
3960 for (int i = 0; i < resume_points.Count; ++i)
3961 labels [i] = resume_points [i].PrepareForEmit (ec);
3962 ec.Emit (OpCodes.Switch, labels);
3967 ec.BeginFinallyBlock ();
3969 Label start_finally = ec.DefineLabel ();
3970 if (resume_points != null) {
3971 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3972 ec.Emit (OpCodes.Brfalse_S, start_finally);
3973 ec.Emit (OpCodes.Endfinally);
3976 ec.MarkLabel (start_finally);
3977 EmitFinallyBody (ec);
3979 ec.EndExceptionBlock ();
3982 public void SomeCodeFollows ()
3984 code_follows = true;
3987 public override bool Resolve (BlockContext ec)
3989 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3990 // So, ensure there's some IL code after this statement.
3991 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3992 ec.NeedReturnLabel ();
3994 iter = ec.CurrentIterator;
3998 public void AddResumePoint (ResumableStatement stmt, int pc)
4000 if (resume_points == null) {
4001 resume_points = new List<ResumableStatement> ();
4002 first_resume_pc = pc;
4005 if (pc != first_resume_pc + resume_points.Count)
4006 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4008 resume_points.Add (stmt);
4011 public override Label PrepareForDispose (EmitContext ec, Label end)
4013 if (!prepared_for_dispose) {
4014 prepared_for_dispose = true;
4015 dispose_try_block = ec.DefineLabel ();
4017 return dispose_try_block;
4020 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4022 if (emitted_dispose)
4025 emitted_dispose = true;
4027 Label end_of_try = ec.DefineLabel ();
4029 // Ensure that the only way we can get into this code is through a dispatcher
4030 if (have_dispatcher)
4031 ec.Emit (OpCodes.Br, end);
4033 ec.BeginExceptionBlock ();
4035 ec.MarkLabel (dispose_try_block);
4037 Label [] labels = null;
4038 for (int i = 0; i < resume_points.Count; ++i) {
4039 ResumableStatement s = (ResumableStatement) resume_points [i];
4040 Label ret = s.PrepareForDispose (ec, end_of_try);
4041 if (ret.Equals (end_of_try) && labels == null)
4043 if (labels == null) {
4044 labels = new Label [resume_points.Count];
4045 for (int j = 0; j < i; ++j)
4046 labels [j] = end_of_try;
4051 if (labels != null) {
4053 for (j = 1; j < labels.Length; ++j)
4054 if (!labels [0].Equals (labels [j]))
4056 bool emit_dispatcher = j < labels.Length;
4058 if (emit_dispatcher) {
4059 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4060 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4061 ec.EmitInt (first_resume_pc);
4062 ec.Emit (OpCodes.Sub);
4063 ec.Emit (OpCodes.Switch, labels);
4064 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4067 foreach (ResumableStatement s in resume_points)
4068 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4071 ec.MarkLabel (end_of_try);
4073 ec.BeginFinallyBlock ();
4075 EmitFinallyBody (ec);
4077 ec.EndExceptionBlock ();
4081 public class Lock : ExceptionStatement {
4083 TemporaryVariableReference temp;
4085 public Lock (Expression expr, Statement stmt, Location loc)
4091 public override bool Resolve (BlockContext ec)
4093 expr = expr.Resolve (ec);
4097 if (!TypeManager.IsReferenceType (expr.Type)){
4098 ec.Report.Error (185, loc,
4099 "`{0}' is not a reference type as required by the lock statement",
4100 TypeManager.CSharpName (expr.Type));
4104 ec.StartFlowBranching (this);
4105 bool ok = Statement.Resolve (ec);
4106 ec.EndFlowBranching ();
4108 ok &= base.Resolve (ec);
4110 temp = TemporaryVariableReference.Create (expr.Type, ec.CurrentBlock.Parent, loc);
4113 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4114 TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4115 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4116 monitor_type, "Enter", loc, TypeManager.object_type);
4117 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4118 monitor_type, "Exit", loc, TypeManager.object_type);
4124 protected override void EmitPreTryBody (EmitContext ec)
4126 temp.EmitAssign (ec, expr);
4128 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4131 protected override void EmitTryBody (EmitContext ec)
4133 Statement.Emit (ec);
4136 protected override void EmitFinallyBody (EmitContext ec)
4139 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4142 protected override void CloneTo (CloneContext clonectx, Statement t)
4144 Lock target = (Lock) t;
4146 target.expr = expr.Clone (clonectx);
4147 target.stmt = Statement.Clone (clonectx);
4151 public class Unchecked : Statement {
4154 public Unchecked (Block b, Location loc)
4161 public override bool Resolve (BlockContext ec)
4163 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4164 return Block.Resolve (ec);
4167 protected override void DoEmit (EmitContext ec)
4169 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4173 protected override void CloneTo (CloneContext clonectx, Statement t)
4175 Unchecked target = (Unchecked) t;
4177 target.Block = clonectx.LookupBlock (Block);
4181 public class Checked : Statement {
4184 public Checked (Block b, Location loc)
4187 b.Unchecked = false;
4191 public override bool Resolve (BlockContext ec)
4193 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4194 return Block.Resolve (ec);
4197 protected override void DoEmit (EmitContext ec)
4199 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4203 protected override void CloneTo (CloneContext clonectx, Statement t)
4205 Checked target = (Checked) t;
4207 target.Block = clonectx.LookupBlock (Block);
4211 public class Unsafe : Statement {
4214 public Unsafe (Block b, Location loc)
4217 Block.Unsafe = true;
4221 public override bool Resolve (BlockContext ec)
4223 if (ec.CurrentIterator != null)
4224 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4226 using (ec.Set (ResolveContext.Options.UnsafeScope))
4227 return Block.Resolve (ec);
4230 protected override void DoEmit (EmitContext ec)
4235 protected override void CloneTo (CloneContext clonectx, Statement t)
4237 Unsafe target = (Unsafe) t;
4239 target.Block = clonectx.LookupBlock (Block);
4246 public class Fixed : Statement
4248 abstract class Emitter : ShimExpression
4250 protected LocalVariable vi;
4252 protected Emitter (Expression expr, LocalVariable li)
4258 public abstract void EmitExit (EmitContext ec);
4261 class ExpressionEmitter : Emitter {
4262 public ExpressionEmitter (Expression converted, LocalVariable li) :
4263 base (converted, li)
4267 protected override Expression DoResolve (ResolveContext rc)
4269 throw new NotImplementedException ();
4272 public override void Emit (EmitContext ec) {
4274 // Store pointer in pinned location
4280 public override void EmitExit (EmitContext ec)
4282 ec.Emit (OpCodes.Ldc_I4_0);
4283 ec.Emit (OpCodes.Conv_U);
4288 class StringEmitter : Emitter
4290 LocalVariable pinned_string;
4292 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4297 protected override Expression DoResolve (ResolveContext rc)
4299 pinned_string = new LocalVariable (vi.Block, "$pinned",
4300 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4303 pinned_string.Type = TypeManager.string_type;
4305 if (TypeManager.int_get_offset_to_string_data == null) {
4306 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4307 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4310 eclass = ExprClass.Variable;
4311 type = TypeManager.int32_type;
4315 public override void Emit (EmitContext ec)
4317 pinned_string.CreateBuilder (ec);
4320 pinned_string.EmitAssign (ec);
4322 // TODO: Should use Binary::Add
4323 pinned_string.Emit (ec);
4324 ec.Emit (OpCodes.Conv_I);
4326 PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4327 //pe.InstanceExpression = pinned_string;
4328 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4330 ec.Emit (OpCodes.Add);
4334 public override void EmitExit (EmitContext ec)
4336 ec.Emit (OpCodes.Ldnull);
4337 pinned_string.EmitAssign (ec);
4341 public class VariableDeclaration : BlockVariableDeclaration
4343 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4348 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4350 if (!Variable.Type.IsPointer && li == Variable) {
4351 bc.Report.Error (209, TypeExpression.Location,
4352 "The type of locals declared in a fixed statement must be a pointer type");
4357 // The rules for the possible declarators are pretty wise,
4358 // but the production on the grammar is more concise.
4360 // So we have to enforce these rules here.
4362 // We do not resolve before doing the case 1 test,
4363 // because the grammar is explicit in that the token &
4364 // is present, so we need to test for this particular case.
4367 if (initializer is Cast) {
4368 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4372 initializer = initializer.Resolve (bc);
4374 if (initializer == null)
4380 if (initializer.Type.IsArray) {
4381 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4384 // Provided that array_type is unmanaged,
4386 if (!TypeManager.VerifyUnmanaged (bc.Compiler, array_type, loc))
4390 // and T* is implicitly convertible to the
4391 // pointer type given in the fixed statement.
4393 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4395 Expression converted = Convert.ImplicitConversionRequired (
4396 bc, array_ptr, li.Type, loc);
4397 if (converted == null)
4401 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4403 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4404 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4405 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (0, loc), loc), loc)),
4406 new NullPointer (loc),
4409 converted = converted.Resolve (bc);
4411 return new ExpressionEmitter (converted, li);
4417 if (initializer.Type == TypeManager.string_type) {
4418 return new StringEmitter (initializer, li, loc).Resolve (bc);
4421 // Case 3: fixed buffer
4422 if (initializer is FixedBufferPtr) {
4423 return new ExpressionEmitter (initializer, li);
4427 // Case 4: & object.
4429 bool already_fixed = true;
4430 Unary u = initializer as Unary;
4431 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4432 IVariableReference vr = u.Expr as IVariableReference;
4433 if (vr == null || !vr.IsFixed) {
4434 already_fixed = false;
4438 if (already_fixed) {
4439 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4442 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4443 return new ExpressionEmitter (initializer, li);
4448 VariableDeclaration decl;
4449 Statement statement;
4452 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4461 public Statement Statement {
4467 public BlockVariableDeclaration Variables {
4475 public override bool Resolve (BlockContext ec)
4477 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4478 if (!decl.Resolve (ec))
4482 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4483 bool ok = statement.Resolve (ec);
4484 bool flow_unreachable = ec.EndFlowBranching ();
4485 has_ret = flow_unreachable;
4490 protected override void DoEmit (EmitContext ec)
4492 decl.Variable.CreateBuilder (ec);
4493 decl.Initializer.Emit (ec);
4494 if (decl.Declarators != null) {
4495 foreach (var d in decl.Declarators) {
4496 d.Variable.CreateBuilder (ec);
4497 d.Initializer.Emit (ec);
4501 statement.Emit (ec);
4507 // Clear the pinned variable
4509 ((Emitter) decl.Initializer).EmitExit (ec);
4510 if (decl.Declarators != null) {
4511 foreach (var d in decl.Declarators) {
4512 ((Emitter)d.Initializer).EmitExit (ec);
4517 protected override void CloneTo (CloneContext clonectx, Statement t)
4519 Fixed target = (Fixed) t;
4521 target.decl = (VariableDeclaration) decl.Clone (clonectx);
4522 target.statement = statement.Clone (clonectx);
4526 public class Catch : Statement
4530 FullNamedExpression type_expr;
4531 CompilerAssign assign;
4534 public Catch (Block block, Location loc)
4542 public Block Block {
4548 public TypeSpec CatchType {
4554 public bool IsGeneral {
4556 return type_expr == null;
4560 public FullNamedExpression TypeExpression {
4569 public LocalVariable Variable {
4580 protected override void DoEmit (EmitContext ec)
4583 ec.BeginCatchBlock (TypeManager.object_type);
4585 ec.BeginCatchBlock (CatchType);
4588 li.CreateBuilder (ec);
4591 // Special case hoisted catch variable, we have to use a temporary variable
4592 // to pass via anonymous storey initialization with the value still on top
4595 if (li.HoistedVariant != null) {
4596 LocalTemporary lt = new LocalTemporary (li.Type);
4597 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4599 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4601 // switch to assigning from the temporary variable and not from top of the stack
4602 assign.UpdateSource (lt);
4605 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4606 ec.Emit (OpCodes.Pop);
4607 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4613 public override bool Resolve (BlockContext ec)
4615 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4616 if (type_expr != null) {
4617 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4622 if (type != TypeManager.exception_type && !TypeSpec.IsBaseClass (type, TypeManager.exception_type, false)) {
4623 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4624 } else if (li != null) {
4626 li.PrepareForFlowAnalysis (ec);
4628 // source variable is at the top of the stack
4629 Expression source = new EmptyExpression (li.Type);
4630 if (li.Type.IsGenericParameter)
4631 source = new UnboxCast (source, li.Type);
4633 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
4634 Block.AddScopeStatement (new StatementExpression (assign));
4638 return Block.Resolve (ec);
4642 protected override void CloneTo (CloneContext clonectx, Statement t)
4644 Catch target = (Catch) t;
4646 if (type_expr != null)
4647 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
4649 target.block = clonectx.LookupBlock (block);
4653 public class TryFinally : ExceptionStatement {
4656 public TryFinally (Statement stmt, Block fini, Location loc)
4662 public override bool Resolve (BlockContext ec)
4666 ec.StartFlowBranching (this);
4668 if (!stmt.Resolve (ec))
4672 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4673 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4674 if (!fini.Resolve (ec))
4678 ec.EndFlowBranching ();
4680 ok &= base.Resolve (ec);
4685 protected override void EmitPreTryBody (EmitContext ec)
4689 protected override void EmitTryBody (EmitContext ec)
4694 protected override void EmitFinallyBody (EmitContext ec)
4699 protected override void CloneTo (CloneContext clonectx, Statement t)
4701 TryFinally target = (TryFinally) t;
4703 target.stmt = (Statement) stmt.Clone (clonectx);
4705 target.fini = clonectx.LookupBlock (fini);
4709 public class TryCatch : Statement {
4711 public List<Catch> Specific;
4712 public Catch General;
4713 bool inside_try_finally, code_follows;
4715 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4718 this.Specific = catch_clauses;
4719 this.inside_try_finally = inside_try_finally;
4721 Catch c = catch_clauses [0];
4724 catch_clauses.RemoveAt (0);
4730 public override bool Resolve (BlockContext ec)
4734 ec.StartFlowBranching (this);
4736 if (!Block.Resolve (ec))
4739 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4741 foreach (Catch c in Specific){
4742 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4744 if (!c.Resolve (ec)) {
4749 TypeSpec resolved_type = c.CatchType;
4750 for (int ii = 0; ii < last_index; ++ii) {
4751 if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4752 ec.Report.Error (160, c.loc,
4753 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4754 TypeManager.CSharpName (prev_catches [ii]));
4759 prev_catches [last_index++] = resolved_type;
4762 if (General != null) {
4763 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4764 foreach (Catch c in Specific){
4765 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4766 ec.Report.Warning (1058, 1, c.loc, "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4771 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4773 if (!General.Resolve (ec))
4777 ec.EndFlowBranching ();
4779 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4780 // So, ensure there's some IL code after this statement
4781 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4782 ec.NeedReturnLabel ();
4787 public void SomeCodeFollows ()
4789 code_follows = true;
4792 protected override void DoEmit (EmitContext ec)
4794 if (!inside_try_finally)
4795 ec.BeginExceptionBlock ();
4799 foreach (Catch c in Specific)
4802 if (General != null)
4805 if (!inside_try_finally)
4806 ec.EndExceptionBlock ();
4809 protected override void CloneTo (CloneContext clonectx, Statement t)
4811 TryCatch target = (TryCatch) t;
4813 target.Block = clonectx.LookupBlock (Block);
4814 if (General != null)
4815 target.General = (Catch) General.Clone (clonectx);
4816 if (Specific != null){
4817 target.Specific = new List<Catch> ();
4818 foreach (Catch c in Specific)
4819 target.Specific.Add ((Catch) c.Clone (clonectx));
4824 // FIXME: Why is it almost exact copy of Using ??
4825 public class UsingTemporary : ExceptionStatement
4827 protected TemporaryVariableReference local_copy;
4829 protected Statement dispose_call;
4831 public UsingTemporary (Expression expr, Statement stmt, Location loc)
4838 public Expression Expression {
4846 protected virtual bool DoResolve (BlockContext ec)
4848 expr = expr.Resolve (ec);
4852 if (expr.Type != TypeManager.idisposable_type && !expr.Type.ImplementsInterface (TypeManager.idisposable_type, false)) {
4853 if (TypeManager.IsNullableType (expr.Type)) {
4854 // it's handled it a custom code bellow
4855 } else if (expr.Type == InternalType.Dynamic) {
4856 expr = Convert.ImplicitConversionStandard (ec, expr, TypeManager.idisposable_type, loc);
4858 Using.Error_IsNotConvertibleToIDisposable (ec, expr.Type, expr.Location);
4863 var expr_type = expr.Type;
4865 local_copy = TemporaryVariableReference.Create (expr_type, ec.CurrentBlock.Parent, loc);
4866 local_copy.Resolve (ec);
4868 if (TypeManager.void_dispose_void == null) {
4869 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4870 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4873 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
4874 dispose_mg.InstanceExpression = TypeManager.IsNullableType (expr_type) ?
4875 new Cast (new TypeExpression (TypeManager.idisposable_type, loc), local_copy, loc).Resolve (ec) :
4878 dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
4880 // Add conditional call when disposing possible null variable
4881 if (!expr_type.IsStruct || TypeManager.IsNullableType (expr_type))
4882 dispose_call = new If (new Binary (Binary.Operator.Inequality, local_copy, new NullLiteral (loc), loc), dispose_call, loc);
4884 return dispose_call.Resolve (ec);
4887 public override bool Resolve (BlockContext ec)
4889 bool ok = DoResolve (ec);
4891 ec.StartFlowBranching (this);
4893 ok &= stmt.Resolve (ec);
4895 ec.EndFlowBranching ();
4897 ok &= base.Resolve (ec);
4902 protected override void EmitPreTryBody (EmitContext ec)
4904 //local_copy.Variable.
4905 local_copy.EmitAssign (ec, expr);
4908 protected override void EmitTryBody (EmitContext ec)
4913 protected override void EmitFinallyBody (EmitContext ec)
4915 dispose_call.Emit (ec);
4918 protected override void CloneTo (CloneContext clonectx, Statement t)
4920 UsingTemporary target = (UsingTemporary) t;
4922 target.expr = expr.Clone (clonectx);
4923 target.stmt = stmt.Clone (clonectx);
4927 public class Using : ExceptionStatement
4929 public class VariableDeclaration : BlockVariableDeclaration
4931 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4936 private VariableDeclaration (LocalVariable li, Location loc)
4942 public override bool Resolve (BlockContext bc)
4944 if (type_expr == null)
4947 return base.Resolve (bc);
4950 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4954 if (li.Type == InternalType.Dynamic) {
4955 initializer = initializer.Resolve (bc);
4956 if (initializer == null)
4959 initializer = Convert.ImplicitConversionRequired (bc, initializer, TypeManager.idisposable_type, loc);
4960 if (initializer == null)
4963 var var = TemporaryVariableReference.Create (TypeManager.idisposable_type, bc.CurrentBlock, loc);
4964 assign = new SimpleAssign (var, initializer, loc);
4965 assign.ResolveStatement (bc);
4969 if (li == Variable) {
4970 if (li.Type != TypeManager.idisposable_type && !li.Type.ImplementsInterface (TypeManager.idisposable_type, false)) {
4971 Using.Error_IsNotConvertibleToIDisposable (bc, li.Type, type_expr.Location);
4976 return base.ResolveInitializer (bc, li, initializer);
4979 public Statement RewriteForDeclarators (BlockContext bc, Statement stmt)
4981 for (int i = declarators.Count - 1; i >= 0; --i) {
4982 var d = declarators [i];
4983 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
4984 vd.Initializer = d.Initializer;
4985 stmt = new Using (vd, stmt, d.Variable.Location);
4993 VariableDeclaration decl;
4995 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5003 public BlockVariableDeclaration Variables {
5011 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, TypeSpec type, Location loc)
5013 ec.Report.SymbolRelatedToPreviousError (type);
5014 ec.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5015 type.GetSignatureForError ());
5018 protected override void EmitPreTryBody (EmitContext ec)
5023 protected override void EmitTryBody (EmitContext ec)
5028 protected override void EmitFinallyBody (EmitContext ec)
5030 Label skip = ec.DefineLabel ();
5032 var var = ((Assign) decl.Initializer).Target;
5033 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5034 if (emit_null_check) {
5036 ec.Emit (OpCodes.Brfalse, skip);
5039 Invocation.EmitCall (ec, var, TypeManager.void_dispose_void, null, loc);
5041 if (emit_null_check)
5042 ec.MarkLabel (skip);
5045 public override bool Resolve (BlockContext ec)
5047 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5048 if (!decl.Resolve (ec))
5051 if (decl.Declarators != null) {
5052 stmt = decl.RewriteForDeclarators (ec, stmt);
5056 ec.StartFlowBranching (this);
5058 bool ok = stmt.Resolve (ec);
5060 ec.EndFlowBranching ();
5062 ok &= base.Resolve (ec);
5064 if (TypeManager.void_dispose_void == null) {
5065 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5066 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5072 protected override void CloneTo (CloneContext clonectx, Statement t)
5074 Using target = (Using) t;
5076 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5077 target.stmt = stmt.Clone (clonectx);
5082 /// Implementation of the foreach C# statement
5084 public class Foreach : Statement {
5086 sealed class ArrayForeach : Statement
5088 readonly Foreach for_each;
5089 readonly Statement statement;
5092 TemporaryVariableReference[] lengths;
5093 Expression [] length_exprs;
5094 StatementExpression[] counter;
5095 TemporaryVariableReference[] variables;
5097 TemporaryVariableReference copy;
5099 LocalVariableReference variable;
5101 public ArrayForeach (Foreach @foreach, int rank)
5103 for_each = @foreach;
5104 statement = for_each.statement;
5106 variable = new LocalVariableReference (for_each.variable, loc);
5108 counter = new StatementExpression[rank];
5109 variables = new TemporaryVariableReference[rank];
5110 length_exprs = new Expression [rank];
5113 // Only use temporary length variables when dealing with
5114 // multi-dimensional arrays
5117 lengths = new TemporaryVariableReference [rank];
5120 protected override void CloneTo (CloneContext clonectx, Statement target)
5122 throw new NotImplementedException ();
5125 public override bool Resolve (BlockContext ec)
5127 Block variables_block = variable.local_info.Block;
5128 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5131 int rank = length_exprs.Length;
5132 Arguments list = new Arguments (rank);
5133 for (int i = 0; i < rank; i++) {
5134 var v = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5136 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5137 counter[i].Resolve (ec);
5140 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5142 lengths[i] = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5143 lengths[i].Resolve (ec);
5145 Arguments args = new Arguments (1);
5146 args.Add (new Argument (new IntConstant (i, loc)));
5147 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5150 list.Add (new Argument (v));
5153 access = new ElementAccess (copy, list, loc).Resolve (ec);
5157 Expression var_type = for_each.type;
5158 VarExpr ve = var_type as VarExpr;
5160 // Infer implicitly typed local variable from foreach array type
5161 var_type = new TypeExpression (access.Type, ve.Location);
5164 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5165 if (var_type == null)
5168 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5174 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5175 ec.CurrentBranching.CreateSibling ();
5178 variable.local_info.Type = conv.Type;
5179 variable.Resolve (ec);
5181 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5182 if (!statement.Resolve (ec))
5184 ec.EndFlowBranching ();
5186 // There's no direct control flow from the end of the embedded statement to the end of the loop
5187 ec.CurrentBranching.CurrentUsageVector.Goto ();
5189 ec.EndFlowBranching ();
5194 protected override void DoEmit (EmitContext ec)
5196 copy.EmitAssign (ec, for_each.expr);
5198 int rank = length_exprs.Length;
5199 Label[] test = new Label [rank];
5200 Label[] loop = new Label [rank];
5202 for (int i = 0; i < rank; i++) {
5203 test [i] = ec.DefineLabel ();
5204 loop [i] = ec.DefineLabel ();
5206 if (lengths != null)
5207 lengths [i].EmitAssign (ec, length_exprs [i]);
5210 IntConstant zero = new IntConstant (0, loc);
5211 for (int i = 0; i < rank; i++) {
5212 variables [i].EmitAssign (ec, zero);
5214 ec.Emit (OpCodes.Br, test [i]);
5215 ec.MarkLabel (loop [i]);
5218 variable.local_info.CreateBuilder (ec);
5219 variable.EmitAssign (ec, conv, false, false);
5221 statement.Emit (ec);
5223 ec.MarkLabel (ec.LoopBegin);
5225 for (int i = rank - 1; i >= 0; i--){
5226 counter [i].Emit (ec);
5228 ec.MarkLabel (test [i]);
5229 variables [i].Emit (ec);
5231 if (lengths != null)
5232 lengths [i].Emit (ec);
5234 length_exprs [i].Emit (ec);
5236 ec.Emit (OpCodes.Blt, loop [i]);
5239 ec.MarkLabel (ec.LoopEnd);
5243 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5245 class Body : Statement
5248 LocalVariableReference variable;
5249 Expression current, conv;
5250 Statement statement;
5252 public Body (TypeSpec type, LocalVariable variable,
5253 Expression current, Statement statement,
5257 this.variable = new LocalVariableReference (variable, loc);
5258 this.current = current;
5259 this.statement = statement;
5263 protected override void CloneTo (CloneContext clonectx, Statement target)
5265 throw new NotImplementedException ();
5268 public override bool Resolve (BlockContext ec)
5270 current = current.Resolve (ec);
5271 if (current == null)
5274 conv = Convert.ExplicitConversion (ec, current, type, loc);
5278 variable.local_info.Type = conv.Type;
5279 variable.Resolve (ec);
5281 if (!statement.Resolve (ec))
5287 protected override void DoEmit (EmitContext ec)
5289 variable.local_info.CreateBuilder (ec);
5290 variable.EmitAssign (ec, conv, false, false);
5292 statement.Emit (ec);
5296 class Dispose : UsingTemporary
5298 LocalTemporary dispose;
5300 public Dispose (TemporaryVariableReference variable, LocalTemporary dispose, Expression expr, Statement statement, Location loc)
5301 : base (expr, statement, loc)
5303 base.local_copy = variable;
5304 this.dispose = dispose;
5307 protected override bool DoResolve (BlockContext ec)
5309 if (TypeManager.void_dispose_void == null) {
5310 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5311 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5314 Expression dispose_var = (Expression) dispose ?? local_copy;
5316 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5317 dispose_mg.InstanceExpression = dispose_var;
5319 dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
5321 if (!dispose_var.Type.IsStruct)
5322 dispose_call = new If (new Binary (Binary.Operator.Inequality, dispose_var, new NullLiteral (loc), loc), dispose_call, loc);
5324 return dispose_call.Resolve (ec);
5327 protected override void EmitFinallyBody (EmitContext ec)
5329 Label call_dispose = ec.DefineLabel ();
5330 if (dispose != null) {
5331 local_copy.Emit (ec, false);
5332 ec.Emit (OpCodes.Isinst, dispose.Type);
5336 base.EmitFinallyBody (ec);
5338 if (dispose != null) {
5339 ec.MarkLabel (call_dispose);
5340 dispose.Release (ec);
5345 LocalVariable variable;
5347 Statement statement;
5348 Expression var_type;
5349 ExpressionStatement init;
5350 TemporaryVariableReference enumerator_variable;
5351 bool ambiguous_getenumerator_name;
5353 public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5355 this.var_type = var_type;
5356 this.variable = var;
5362 protected override void CloneTo (CloneContext clonectx, Statement target)
5364 throw new NotImplementedException ();
5367 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5369 rc.Report.SymbolRelatedToPreviousError (enumerator);
5370 rc.Report.Error (202, loc,
5371 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5372 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5375 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5378 // Option 1: Try to match by name GetEnumerator first
5380 var mexpr = Expression.MemberLookup (rc, rc.CurrentType, expr.Type,
5381 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5383 var mg = mexpr as MethodGroupExpr;
5385 mg.InstanceExpression = expr;
5386 Arguments args = new Arguments (0);
5387 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5389 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5390 if (ambiguous_getenumerator_name)
5393 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5399 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5401 TypeSpec iface_candidate = null;
5402 for (TypeSpec t = expr.Type; t != null && t != TypeManager.object_type; t = t.BaseType) {
5403 var ifaces = t.Interfaces;
5404 if (ifaces != null) {
5405 foreach (var iface in ifaces) {
5406 if (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) {
5407 if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) {
5408 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5409 rc.Report.Error (1640, loc,
5410 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5411 expr.Type.GetSignatureForError (), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5416 iface_candidate = iface;
5420 if (iface == TypeManager.ienumerable_type && iface_candidate == null) {
5421 iface_candidate = iface;
5427 if (iface_candidate == null) {
5428 rc.Report.Error (1579, loc,
5429 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5430 expr.Type.GetSignatureForError (), "GetEnumerator");
5435 var method = TypeManager.GetPredefinedMethod (iface_candidate,
5436 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null), loc);
5441 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5442 mg.InstanceExpression = expr;
5446 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5448 var ms = MemberCache.FindMember (enumerator.ReturnType,
5449 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5450 BindingRestriction.InstanceOnly) as MethodSpec;
5452 if (ms == null || !ms.IsPublic) {
5453 Error_WrongEnumerator (rc, enumerator);
5457 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5460 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5462 var ps = MemberCache.FindMember (enumerator.ReturnType,
5463 MemberFilter.Property ("Current", null),
5464 BindingRestriction.InstanceOnly) as PropertySpec;
5466 if (ps == null || !ps.IsPublic) {
5467 Error_WrongEnumerator (rc, enumerator);
5474 public override bool Resolve (BlockContext ec)
5476 bool is_dynamic = expr.Type == InternalType.Dynamic;
5478 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5480 var get_enumerator_mg = ResolveGetEnumerator (ec);
5481 if (get_enumerator_mg == null) {
5485 var get_enumerator = get_enumerator_mg.BestCandidate;
5486 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
5487 enumerator_variable.Resolve (ec);
5489 // Prepare bool MoveNext ()
5490 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5491 if (move_next_mg == null) {
5495 move_next_mg.InstanceExpression = enumerator_variable;
5497 // Prepare ~T~ Current { get; }
5498 var current_prop = ResolveCurrent (ec, get_enumerator);
5499 if (current_prop == null) {
5503 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
5504 if (current_pe == null)
5507 VarExpr ve = var_type as VarExpr;
5510 // Source type is dynamic, set element type to dynamic too
5511 var_type = new TypeExpression (InternalType.Dynamic, var_type.Location);
5513 // Infer implicitly typed local variable from foreach enumerable type
5514 var_type = new TypeExpression (current_pe.Type, var_type.Location);
5516 } else if (is_dynamic) {
5517 // Explicit cast of dynamic collection elements has to be done at runtime
5518 current_pe = EmptyCast.Create (current_pe, InternalType.Dynamic);
5521 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5522 if (var_type == null)
5525 variable.Type = var_type.Type;
5527 var init = new Invocation (get_enumerator_mg, null);
5530 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5531 new Body (var_type.Type, variable, current_pe, statement, loc), loc);
5533 var enum_type = enumerator_variable.Type;
5536 // Add Dispose method call when enumerator can be IDisposable
5538 if (!enum_type.ImplementsInterface (TypeManager.idisposable_type, false)) {
5539 if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) {
5541 // Runtime Dispose check
5543 var tv = new LocalTemporary (TypeManager.idisposable_type);
5544 statement = new Dispose (enumerator_variable, tv, init, statement, loc);
5547 // No Dispose call needed
5549 this.init = new SimpleAssign (enumerator_variable, init);
5550 this.init.Resolve (ec);
5554 // Static Dispose check
5556 statement = new Dispose (enumerator_variable, null, init, statement, loc);
5559 return statement.Resolve (ec);
5562 protected override void DoEmit (EmitContext ec)
5564 enumerator_variable.LocalInfo.CreateBuilder (ec);
5567 init.EmitStatement (ec);
5569 statement.Emit (ec);
5572 #region IErrorHandler Members
5574 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5576 ec.Report.SymbolRelatedToPreviousError (best);
5577 ec.Report.Warning (278, 2, loc,
5578 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5579 expr.Type.GetSignatureForError (), "enumerable",
5580 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5582 ambiguous_getenumerator_name = true;
5586 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5591 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5596 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5605 LocalVariable variable;
5607 Statement statement;
5609 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
5612 this.variable = var;
5618 public Statement Statement {
5619 get { return statement; }
5622 public override bool Resolve (BlockContext ec)
5624 expr = expr.Resolve (ec);
5629 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5633 if (expr.Type == TypeManager.string_type) {
5634 statement = new ArrayForeach (this, 1);
5635 } else if (expr.Type is ArrayContainer) {
5636 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5638 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5639 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5640 expr.ExprClassName);
5644 statement = new CollectionForeach (type, variable, expr, statement, loc);
5647 return statement.Resolve (ec);
5650 protected override void DoEmit (EmitContext ec)
5652 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5653 ec.LoopBegin = ec.DefineLabel ();
5654 ec.LoopEnd = ec.DefineLabel ();
5656 statement.Emit (ec);
5658 ec.LoopBegin = old_begin;
5659 ec.LoopEnd = old_end;
5662 protected override void CloneTo (CloneContext clonectx, Statement t)
5664 Foreach target = (Foreach) t;
5666 target.type = type.Clone (clonectx);
5667 target.expr = expr.Clone (clonectx);
5668 target.statement = statement.Clone (clonectx);