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 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
2022 // Some statements are wrapped by a Block. Since
2023 // others' internal could be changed, here I treat
2024 // them as possibly wrapped by Block equally.
2025 Block b = s as Block;
2026 if (b != null && b.statements.Count == 1)
2027 s = b.statements [0];
2030 body = ((Lock) s).Statement;
2032 body = ((For) s).Statement;
2033 else if (s is Foreach)
2034 body = ((Foreach) s).Statement;
2035 else if (s is While)
2036 body = ((While) s).Statement;
2037 else if (s is Fixed)
2038 body = ((Fixed) s).Statement;
2039 else if (s is Using)
2040 body = ((Using) s).Statement;
2041 else if (s is UsingTemporary)
2042 body = ((UsingTemporary) s).Statement;
2046 if (body == null || body is EmptyStatement)
2047 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2050 public override bool Resolve (BlockContext ec)
2052 Block prev_block = ec.CurrentBlock;
2055 ec.CurrentBlock = this;
2056 ec.StartFlowBranching (this);
2058 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2061 // Compiler generated scope statements
2063 if (scope_initializers != null) {
2064 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2065 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2068 resolving_init_idx = null;
2072 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2073 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2074 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2075 // responsible for handling the situation.
2077 int statement_count = statements.Count;
2078 for (int ix = 0; ix < statement_count; ix++){
2079 Statement s = statements [ix];
2080 // Check possible empty statement (CS0642)
2081 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2082 statements [ix + 1] is ExplicitBlock)
2083 CheckPossibleMistakenEmptyStatement (ec, s);
2086 // Warn if we detect unreachable code.
2089 if (s is EmptyStatement)
2092 if (!unreachable_shown && !(s is LabeledStatement)) {
2093 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2094 unreachable_shown = true;
2097 Block c_block = s as Block;
2098 if (c_block != null)
2099 c_block.unreachable = c_block.unreachable_shown = true;
2103 // Note that we're not using ResolveUnreachable() for unreachable
2104 // statements here. ResolveUnreachable() creates a temporary
2105 // flow branching and kills it afterwards. This leads to problems
2106 // if you have two unreachable statements where the first one
2107 // assigns a variable and the second one tries to access it.
2110 if (!s.Resolve (ec)) {
2112 if (ec.IsInProbingMode)
2115 statements [ix] = new EmptyStatement (s.loc);
2119 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2120 statements [ix] = new EmptyStatement (s.loc);
2122 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2123 if (unreachable && s is LabeledStatement)
2124 throw new InternalErrorException ("should not happen");
2127 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2128 ec.CurrentBranching, statement_count);
2130 while (ec.CurrentBranching is FlowBranchingLabeled)
2131 ec.EndFlowBranching ();
2133 bool flow_unreachable = ec.EndFlowBranching ();
2135 ec.CurrentBlock = prev_block;
2137 if (flow_unreachable)
2138 flags |= Flags.HasRet;
2140 // If we're a non-static `struct' constructor which doesn't have an
2141 // initializer, then we must initialize all of the struct's fields.
2142 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2145 if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2146 foreach (LabeledStatement label in labels.Values)
2147 if (!label.HasBeenReferenced)
2148 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2154 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2156 unreachable_shown = true;
2160 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2162 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2163 bool ok = Resolve (ec);
2164 ec.KillFlowBranching ();
2169 protected override void DoEmit (EmitContext ec)
2171 for (int ix = 0; ix < statements.Count; ix++){
2172 statements [ix].Emit (ec);
2176 public override void Emit (EmitContext ec)
2178 if (scope_initializers != null)
2179 EmitScopeInitializers (ec);
2181 ec.Mark (StartLocation);
2184 if (SymbolWriter.HasSymbolWriter)
2185 EmitSymbolInfo (ec);
2188 protected void EmitScopeInitializers (EmitContext ec)
2190 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2192 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2193 foreach (Statement s in scope_initializers)
2197 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2200 protected virtual void EmitSymbolInfo (EmitContext ec)
2205 public override string ToString ()
2207 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2211 protected override void CloneTo (CloneContext clonectx, Statement t)
2213 Block target = (Block) t;
2215 target.clone_id = clone_id_counter++;
2218 clonectx.AddBlockMap (this, target);
2220 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2221 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2224 target.Parent = clonectx.RemapBlockCopy (Parent);
2226 target.statements = new List<Statement> (statements.Count);
2227 foreach (Statement s in statements)
2228 target.statements.Add (s.Clone (clonectx));
2232 public class ExplicitBlock : Block
2234 protected AnonymousMethodStorey am_storey;
2236 public ExplicitBlock (Block parent, Location start, Location end)
2237 : this (parent, (Flags) 0, start, end)
2241 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2242 : base (parent, flags, start, end)
2244 this.Explicit = this;
2249 public AnonymousMethodStorey AnonymousMethodStorey {
2255 public bool HasCapturedThis {
2256 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2258 return (flags & Flags.HasCapturedThis) != 0;
2262 public bool HasCapturedVariable {
2263 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2265 return (flags & Flags.HasCapturedVariable) != 0;
2272 // Creates anonymous method storey in current block
2274 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2277 // An iterator has only 1 storey block
2279 if (ec.CurrentIterator != null)
2280 return ec.CurrentIterator.Storey;
2283 // When referencing a variable in iterator storey from children anonymous method
2285 if (ParametersBlock.am_storey is IteratorStorey) {
2286 return ParametersBlock.am_storey;
2290 // Switch block does not follow sequential flow and we cannot emit
2291 // storey initialization inside the block because it can be jumped over
2292 // for all non-first cases. Instead we push it up to the parent block to be
2293 // always initialized
2295 if (switch_block != null)
2296 return switch_block.CreateAnonymousMethodStorey (ec);
2298 if (am_storey == null) {
2299 MemberBase mc = ec.MemberContext as MemberBase;
2302 // Creates anonymous method storey for this block
2304 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2310 public override void Emit (EmitContext ec)
2312 if (am_storey != null) {
2313 DefineAnonymousStorey (ec);
2314 am_storey.EmitStoreyInstantiation (ec);
2317 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2318 if (emit_debug_info)
2323 if (emit_debug_info)
2327 void DefineAnonymousStorey (EmitContext ec)
2330 // Creates anonymous method storey
2332 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2334 // Creates parent storey reference when hoisted this is accessible
2336 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2337 ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2340 // Hoisted this exists in top-level parent storey only
2342 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2343 parent = parent.Parent.Explicit;
2345 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2348 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2350 // TODO MemberCache: Review
2351 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2354 am_storey.CreateType ();
2355 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2356 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2358 am_storey.DefineType ();
2359 am_storey.ResolveTypeParameters ();
2361 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2362 if (ref_blocks != null) {
2363 foreach (ExplicitBlock ref_block in ref_blocks) {
2364 for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2365 if (b.am_storey != null) {
2366 b.am_storey.AddParentStoreyReference (ec, am_storey);
2368 // Stop propagation inside same top block
2369 if (b.ParametersBlock.am_storey == ParametersBlock.am_storey)
2372 b = b.ParametersBlock;
2375 b.HasCapturedVariable = true;
2380 am_storey.Define ();
2381 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2385 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2387 tryBlock.statements = statements;
2388 statements = new List<Statement> (1);
2389 statements.Add (tf);
2394 // ParametersBlock was introduced to support anonymous methods
2395 // and lambda expressions
2397 public class ParametersBlock : ExplicitBlock
2399 public class ParameterInfo : INamedBlockVariable
2401 readonly ParametersBlock block;
2403 public VariableInfo VariableInfo;
2405 public ParameterInfo (ParametersBlock block, int index)
2413 public Block Block {
2419 public bool IsDeclared {
2425 public Location Location {
2427 return Parameter.Location;
2431 public Parameter Parameter {
2433 return block.Parameters [index];
2437 public TypeSpec ParameterType {
2439 return Parameter.Type;
2445 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2447 return new ParameterReference (this, loc);
2452 // Block is converted to an expression
2454 sealed class BlockScopeExpression : Expression
2457 readonly ParametersBlock block;
2459 public BlockScopeExpression (Expression child, ParametersBlock block)
2465 public override Expression CreateExpressionTree (ResolveContext ec)
2467 throw new NotSupportedException ();
2470 protected override Expression DoResolve (ResolveContext ec)
2475 child = child.Resolve (ec);
2479 eclass = child.eclass;
2484 public override void Emit (EmitContext ec)
2486 block.EmitScopeInitializers (ec);
2491 protected ParametersCompiled parameters;
2492 protected ParameterInfo[] parameter_info;
2494 protected bool unreachable;
2495 protected ToplevelBlock top_block;
2497 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2498 : base (parent, 0, start, start)
2500 if (parameters == null)
2501 throw new ArgumentNullException ("parameters");
2503 this.parameters = parameters;
2504 ParametersBlock = this;
2506 this.top_block = parent.ParametersBlock.top_block;
2507 ProcessParameters ();
2510 protected ParametersBlock (ParametersCompiled parameters, Location start)
2511 : base (null, 0, start, start)
2513 if (parameters == null)
2514 throw new ArgumentNullException ("parameters");
2516 this.parameters = parameters;
2517 ParametersBlock = this;
2520 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2521 : base (null, 0, source.StartLocation, source.EndLocation)
2523 this.parameters = parameters;
2524 this.statements = source.statements;
2525 this.scope_initializers = source.scope_initializers;
2526 this.switch_block = source.switch_block;
2528 this.resolved = true;
2529 this.unreachable = source.unreachable;
2530 this.am_storey = source.am_storey;
2532 ParametersBlock = this;
2538 // Block has been converted to expression tree
2540 public bool IsExpressionTree {
2542 return (flags & Flags.IsExpressionTree) != 0;
2547 // The parameters for the block.
2549 public ParametersCompiled Parameters {
2555 public ToplevelBlock TopBlock {
2561 public bool Resolved {
2570 // Check whether all `out' parameters have been assigned.
2572 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2574 if (vector.IsUnreachable)
2577 int n = parameter_info == null ? 0 : parameter_info.Length;
2579 for (int i = 0; i < n; i++) {
2580 VariableInfo var = parameter_info[i].VariableInfo;
2585 if (vector.IsAssigned (var, false))
2588 TopBlock.Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2593 public override Expression CreateExpressionTree (ResolveContext ec)
2595 if (statements.Count == 1) {
2596 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2597 if (scope_initializers != null)
2598 expr = new BlockScopeExpression (expr, this);
2603 return base.CreateExpressionTree (ec);
2606 public ParameterInfo GetParameterInfo (Parameter p)
2608 for (int i = 0; i < parameters.Count; ++i) {
2609 if (parameters[i] == p)
2610 return parameter_info[i];
2613 throw new ArgumentException ("Invalid parameter");
2616 public Expression GetParameterReference (int index, Location loc)
2618 return new ParameterReference (parameter_info[index], loc);
2621 protected void ProcessParameters ()
2623 if (parameters.Count == 0)
2626 parameter_info = new ParameterInfo[parameters.Count];
2627 for (int i = 0; i < parameter_info.Length; ++i) {
2628 var p = parameters.FixedParameters[i];
2632 // TODO: Should use Parameter only and more block there
2633 parameter_info[i] = new ParameterInfo (this, i);
2634 AddLocalName (p.Name, parameter_info[i]);
2638 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2645 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2646 flags |= Flags.IsExpressionTree;
2651 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2652 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2657 unreachable = top_level.End ();
2659 } catch (Exception e) {
2660 if (e is CompletionResult || rc.Report.IsDisabled)
2663 if (rc.CurrentBlock != null) {
2664 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2666 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2669 if (Report.DebugFlags > 0)
2673 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2674 if (rc.CurrentAnonymousMethod == null) {
2675 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2676 if (md is IteratorMethod) {
2679 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2683 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2684 rc.CurrentAnonymousMethod.GetSignatureForError ());
2692 void ResolveMeta (BlockContext ec)
2694 int orig_count = parameters.Count;
2696 for (int i = 0; i < orig_count; ++i) {
2697 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2699 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2702 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2703 parameter_info[i].VariableInfo = vi;
2704 ec.FlowOffset += vi.Length;
2708 public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
2710 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2711 pb.EndLocation = EndLocation;
2712 pb.statements = statements;
2714 var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2715 am_storey = new IteratorStorey (iterator);
2717 statements = new List<Statement> (1);
2718 AddStatement (new Return (iterator, iterator.Location));
2725 public class ToplevelBlock : ParametersBlock
2727 LocalVariable this_variable;
2728 CompilerContext compiler;
2729 Dictionary<string, object> names;
2731 public HoistedVariable HoistedThisVariable;
2733 public Report Report {
2734 get { return compiler.Report; }
2737 public ToplevelBlock (CompilerContext ctx, Location loc)
2738 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2742 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2743 : base (parameters, start)
2745 this.compiler = ctx;
2748 ProcessParameters ();
2752 // Recreates a top level block from parameters block. Used for
2753 // compiler generated methods where the original block comes from
2754 // explicit child block. This works for already resolved blocks
2755 // only to ensure we resolve them in the correct flow order
2757 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
2758 : base (source, parameters)
2760 this.compiler = source.TopBlock.compiler;
2764 public override void AddLocalName (string name, INamedBlockVariable li)
2767 names = new Dictionary<string, object> ();
2770 if (!names.TryGetValue (name, out value)) {
2771 names.Add (name, li);
2775 INamedBlockVariable existing = value as INamedBlockVariable;
2776 List<INamedBlockVariable> existing_list;
2777 if (existing != null) {
2778 existing_list = new List<INamedBlockVariable> ();
2779 existing_list.Add (existing);
2780 names[name] = existing_list;
2782 existing_list = (List<INamedBlockVariable>) value;
2786 // A collision checking between local names
2788 for (int i = 0; i < existing_list.Count; ++i) {
2789 existing = existing_list[i];
2790 Block b = existing.Block;
2792 // Collision at same level
2793 if (li.Block == b) {
2794 li.Block.Error_AlreadyDeclared (name, li);
2798 // Collision with parent
2800 while ((b = b.Parent) != null) {
2801 if (existing.Block == b) {
2802 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
2803 i = existing_list.Count;
2808 // Collision with with children
2810 while ((b = b.Parent) != null) {
2811 if (li.Block == b) {
2812 li.Block.Error_AlreadyDeclared (name, li, "child");
2813 i = existing_list.Count;
2819 existing_list.Add (li);
2822 public bool CheckError158 (string name, Location loc)
2825 if (AnonymousChildren != null) {
2826 foreach (ToplevelBlock child in AnonymousChildren) {
2827 if (!child.CheckError158 (name, loc))
2832 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2833 if (!c.DoCheckError158 (name, loc))
2840 bool DoCheckError158 (string name, Location loc)
2842 LabeledStatement s = LookupLabel (name);
2844 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2845 Error_158 (name, loc);
2853 // Creates an arguments set from all parameters, useful for method proxy calls
2855 public Arguments GetAllParametersArguments ()
2857 int count = parameters.Count;
2858 Arguments args = new Arguments (count);
2859 for (int i = 0; i < count; ++i) {
2860 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
2861 args.Add (new Argument (arg_expr));
2868 // Lookup inside a block, the returned value can represent 3 states
2870 // true+variable: A local name was found and it's valid
2871 // false+variable: A local name was found in a child block only
2872 // false+null: No local name was found
2874 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
2880 if (!names.TryGetValue (name, out value))
2883 variable = value as INamedBlockVariable;
2885 if (variable != null) {
2887 if (variable.Block == b)
2891 } while (b != null);
2899 } while (b != null);
2901 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
2902 for (int i = 0; i < list.Count; ++i) {
2905 if (variable.Block == b)
2909 } while (b != null);
2917 } while (b != null);
2928 // Returns the "this" instance variable of this block.
2929 // See AddThisVariable() for more information.
2931 public LocalVariable ThisVariable {
2932 get { return this_variable; }
2936 // This is used by non-static `struct' constructors which do not have an
2937 // initializer - in this case, the constructor must initialize all of the
2938 // struct's fields. To do this, we add a "this" variable and use the flow
2939 // analysis code to ensure that it's been fully initialized before control
2940 // leaves the constructor.
2942 public LocalVariable AddThisVariable (BlockContext bc, TypeContainer ds, Location l)
2944 if (this_variable == null) {
2945 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, l);
2946 this_variable.Type = ds.CurrentType;
2947 this_variable.PrepareForFlowAnalysis (bc);
2950 return this_variable;
2953 public bool IsIterator {
2954 get { return (flags & Flags.IsIterator) != 0; }
2955 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2958 public bool IsThisAssigned (BlockContext ec)
2960 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2963 public override void Emit (EmitContext ec)
2965 if (Report.Errors > 0)
2971 if (ec.HasReturnLabel)
2972 ec.ReturnLabel = ec.DefineLabel ();
2976 ec.Mark (EndLocation);
2978 if (ec.HasReturnLabel)
2979 ec.MarkLabel (ec.ReturnLabel);
2981 if (ec.return_value != null) {
2982 ec.Emit (OpCodes.Ldloc, ec.return_value);
2983 ec.Emit (OpCodes.Ret);
2986 // If `HasReturnLabel' is set, then we already emitted a
2987 // jump to the end of the method, so we must emit a `ret'
2990 // Unfortunately, System.Reflection.Emit automatically emits
2991 // a leave to the end of a finally block. This is a problem
2992 // if no code is following the try/finally block since we may
2993 // jump to a point after the end of the method.
2994 // As a workaround, we're always creating a return label in
2998 if (ec.HasReturnLabel || !unreachable) {
2999 if (ec.ReturnType != TypeManager.void_type)
3000 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3001 ec.Emit (OpCodes.Ret);
3006 } catch (Exception e){
3007 Console.WriteLine ("Exception caught by the compiler while emitting:");
3008 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3010 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3016 protected override void EmitSymbolInfo (EmitContext ec)
3018 AnonymousExpression ae = ec.CurrentAnonymousMethod;
3019 if ((ae != null) && (ae.Storey != null))
3020 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
3022 base.EmitSymbolInfo (ec);
3026 public class SwitchLabel {
3033 Label il_label_code;
3034 bool il_label_code_set;
3036 public static readonly object NullStringCase = new object ();
3039 // if expr == null, then it is the default case.
3041 public SwitchLabel (Expression expr, Location l)
3047 public Expression Label {
3053 public Location Location {
3057 public object Converted {
3063 public Label GetILLabel (EmitContext ec)
3066 il_label = ec.DefineLabel ();
3067 il_label_set = true;
3072 public Label GetILLabelCode (EmitContext ec)
3074 if (!il_label_code_set){
3075 il_label_code = ec.DefineLabel ();
3076 il_label_code_set = true;
3078 return il_label_code;
3082 // Resolves the expression, reduces it to a literal if possible
3083 // and then converts it to the requested type.
3085 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3087 Expression e = label.Resolve (ec);
3092 Constant c = e as Constant;
3094 ec.Report.Error (150, loc, "A constant value is expected");
3098 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3099 converted = NullStringCase;
3103 if (allow_nullable && c.GetValue () == null) {
3104 converted = NullStringCase;
3108 c = c.ImplicitConversionRequired (ec, required_type, loc);
3112 converted = c.GetValue ();
3116 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3119 if (converted == null)
3121 else if (converted == NullStringCase)
3124 label = converted.ToString ();
3126 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3127 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3130 public SwitchLabel Clone (CloneContext clonectx)
3132 return new SwitchLabel (label.Clone (clonectx), loc);
3136 public class SwitchSection {
3137 // An array of SwitchLabels.
3138 public readonly List<SwitchLabel> Labels;
3139 public readonly Block Block;
3141 public SwitchSection (List<SwitchLabel> labels, Block block)
3147 public SwitchSection Clone (CloneContext clonectx)
3149 var cloned_labels = new List<SwitchLabel> ();
3151 foreach (SwitchLabel sl in cloned_labels)
3152 cloned_labels.Add (sl.Clone (clonectx));
3154 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3158 public class Switch : Statement {
3159 public List<SwitchSection> Sections;
3160 public Expression Expr;
3163 /// Maps constants whose type type SwitchType to their SwitchLabels.
3165 public IDictionary<object, SwitchLabel> Elements;
3168 /// The governing switch type
3170 public TypeSpec SwitchType;
3175 Label default_target;
3177 Expression new_expr;
3180 SwitchSection constant_section;
3181 SwitchSection default_section;
3183 ExpressionStatement string_dictionary;
3184 FieldExpr switch_cache_field;
3185 static int unique_counter;
3188 // Nullable Types support
3190 Nullable.Unwrap unwrap;
3192 protected bool HaveUnwrap {
3193 get { return unwrap != null; }
3197 // The types allowed to be implicitly cast from
3198 // on the governing type
3200 static TypeSpec [] allowed_types;
3202 public Switch (Expression e, List<SwitchSection> sects, Location l)
3209 public bool GotDefault {
3211 return default_section != null;
3215 public Label DefaultTarget {
3217 return default_target;
3222 // Determines the governing type for a switch. The returned
3223 // expression might be the expression from the switch, or an
3224 // expression that includes any potential conversions to the
3225 // integral types or to string.
3227 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3229 TypeSpec t = expr.Type;
3231 if (t == TypeManager.byte_type ||
3232 t == TypeManager.sbyte_type ||
3233 t == TypeManager.ushort_type ||
3234 t == TypeManager.short_type ||
3235 t == TypeManager.uint32_type ||
3236 t == TypeManager.int32_type ||
3237 t == TypeManager.uint64_type ||
3238 t == TypeManager.int64_type ||
3239 t == TypeManager.char_type ||
3240 t == TypeManager.string_type ||
3241 t == TypeManager.bool_type ||
3242 TypeManager.IsEnumType (t))
3245 if (allowed_types == null){
3246 allowed_types = new TypeSpec [] {
3247 TypeManager.sbyte_type,
3248 TypeManager.byte_type,
3249 TypeManager.short_type,
3250 TypeManager.ushort_type,
3251 TypeManager.int32_type,
3252 TypeManager.uint32_type,
3253 TypeManager.int64_type,
3254 TypeManager.uint64_type,
3255 TypeManager.char_type,
3256 TypeManager.string_type
3261 // Try to find a *user* defined implicit conversion.
3263 // If there is no implicit conversion, or if there are multiple
3264 // conversions, we have to report an error
3266 Expression converted = null;
3267 foreach (TypeSpec tt in allowed_types){
3270 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3275 // Ignore over-worked ImplicitUserConversions that do
3276 // an implicit conversion in addition to the user conversion.
3278 if (!(e is UserCast))
3281 if (converted != null){
3282 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3292 // Performs the basic sanity checks on the switch statement
3293 // (looks for duplicate keys and non-constant expressions).
3295 // It also returns a hashtable with the keys that we will later
3296 // use to compute the switch tables
3298 bool CheckSwitch (ResolveContext ec)
3301 Elements = new Dictionary<object, SwitchLabel> ();
3303 foreach (SwitchSection ss in Sections){
3304 foreach (SwitchLabel sl in ss.Labels){
3305 if (sl.Label == null){
3306 if (default_section != null){
3307 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3310 default_section = ss;
3314 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3319 object key = sl.Converted;
3320 if (key == SwitchLabel.NullStringCase)
3321 has_null_case = true;
3324 Elements.Add (key, sl);
3325 } catch (ArgumentException) {
3326 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3334 void EmitObjectInteger (EmitContext ec, object k)
3337 ec.EmitInt ((int) k);
3338 else if (k is Constant) {
3339 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3342 ec.EmitInt (unchecked ((int) (uint) k));
3345 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3347 ec.EmitInt ((int) (long) k);
3348 ec.Emit (OpCodes.Conv_I8);
3351 ec.EmitLong ((long) k);
3353 else if (k is ulong)
3355 ulong ul = (ulong) k;
3358 ec.EmitInt (unchecked ((int) ul));
3359 ec.Emit (OpCodes.Conv_U8);
3363 ec.EmitLong (unchecked ((long) ul));
3367 ec.EmitInt ((int) ((char) k));
3368 else if (k is sbyte)
3369 ec.EmitInt ((int) ((sbyte) k));
3371 ec.EmitInt ((int) ((byte) k));
3372 else if (k is short)
3373 ec.EmitInt ((int) ((short) k));
3374 else if (k is ushort)
3375 ec.EmitInt ((int) ((ushort) k));
3377 ec.EmitInt (((bool) k) ? 1 : 0);
3379 throw new Exception ("Unhandled case");
3382 // structure used to hold blocks of keys while calculating table switch
3383 class KeyBlock : IComparable
3385 public KeyBlock (long _first)
3387 first = last = _first;
3391 public List<object> element_keys;
3392 // how many items are in the bucket
3393 public int Size = 1;
3396 get { return (int) (last - first + 1); }
3398 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3400 return kb_last.last - kb_first.first + 1;
3402 public int CompareTo (object obj)
3404 KeyBlock kb = (KeyBlock) obj;
3405 int nLength = Length;
3406 int nLengthOther = kb.Length;
3407 if (nLengthOther == nLength)
3408 return (int) (kb.first - first);
3409 return nLength - nLengthOther;
3414 /// This method emits code for a lookup-based switch statement (non-string)
3415 /// Basically it groups the cases into blocks that are at least half full,
3416 /// and then spits out individual lookup opcodes for each block.
3417 /// It emits the longest blocks first, and short blocks are just
3418 /// handled with direct compares.
3420 /// <param name="ec"></param>
3421 /// <param name="val"></param>
3422 /// <returns></returns>
3423 void TableSwitchEmit (EmitContext ec, Expression val)
3425 int element_count = Elements.Count;
3426 object [] element_keys = new object [element_count];
3427 Elements.Keys.CopyTo (element_keys, 0);
3428 Array.Sort (element_keys);
3430 // initialize the block list with one element per key
3431 var key_blocks = new List<KeyBlock> (element_count);
3432 foreach (object key in element_keys)
3433 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3435 KeyBlock current_kb;
3436 // iteratively merge the blocks while they are at least half full
3437 // there's probably a really cool way to do this with a tree...
3438 while (key_blocks.Count > 1)
3440 var key_blocks_new = new List<KeyBlock> ();
3441 current_kb = (KeyBlock) key_blocks [0];
3442 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3444 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3445 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3448 current_kb.last = kb.last;
3449 current_kb.Size += kb.Size;
3453 // start a new block
3454 key_blocks_new.Add (current_kb);
3458 key_blocks_new.Add (current_kb);
3459 if (key_blocks.Count == key_blocks_new.Count)
3461 key_blocks = key_blocks_new;
3464 // initialize the key lists
3465 foreach (KeyBlock kb in key_blocks)
3466 kb.element_keys = new List<object> ();
3468 // fill the key lists
3470 if (key_blocks.Count > 0) {
3471 current_kb = (KeyBlock) key_blocks [0];
3472 foreach (object key in element_keys)
3474 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3475 System.Convert.ToInt64 (key) > current_kb.last;
3477 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3478 current_kb.element_keys.Add (key);
3482 // sort the blocks so we can tackle the largest ones first
3485 // okay now we can start...
3486 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3487 Label lbl_default = default_target;
3489 Type type_keys = null;
3490 if (element_keys.Length > 0)
3491 type_keys = element_keys [0].GetType (); // used for conversions
3493 TypeSpec compare_type;
3495 if (TypeManager.IsEnumType (SwitchType))
3496 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3498 compare_type = SwitchType;
3500 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3502 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3503 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3506 foreach (object key in kb.element_keys) {
3507 SwitchLabel sl = (SwitchLabel) Elements [key];
3508 if (key is int && (int) key == 0) {
3509 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3512 EmitObjectInteger (ec, key);
3513 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3519 // TODO: if all the keys in the block are the same and there are
3520 // no gaps/defaults then just use a range-check.
3521 if (compare_type == TypeManager.int64_type ||
3522 compare_type == TypeManager.uint64_type)
3524 // TODO: optimize constant/I4 cases
3526 // check block range (could be > 2^31)
3528 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3529 ec.Emit (OpCodes.Blt, lbl_default);
3531 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3532 ec.Emit (OpCodes.Bgt, lbl_default);
3538 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3539 ec.Emit (OpCodes.Sub);
3541 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3547 int first = (int) kb.first;
3551 ec.Emit (OpCodes.Sub);
3555 ec.EmitInt (-first);
3556 ec.Emit (OpCodes.Add);
3560 // first, build the list of labels for the switch
3562 int cJumps = kb.Length;
3563 Label [] switch_labels = new Label [cJumps];
3564 for (int iJump = 0; iJump < cJumps; iJump++)
3566 object key = kb.element_keys [iKey];
3567 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3569 SwitchLabel sl = (SwitchLabel) Elements [key];
3570 switch_labels [iJump] = sl.GetILLabel (ec);
3574 switch_labels [iJump] = lbl_default;
3576 // emit the switch opcode
3577 ec.Emit (OpCodes.Switch, switch_labels);
3580 // mark the default for this block
3582 ec.MarkLabel (lbl_default);
3585 // TODO: find the default case and emit it here,
3586 // to prevent having to do the following jump.
3587 // make sure to mark other labels in the default section
3589 // the last default just goes to the end
3590 if (element_keys.Length > 0)
3591 ec.Emit (OpCodes.Br, lbl_default);
3593 // now emit the code for the sections
3594 bool found_default = false;
3596 foreach (SwitchSection ss in Sections) {
3597 foreach (SwitchLabel sl in ss.Labels) {
3598 if (sl.Converted == SwitchLabel.NullStringCase) {
3599 ec.MarkLabel (null_target);
3600 } else if (sl.Label == null) {
3601 ec.MarkLabel (lbl_default);
3602 found_default = true;
3604 ec.MarkLabel (null_target);
3606 ec.MarkLabel (sl.GetILLabel (ec));
3607 ec.MarkLabel (sl.GetILLabelCode (ec));
3612 if (!found_default) {
3613 ec.MarkLabel (lbl_default);
3614 if (!has_null_case) {
3615 ec.MarkLabel (null_target);
3619 ec.MarkLabel (lbl_end);
3622 SwitchSection FindSection (SwitchLabel label)
3624 foreach (SwitchSection ss in Sections){
3625 foreach (SwitchLabel sl in ss.Labels){
3634 public static void Reset ()
3637 allowed_types = null;
3640 public override bool Resolve (BlockContext ec)
3642 Expr = Expr.Resolve (ec);
3646 new_expr = SwitchGoverningType (ec, Expr);
3648 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3649 unwrap = Nullable.Unwrap.Create (Expr, false);
3653 new_expr = SwitchGoverningType (ec, unwrap);
3656 if (new_expr == null){
3657 ec.Report.Error (151, loc,
3658 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3659 TypeManager.CSharpName (Expr.Type));
3664 SwitchType = new_expr.Type;
3666 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3667 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3671 if (!CheckSwitch (ec))
3675 Elements.Remove (SwitchLabel.NullStringCase);
3677 Switch old_switch = ec.Switch;
3679 ec.Switch.SwitchType = SwitchType;
3681 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3682 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3684 var constant = new_expr as Constant;
3685 if (constant != null) {
3687 object key = constant.GetValue ();
3689 if (Elements.TryGetValue (key, out label))
3690 constant_section = FindSection (label);
3692 if (constant_section == null)
3693 constant_section = default_section;
3698 foreach (SwitchSection ss in Sections){
3700 ec.CurrentBranching.CreateSibling (
3701 null, FlowBranching.SiblingType.SwitchSection);
3705 if (is_constant && (ss != constant_section)) {
3706 // If we're a constant switch, we're only emitting
3707 // one single section - mark all the others as
3709 ec.CurrentBranching.CurrentUsageVector.Goto ();
3710 if (!ss.Block.ResolveUnreachable (ec, true)) {
3714 if (!ss.Block.Resolve (ec))
3719 if (default_section == null)
3720 ec.CurrentBranching.CreateSibling (
3721 null, FlowBranching.SiblingType.SwitchSection);
3723 ec.EndFlowBranching ();
3724 ec.Switch = old_switch;
3726 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3731 if (SwitchType == TypeManager.string_type && !is_constant) {
3732 // TODO: Optimize single case, and single+default case
3733 ResolveStringSwitchMap (ec);
3739 void ResolveStringSwitchMap (ResolveContext ec)
3741 FullNamedExpression string_dictionary_type;
3742 if (TypeManager.generic_ienumerable_type != null) {
3743 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3744 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3746 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3748 new TypeExpression (TypeManager.string_type, loc),
3749 new TypeExpression (TypeManager.int32_type, loc)), loc);
3751 MemberAccess system_collections_generic = new MemberAccess (
3752 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3754 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3757 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3758 Field field = new Field (ctype, string_dictionary_type,
3759 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3760 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3761 if (!field.Define ())
3763 ctype.AddField (field);
3765 var init = new List<Expression> ();
3768 string value = null;
3769 foreach (SwitchSection section in Sections) {
3770 int last_count = init.Count;
3771 foreach (SwitchLabel sl in section.Labels) {
3772 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3775 value = (string) sl.Converted;
3776 var init_args = new List<Expression> (2);
3777 init_args.Add (new StringLiteral (value, sl.Location));
3778 init_args.Add (new IntConstant (counter, loc));
3779 init.Add (new CollectionElementInitializer (init_args, loc));
3783 // Don't add empty sections
3785 if (last_count == init.Count)
3788 Elements.Add (counter, section.Labels [0]);
3792 Arguments args = new Arguments (1);
3793 args.Add (new Argument (new IntConstant (init.Count, loc)));
3794 Expression initializer = new NewInitialize (string_dictionary_type, args,
3795 new CollectionOrObjectInitializers (init, loc), loc);
3797 switch_cache_field = new FieldExpr (field, loc);
3798 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3801 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3803 Label l_initialized = ec.DefineLabel ();
3806 // Skip initialization when value is null
3808 value.EmitBranchable (ec, null_target, false);
3811 // Check if string dictionary is initialized and initialize
3813 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3814 string_dictionary.EmitStatement (ec);
3815 ec.MarkLabel (l_initialized);
3817 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3819 ResolveContext rc = new ResolveContext (ec.MemberContext);
3821 if (TypeManager.generic_ienumerable_type != null) {
3822 Arguments get_value_args = new Arguments (2);
3823 get_value_args.Add (new Argument (value));
3824 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3825 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3826 if (get_item == null)
3830 // A value was not found, go to default case
3832 get_item.EmitBranchable (ec, default_target, false);
3834 Arguments get_value_args = new Arguments (1);
3835 get_value_args.Add (new Argument (value));
3837 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3838 if (get_item == null)
3841 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3842 get_item_object.EmitAssign (ec, get_item, true, false);
3843 ec.Emit (OpCodes.Brfalse, default_target);
3845 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3846 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3848 get_item_int.EmitStatement (ec);
3849 get_item_object.Release (ec);
3852 TableSwitchEmit (ec, string_switch_variable);
3853 string_switch_variable.Release (ec);
3856 protected override void DoEmit (EmitContext ec)
3858 default_target = ec.DefineLabel ();
3859 null_target = ec.DefineLabel ();
3861 // Store variable for comparission purposes
3862 // TODO: Don't duplicate non-captured VariableReference
3863 LocalTemporary value;
3865 value = new LocalTemporary (SwitchType);
3866 unwrap.EmitCheck (ec);
3867 ec.Emit (OpCodes.Brfalse, null_target);
3870 } else if (!is_constant) {
3871 value = new LocalTemporary (SwitchType);
3878 // Setup the codegen context
3880 Label old_end = ec.LoopEnd;
3881 Switch old_switch = ec.Switch;
3883 ec.LoopEnd = ec.DefineLabel ();
3888 if (constant_section != null)
3889 constant_section.Block.Emit (ec);
3890 } else if (string_dictionary != null) {
3891 DoEmitStringSwitch (value, ec);
3893 TableSwitchEmit (ec, value);
3899 // Restore context state.
3900 ec.MarkLabel (ec.LoopEnd);
3903 // Restore the previous context
3905 ec.LoopEnd = old_end;
3906 ec.Switch = old_switch;
3909 protected override void CloneTo (CloneContext clonectx, Statement t)
3911 Switch target = (Switch) t;
3913 target.Expr = Expr.Clone (clonectx);
3914 target.Sections = new List<SwitchSection> ();
3915 foreach (SwitchSection ss in Sections){
3916 target.Sections.Add (ss.Clone (clonectx));
3921 // A place where execution can restart in an iterator
3922 public abstract class ResumableStatement : Statement
3925 protected Label resume_point;
3927 public Label PrepareForEmit (EmitContext ec)
3931 resume_point = ec.DefineLabel ();
3933 return resume_point;
3936 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3940 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3945 // Base class for statements that are implemented in terms of try...finally
3946 public abstract class ExceptionStatement : ResumableStatement
3950 List<ResumableStatement> resume_points;
3951 int first_resume_pc;
3952 protected Statement stmt;
3953 Label dispose_try_block;
3954 bool prepared_for_dispose, emitted_dispose;
3956 protected ExceptionStatement (Statement stmt, Location loc)
3964 public Statement Statement {
3972 protected abstract void EmitPreTryBody (EmitContext ec);
3973 protected abstract void EmitTryBody (EmitContext ec);
3974 protected abstract void EmitFinallyBody (EmitContext ec);
3976 protected sealed override void DoEmit (EmitContext ec)
3978 EmitPreTryBody (ec);
3980 if (resume_points != null) {
3981 ec.EmitInt ((int) Iterator.State.Running);
3982 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3985 ec.BeginExceptionBlock ();
3987 if (resume_points != null) {
3988 ec.MarkLabel (resume_point);
3990 // For normal control flow, we want to fall-through the Switch
3991 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3992 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3993 ec.EmitInt (first_resume_pc);
3994 ec.Emit (OpCodes.Sub);
3996 Label [] labels = new Label [resume_points.Count];
3997 for (int i = 0; i < resume_points.Count; ++i)
3998 labels [i] = resume_points [i].PrepareForEmit (ec);
3999 ec.Emit (OpCodes.Switch, labels);
4004 ec.BeginFinallyBlock ();
4006 Label start_finally = ec.DefineLabel ();
4007 if (resume_points != null) {
4008 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
4009 ec.Emit (OpCodes.Brfalse_S, start_finally);
4010 ec.Emit (OpCodes.Endfinally);
4013 ec.MarkLabel (start_finally);
4014 EmitFinallyBody (ec);
4016 ec.EndExceptionBlock ();
4019 public void SomeCodeFollows ()
4021 code_follows = true;
4024 public override bool Resolve (BlockContext ec)
4026 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4027 // So, ensure there's some IL code after this statement.
4028 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4029 ec.NeedReturnLabel ();
4031 iter = ec.CurrentIterator;
4035 public void AddResumePoint (ResumableStatement stmt, int pc)
4037 if (resume_points == null) {
4038 resume_points = new List<ResumableStatement> ();
4039 first_resume_pc = pc;
4042 if (pc != first_resume_pc + resume_points.Count)
4043 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4045 resume_points.Add (stmt);
4048 public override Label PrepareForDispose (EmitContext ec, Label end)
4050 if (!prepared_for_dispose) {
4051 prepared_for_dispose = true;
4052 dispose_try_block = ec.DefineLabel ();
4054 return dispose_try_block;
4057 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4059 if (emitted_dispose)
4062 emitted_dispose = true;
4064 Label end_of_try = ec.DefineLabel ();
4066 // Ensure that the only way we can get into this code is through a dispatcher
4067 if (have_dispatcher)
4068 ec.Emit (OpCodes.Br, end);
4070 ec.BeginExceptionBlock ();
4072 ec.MarkLabel (dispose_try_block);
4074 Label [] labels = null;
4075 for (int i = 0; i < resume_points.Count; ++i) {
4076 ResumableStatement s = (ResumableStatement) resume_points [i];
4077 Label ret = s.PrepareForDispose (ec, end_of_try);
4078 if (ret.Equals (end_of_try) && labels == null)
4080 if (labels == null) {
4081 labels = new Label [resume_points.Count];
4082 for (int j = 0; j < i; ++j)
4083 labels [j] = end_of_try;
4088 if (labels != null) {
4090 for (j = 1; j < labels.Length; ++j)
4091 if (!labels [0].Equals (labels [j]))
4093 bool emit_dispatcher = j < labels.Length;
4095 if (emit_dispatcher) {
4096 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4097 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4098 ec.EmitInt (first_resume_pc);
4099 ec.Emit (OpCodes.Sub);
4100 ec.Emit (OpCodes.Switch, labels);
4101 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4104 foreach (ResumableStatement s in resume_points)
4105 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4108 ec.MarkLabel (end_of_try);
4110 ec.BeginFinallyBlock ();
4112 EmitFinallyBody (ec);
4114 ec.EndExceptionBlock ();
4118 public class Lock : ExceptionStatement {
4120 TemporaryVariableReference temp;
4122 public Lock (Expression expr, Statement stmt, Location loc)
4128 public override bool Resolve (BlockContext ec)
4130 expr = expr.Resolve (ec);
4134 if (!TypeManager.IsReferenceType (expr.Type)){
4135 ec.Report.Error (185, loc,
4136 "`{0}' is not a reference type as required by the lock statement",
4137 TypeManager.CSharpName (expr.Type));
4141 ec.StartFlowBranching (this);
4142 bool ok = Statement.Resolve (ec);
4143 ec.EndFlowBranching ();
4145 ok &= base.Resolve (ec);
4147 temp = TemporaryVariableReference.Create (expr.Type, ec.CurrentBlock.Parent, loc);
4150 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4151 TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4152 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4153 monitor_type, "Enter", loc, TypeManager.object_type);
4154 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4155 monitor_type, "Exit", loc, TypeManager.object_type);
4161 protected override void EmitPreTryBody (EmitContext ec)
4163 temp.EmitAssign (ec, expr);
4165 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4168 protected override void EmitTryBody (EmitContext ec)
4170 Statement.Emit (ec);
4173 protected override void EmitFinallyBody (EmitContext ec)
4176 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4179 protected override void CloneTo (CloneContext clonectx, Statement t)
4181 Lock target = (Lock) t;
4183 target.expr = expr.Clone (clonectx);
4184 target.stmt = Statement.Clone (clonectx);
4188 public class Unchecked : Statement {
4191 public Unchecked (Block b, Location loc)
4198 public override bool Resolve (BlockContext ec)
4200 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4201 return Block.Resolve (ec);
4204 protected override void DoEmit (EmitContext ec)
4206 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4210 protected override void CloneTo (CloneContext clonectx, Statement t)
4212 Unchecked target = (Unchecked) t;
4214 target.Block = clonectx.LookupBlock (Block);
4218 public class Checked : Statement {
4221 public Checked (Block b, Location loc)
4224 b.Unchecked = false;
4228 public override bool Resolve (BlockContext ec)
4230 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4231 return Block.Resolve (ec);
4234 protected override void DoEmit (EmitContext ec)
4236 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4240 protected override void CloneTo (CloneContext clonectx, Statement t)
4242 Checked target = (Checked) t;
4244 target.Block = clonectx.LookupBlock (Block);
4248 public class Unsafe : Statement {
4251 public Unsafe (Block b, Location loc)
4254 Block.Unsafe = true;
4258 public override bool Resolve (BlockContext ec)
4260 if (ec.CurrentIterator != null)
4261 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4263 using (ec.Set (ResolveContext.Options.UnsafeScope))
4264 return Block.Resolve (ec);
4267 protected override void DoEmit (EmitContext ec)
4272 protected override void CloneTo (CloneContext clonectx, Statement t)
4274 Unsafe target = (Unsafe) t;
4276 target.Block = clonectx.LookupBlock (Block);
4283 public class Fixed : Statement
4285 abstract class Emitter : ShimExpression
4287 protected LocalVariable vi;
4289 protected Emitter (Expression expr, LocalVariable li)
4295 public abstract void EmitExit (EmitContext ec);
4298 class ExpressionEmitter : Emitter {
4299 public ExpressionEmitter (Expression converted, LocalVariable li) :
4300 base (converted, li)
4304 protected override Expression DoResolve (ResolveContext rc)
4306 throw new NotImplementedException ();
4309 public override void Emit (EmitContext ec) {
4311 // Store pointer in pinned location
4317 public override void EmitExit (EmitContext ec)
4319 ec.Emit (OpCodes.Ldc_I4_0);
4320 ec.Emit (OpCodes.Conv_U);
4325 class StringEmitter : Emitter
4327 LocalVariable pinned_string;
4329 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4334 protected override Expression DoResolve (ResolveContext rc)
4336 pinned_string = new LocalVariable (vi.Block, "$pinned",
4337 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4340 pinned_string.Type = TypeManager.string_type;
4342 if (TypeManager.int_get_offset_to_string_data == null) {
4343 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4344 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4347 eclass = ExprClass.Variable;
4348 type = TypeManager.int32_type;
4352 public override void Emit (EmitContext ec)
4354 pinned_string.CreateBuilder (ec);
4357 pinned_string.EmitAssign (ec);
4359 // TODO: Should use Binary::Add
4360 pinned_string.Emit (ec);
4361 ec.Emit (OpCodes.Conv_I);
4363 PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4364 //pe.InstanceExpression = pinned_string;
4365 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4367 ec.Emit (OpCodes.Add);
4371 public override void EmitExit (EmitContext ec)
4373 ec.Emit (OpCodes.Ldnull);
4374 pinned_string.EmitAssign (ec);
4378 public class VariableDeclaration : BlockVariableDeclaration
4380 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4385 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4387 if (!Variable.Type.IsPointer && li == Variable) {
4388 bc.Report.Error (209, TypeExpression.Location,
4389 "The type of locals declared in a fixed statement must be a pointer type");
4394 // The rules for the possible declarators are pretty wise,
4395 // but the production on the grammar is more concise.
4397 // So we have to enforce these rules here.
4399 // We do not resolve before doing the case 1 test,
4400 // because the grammar is explicit in that the token &
4401 // is present, so we need to test for this particular case.
4404 if (initializer is Cast) {
4405 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4409 initializer = initializer.Resolve (bc);
4411 if (initializer == null)
4417 if (initializer.Type.IsArray) {
4418 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4421 // Provided that array_type is unmanaged,
4423 if (!TypeManager.VerifyUnmanaged (bc.Compiler, array_type, loc))
4427 // and T* is implicitly convertible to the
4428 // pointer type given in the fixed statement.
4430 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4432 Expression converted = Convert.ImplicitConversionRequired (
4433 bc, array_ptr, li.Type, loc);
4434 if (converted == null)
4438 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4440 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4441 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4442 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (0, loc), loc), loc)),
4443 new NullPointer (loc),
4446 converted = converted.Resolve (bc);
4448 return new ExpressionEmitter (converted, li);
4454 if (initializer.Type == TypeManager.string_type) {
4455 return new StringEmitter (initializer, li, loc).Resolve (bc);
4458 // Case 3: fixed buffer
4459 if (initializer is FixedBufferPtr) {
4460 return new ExpressionEmitter (initializer, li);
4464 // Case 4: & object.
4466 bool already_fixed = true;
4467 Unary u = initializer as Unary;
4468 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4469 IVariableReference vr = u.Expr as IVariableReference;
4470 if (vr == null || !vr.IsFixed) {
4471 already_fixed = false;
4475 if (already_fixed) {
4476 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4479 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4480 return new ExpressionEmitter (initializer, li);
4485 VariableDeclaration decl;
4486 Statement statement;
4489 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4498 public Statement Statement {
4504 public BlockVariableDeclaration Variables {
4512 public override bool Resolve (BlockContext ec)
4514 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4515 if (!decl.Resolve (ec))
4519 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4520 bool ok = statement.Resolve (ec);
4521 bool flow_unreachable = ec.EndFlowBranching ();
4522 has_ret = flow_unreachable;
4527 protected override void DoEmit (EmitContext ec)
4529 decl.Variable.CreateBuilder (ec);
4530 decl.Initializer.Emit (ec);
4531 if (decl.Declarators != null) {
4532 foreach (var d in decl.Declarators) {
4533 d.Variable.CreateBuilder (ec);
4534 d.Initializer.Emit (ec);
4538 statement.Emit (ec);
4544 // Clear the pinned variable
4546 ((Emitter) decl.Initializer).EmitExit (ec);
4547 if (decl.Declarators != null) {
4548 foreach (var d in decl.Declarators) {
4549 ((Emitter)d.Initializer).EmitExit (ec);
4554 protected override void CloneTo (CloneContext clonectx, Statement t)
4556 Fixed target = (Fixed) t;
4558 target.decl = (VariableDeclaration) decl.Clone (clonectx);
4559 target.statement = statement.Clone (clonectx);
4563 public class Catch : Statement
4567 FullNamedExpression type_expr;
4568 CompilerAssign assign;
4571 public Catch (Block block, Location loc)
4579 public Block Block {
4585 public TypeSpec CatchType {
4591 public bool IsGeneral {
4593 return type_expr == null;
4597 public FullNamedExpression TypeExpression {
4606 public LocalVariable Variable {
4617 protected override void DoEmit (EmitContext ec)
4620 ec.BeginCatchBlock (TypeManager.object_type);
4622 ec.BeginCatchBlock (CatchType);
4625 li.CreateBuilder (ec);
4628 // Special case hoisted catch variable, we have to use a temporary variable
4629 // to pass via anonymous storey initialization with the value still on top
4632 if (li.HoistedVariant != null) {
4633 LocalTemporary lt = new LocalTemporary (li.Type);
4634 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4636 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4638 // switch to assigning from the temporary variable and not from top of the stack
4639 assign.UpdateSource (lt);
4642 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4643 ec.Emit (OpCodes.Pop);
4644 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4650 public override bool Resolve (BlockContext ec)
4652 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4653 if (type_expr != null) {
4654 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4659 if (type != TypeManager.exception_type && !TypeSpec.IsBaseClass (type, TypeManager.exception_type, false)) {
4660 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4661 } else if (li != null) {
4663 li.PrepareForFlowAnalysis (ec);
4665 // source variable is at the top of the stack
4666 Expression source = new EmptyExpression (li.Type);
4667 if (li.Type.IsGenericParameter)
4668 source = new UnboxCast (source, li.Type);
4670 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
4671 Block.AddScopeStatement (new StatementExpression (assign));
4675 return Block.Resolve (ec);
4679 protected override void CloneTo (CloneContext clonectx, Statement t)
4681 Catch target = (Catch) t;
4683 if (type_expr != null)
4684 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
4686 target.block = clonectx.LookupBlock (block);
4690 public class TryFinally : ExceptionStatement {
4693 public TryFinally (Statement stmt, Block fini, Location loc)
4699 public override bool Resolve (BlockContext ec)
4703 ec.StartFlowBranching (this);
4705 if (!stmt.Resolve (ec))
4709 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4710 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4711 if (!fini.Resolve (ec))
4715 ec.EndFlowBranching ();
4717 ok &= base.Resolve (ec);
4722 protected override void EmitPreTryBody (EmitContext ec)
4726 protected override void EmitTryBody (EmitContext ec)
4731 protected override void EmitFinallyBody (EmitContext ec)
4736 protected override void CloneTo (CloneContext clonectx, Statement t)
4738 TryFinally target = (TryFinally) t;
4740 target.stmt = (Statement) stmt.Clone (clonectx);
4742 target.fini = clonectx.LookupBlock (fini);
4746 public class TryCatch : Statement {
4748 public List<Catch> Specific;
4749 public Catch General;
4750 bool inside_try_finally, code_follows;
4752 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4755 this.Specific = catch_clauses;
4756 this.inside_try_finally = inside_try_finally;
4758 Catch c = catch_clauses [0];
4761 catch_clauses.RemoveAt (0);
4767 public override bool Resolve (BlockContext ec)
4771 ec.StartFlowBranching (this);
4773 if (!Block.Resolve (ec))
4776 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4778 foreach (Catch c in Specific){
4779 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4781 if (!c.Resolve (ec)) {
4786 TypeSpec resolved_type = c.CatchType;
4787 for (int ii = 0; ii < last_index; ++ii) {
4788 if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4789 ec.Report.Error (160, c.loc,
4790 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4791 TypeManager.CSharpName (prev_catches [ii]));
4796 prev_catches [last_index++] = resolved_type;
4799 if (General != null) {
4800 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4801 foreach (Catch c in Specific){
4802 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4803 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'");
4808 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4810 if (!General.Resolve (ec))
4814 ec.EndFlowBranching ();
4816 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4817 // So, ensure there's some IL code after this statement
4818 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4819 ec.NeedReturnLabel ();
4824 public void SomeCodeFollows ()
4826 code_follows = true;
4829 protected override void DoEmit (EmitContext ec)
4831 if (!inside_try_finally)
4832 ec.BeginExceptionBlock ();
4836 foreach (Catch c in Specific)
4839 if (General != null)
4842 if (!inside_try_finally)
4843 ec.EndExceptionBlock ();
4846 protected override void CloneTo (CloneContext clonectx, Statement t)
4848 TryCatch target = (TryCatch) t;
4850 target.Block = clonectx.LookupBlock (Block);
4851 if (General != null)
4852 target.General = (Catch) General.Clone (clonectx);
4853 if (Specific != null){
4854 target.Specific = new List<Catch> ();
4855 foreach (Catch c in Specific)
4856 target.Specific.Add ((Catch) c.Clone (clonectx));
4861 // FIXME: Why is it almost exact copy of Using ??
4862 public class UsingTemporary : ExceptionStatement
4864 protected TemporaryVariableReference local_copy;
4866 protected Statement dispose_call;
4868 public UsingTemporary (Expression expr, Statement stmt, Location loc)
4875 public Expression Expression {
4883 protected virtual bool DoResolve (BlockContext ec)
4885 expr = expr.Resolve (ec);
4889 if (expr.Type != TypeManager.idisposable_type && !expr.Type.ImplementsInterface (TypeManager.idisposable_type)) {
4890 if (TypeManager.IsNullableType (expr.Type)) {
4891 // it's handled it a custom code bellow
4892 } else if (expr.Type == InternalType.Dynamic) {
4893 expr = Convert.ImplicitConversionStandard (ec, expr, TypeManager.idisposable_type, loc);
4895 Using.Error_IsNotConvertibleToIDisposable (ec, expr.Type, expr.Location);
4900 var expr_type = expr.Type;
4902 local_copy = TemporaryVariableReference.Create (expr_type, ec.CurrentBlock.Parent, loc);
4903 local_copy.Resolve (ec);
4905 if (TypeManager.void_dispose_void == null) {
4906 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4907 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4910 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
4911 dispose_mg.InstanceExpression = TypeManager.IsNullableType (expr_type) ?
4912 new Cast (new TypeExpression (TypeManager.idisposable_type, loc), local_copy, loc).Resolve (ec) :
4915 dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
4917 // Add conditional call when disposing possible null variable
4918 if (!expr_type.IsStruct || TypeManager.IsNullableType (expr_type))
4919 dispose_call = new If (new Binary (Binary.Operator.Inequality, local_copy, new NullLiteral (loc), loc), dispose_call, loc);
4921 return dispose_call.Resolve (ec);
4924 public override bool Resolve (BlockContext ec)
4926 bool ok = DoResolve (ec);
4928 ec.StartFlowBranching (this);
4930 ok &= stmt.Resolve (ec);
4932 ec.EndFlowBranching ();
4934 ok &= base.Resolve (ec);
4939 protected override void EmitPreTryBody (EmitContext ec)
4941 //local_copy.Variable.
4942 local_copy.EmitAssign (ec, expr);
4945 protected override void EmitTryBody (EmitContext ec)
4950 protected override void EmitFinallyBody (EmitContext ec)
4952 dispose_call.Emit (ec);
4955 protected override void CloneTo (CloneContext clonectx, Statement t)
4957 UsingTemporary target = (UsingTemporary) t;
4959 target.expr = expr.Clone (clonectx);
4960 target.stmt = stmt.Clone (clonectx);
4964 public class Using : ExceptionStatement
4966 public class VariableDeclaration : BlockVariableDeclaration
4968 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4973 private VariableDeclaration (LocalVariable li, Location loc)
4979 public override bool Resolve (BlockContext bc)
4981 if (type_expr == null)
4984 return base.Resolve (bc);
4987 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4991 if (li.Type == InternalType.Dynamic) {
4992 initializer = initializer.Resolve (bc);
4993 if (initializer == null)
4996 initializer = Convert.ImplicitConversionRequired (bc, initializer, TypeManager.idisposable_type, loc);
4997 if (initializer == null)
5000 var var = TemporaryVariableReference.Create (TypeManager.idisposable_type, bc.CurrentBlock, loc);
5001 assign = new SimpleAssign (var, initializer, loc);
5002 assign.ResolveStatement (bc);
5006 if (li == Variable) {
5007 if (li.Type != TypeManager.idisposable_type && !li.Type.ImplementsInterface (TypeManager.idisposable_type)) {
5008 Using.Error_IsNotConvertibleToIDisposable (bc, li.Type, type_expr.Location);
5013 return base.ResolveInitializer (bc, li, initializer);
5016 public Statement RewriteForDeclarators (BlockContext bc, Statement stmt)
5018 for (int i = declarators.Count - 1; i >= 0; --i) {
5019 var d = declarators [i];
5020 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5021 vd.Initializer = d.Initializer;
5022 stmt = new Using (vd, stmt, d.Variable.Location);
5030 VariableDeclaration decl;
5032 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5040 public BlockVariableDeclaration Variables {
5048 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, TypeSpec type, Location loc)
5050 ec.Report.SymbolRelatedToPreviousError (type);
5051 ec.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5052 type.GetSignatureForError ());
5055 protected override void EmitPreTryBody (EmitContext ec)
5060 protected override void EmitTryBody (EmitContext ec)
5065 protected override void EmitFinallyBody (EmitContext ec)
5067 Label skip = ec.DefineLabel ();
5069 var var = ((Assign) decl.Initializer).Target;
5070 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5071 if (emit_null_check) {
5073 ec.Emit (OpCodes.Brfalse, skip);
5076 Invocation.EmitCall (ec, var, TypeManager.void_dispose_void, null, loc);
5078 if (emit_null_check)
5079 ec.MarkLabel (skip);
5082 public override bool Resolve (BlockContext ec)
5084 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5085 if (!decl.Resolve (ec))
5088 if (decl.Declarators != null) {
5089 stmt = decl.RewriteForDeclarators (ec, stmt);
5093 ec.StartFlowBranching (this);
5095 bool ok = stmt.Resolve (ec);
5097 ec.EndFlowBranching ();
5099 ok &= base.Resolve (ec);
5101 if (TypeManager.void_dispose_void == null) {
5102 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5103 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5109 protected override void CloneTo (CloneContext clonectx, Statement t)
5111 Using target = (Using) t;
5113 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5114 target.stmt = stmt.Clone (clonectx);
5119 /// Implementation of the foreach C# statement
5121 public class Foreach : Statement {
5123 sealed class ArrayForeach : Statement
5125 readonly Foreach for_each;
5126 readonly Statement statement;
5129 TemporaryVariableReference[] lengths;
5130 Expression [] length_exprs;
5131 StatementExpression[] counter;
5132 TemporaryVariableReference[] variables;
5134 TemporaryVariableReference copy;
5136 LocalVariableReference variable;
5138 public ArrayForeach (Foreach @foreach, int rank)
5140 for_each = @foreach;
5141 statement = for_each.statement;
5143 variable = new LocalVariableReference (for_each.variable, loc);
5145 counter = new StatementExpression[rank];
5146 variables = new TemporaryVariableReference[rank];
5147 length_exprs = new Expression [rank];
5150 // Only use temporary length variables when dealing with
5151 // multi-dimensional arrays
5154 lengths = new TemporaryVariableReference [rank];
5157 protected override void CloneTo (CloneContext clonectx, Statement target)
5159 throw new NotImplementedException ();
5162 public override bool Resolve (BlockContext ec)
5164 Block variables_block = variable.local_info.Block;
5165 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5168 int rank = length_exprs.Length;
5169 Arguments list = new Arguments (rank);
5170 for (int i = 0; i < rank; i++) {
5171 var v = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5173 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5174 counter[i].Resolve (ec);
5177 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5179 lengths[i] = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5180 lengths[i].Resolve (ec);
5182 Arguments args = new Arguments (1);
5183 args.Add (new Argument (new IntConstant (i, loc)));
5184 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5187 list.Add (new Argument (v));
5190 access = new ElementAccess (copy, list, loc).Resolve (ec);
5194 Expression var_type = for_each.type;
5195 VarExpr ve = var_type as VarExpr;
5197 // Infer implicitly typed local variable from foreach array type
5198 var_type = new TypeExpression (access.Type, ve.Location);
5201 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5202 if (var_type == null)
5205 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5211 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5212 ec.CurrentBranching.CreateSibling ();
5215 variable.local_info.Type = conv.Type;
5216 variable.Resolve (ec);
5218 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5219 if (!statement.Resolve (ec))
5221 ec.EndFlowBranching ();
5223 // There's no direct control flow from the end of the embedded statement to the end of the loop
5224 ec.CurrentBranching.CurrentUsageVector.Goto ();
5226 ec.EndFlowBranching ();
5231 protected override void DoEmit (EmitContext ec)
5233 copy.EmitAssign (ec, for_each.expr);
5235 int rank = length_exprs.Length;
5236 Label[] test = new Label [rank];
5237 Label[] loop = new Label [rank];
5239 for (int i = 0; i < rank; i++) {
5240 test [i] = ec.DefineLabel ();
5241 loop [i] = ec.DefineLabel ();
5243 if (lengths != null)
5244 lengths [i].EmitAssign (ec, length_exprs [i]);
5247 IntConstant zero = new IntConstant (0, loc);
5248 for (int i = 0; i < rank; i++) {
5249 variables [i].EmitAssign (ec, zero);
5251 ec.Emit (OpCodes.Br, test [i]);
5252 ec.MarkLabel (loop [i]);
5255 variable.local_info.CreateBuilder (ec);
5256 variable.EmitAssign (ec, conv, false, false);
5258 statement.Emit (ec);
5260 ec.MarkLabel (ec.LoopBegin);
5262 for (int i = rank - 1; i >= 0; i--){
5263 counter [i].Emit (ec);
5265 ec.MarkLabel (test [i]);
5266 variables [i].Emit (ec);
5268 if (lengths != null)
5269 lengths [i].Emit (ec);
5271 length_exprs [i].Emit (ec);
5273 ec.Emit (OpCodes.Blt, loop [i]);
5276 ec.MarkLabel (ec.LoopEnd);
5280 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5282 class Body : Statement
5285 LocalVariableReference variable;
5286 Expression current, conv;
5287 Statement statement;
5289 public Body (TypeSpec type, LocalVariable variable,
5290 Expression current, Statement statement,
5294 this.variable = new LocalVariableReference (variable, loc);
5295 this.current = current;
5296 this.statement = statement;
5300 protected override void CloneTo (CloneContext clonectx, Statement target)
5302 throw new NotImplementedException ();
5305 public override bool Resolve (BlockContext ec)
5307 current = current.Resolve (ec);
5308 if (current == null)
5311 conv = Convert.ExplicitConversion (ec, current, type, loc);
5315 variable.local_info.Type = conv.Type;
5316 variable.Resolve (ec);
5318 if (!statement.Resolve (ec))
5324 protected override void DoEmit (EmitContext ec)
5326 variable.local_info.CreateBuilder (ec);
5327 variable.EmitAssign (ec, conv, false, false);
5329 statement.Emit (ec);
5333 class Dispose : UsingTemporary
5335 LocalTemporary dispose;
5337 public Dispose (TemporaryVariableReference variable, LocalTemporary dispose, Expression expr, Statement statement, Location loc)
5338 : base (expr, statement, loc)
5340 base.local_copy = variable;
5341 this.dispose = dispose;
5344 protected override bool DoResolve (BlockContext ec)
5346 if (TypeManager.void_dispose_void == null) {
5347 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5348 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5351 Expression dispose_var = (Expression) dispose ?? local_copy;
5353 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5354 dispose_mg.InstanceExpression = dispose_var;
5356 dispose_call = new StatementExpression (new Invocation (dispose_mg, null));
5358 if (!dispose_var.Type.IsStruct)
5359 dispose_call = new If (new Binary (Binary.Operator.Inequality, dispose_var, new NullLiteral (loc), loc), dispose_call, loc);
5361 return dispose_call.Resolve (ec);
5364 protected override void EmitFinallyBody (EmitContext ec)
5366 Label call_dispose = ec.DefineLabel ();
5367 if (dispose != null) {
5368 local_copy.Emit (ec, false);
5369 ec.Emit (OpCodes.Isinst, dispose.Type);
5373 base.EmitFinallyBody (ec);
5375 if (dispose != null) {
5376 ec.MarkLabel (call_dispose);
5377 dispose.Release (ec);
5382 LocalVariable variable;
5384 Statement statement;
5385 Expression var_type;
5386 ExpressionStatement init;
5387 TemporaryVariableReference enumerator_variable;
5388 bool ambiguous_getenumerator_name;
5390 public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5392 this.var_type = var_type;
5393 this.variable = var;
5399 protected override void CloneTo (CloneContext clonectx, Statement target)
5401 throw new NotImplementedException ();
5404 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5406 rc.Report.SymbolRelatedToPreviousError (enumerator);
5407 rc.Report.Error (202, loc,
5408 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5409 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5412 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5415 // Option 1: Try to match by name GetEnumerator first
5417 var mexpr = Expression.MemberLookup (rc, rc.CurrentType, expr.Type, "GetEnumerator", 0, true, loc); // TODO: What if CS0229 ?
5419 var mg = mexpr as MethodGroupExpr;
5421 mg.InstanceExpression = expr;
5422 Arguments args = new Arguments (0);
5423 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5425 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5426 if (ambiguous_getenumerator_name)
5429 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5435 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5437 TypeSpec iface_candidate = null;
5438 for (TypeSpec t = expr.Type; t != null && t != TypeManager.object_type; t = t.BaseType) {
5439 var ifaces = t.Interfaces;
5440 if (ifaces != null) {
5441 foreach (var iface in ifaces) {
5442 if (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) {
5443 if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) {
5444 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5445 rc.Report.Error (1640, loc,
5446 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5447 expr.Type.GetSignatureForError (), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5452 iface_candidate = iface;
5456 if (iface == TypeManager.ienumerable_type && iface_candidate == null) {
5457 iface_candidate = iface;
5463 if (iface_candidate == null) {
5464 rc.Report.Error (1579, loc,
5465 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5466 expr.Type.GetSignatureForError (), "GetEnumerator");
5471 var method = TypeManager.GetPredefinedMethod (iface_candidate,
5472 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null), loc);
5477 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5478 mg.InstanceExpression = expr;
5482 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5484 var ms = MemberCache.FindMember (enumerator.ReturnType,
5485 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5486 BindingRestriction.InstanceOnly) as MethodSpec;
5488 if (ms == null || !ms.IsPublic) {
5489 Error_WrongEnumerator (rc, enumerator);
5493 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5496 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5498 var ps = MemberCache.FindMember (enumerator.ReturnType,
5499 MemberFilter.Property ("Current", null),
5500 BindingRestriction.InstanceOnly) as PropertySpec;
5502 if (ps == null || !ps.IsPublic) {
5503 Error_WrongEnumerator (rc, enumerator);
5510 public override bool Resolve (BlockContext ec)
5512 bool is_dynamic = expr.Type == InternalType.Dynamic;
5514 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5516 var get_enumerator_mg = ResolveGetEnumerator (ec);
5517 if (get_enumerator_mg == null) {
5521 var get_enumerator = get_enumerator_mg.BestCandidate;
5522 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
5523 enumerator_variable.Resolve (ec);
5525 // Prepare bool MoveNext ()
5526 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5527 if (move_next_mg == null) {
5531 move_next_mg.InstanceExpression = enumerator_variable;
5533 // Prepare ~T~ Current { get; }
5534 var current_prop = ResolveCurrent (ec, get_enumerator);
5535 if (current_prop == null) {
5539 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
5540 if (current_pe == null)
5543 VarExpr ve = var_type as VarExpr;
5546 // Source type is dynamic, set element type to dynamic too
5547 var_type = new TypeExpression (InternalType.Dynamic, var_type.Location);
5549 // Infer implicitly typed local variable from foreach enumerable type
5550 var_type = new TypeExpression (current_pe.Type, var_type.Location);
5552 } else if (is_dynamic) {
5553 // Explicit cast of dynamic collection elements has to be done at runtime
5554 current_pe = EmptyCast.Create (current_pe, InternalType.Dynamic);
5557 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5558 if (var_type == null)
5561 variable.Type = var_type.Type;
5563 var init = new Invocation (get_enumerator_mg, null);
5566 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5567 new Body (var_type.Type, variable, current_pe, statement, loc), loc);
5569 var enum_type = enumerator_variable.Type;
5572 // Add Dispose method call when enumerator can be IDisposable
5574 if (!enum_type.ImplementsInterface (TypeManager.idisposable_type)) {
5575 if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) {
5577 // Runtime Dispose check
5579 var tv = new LocalTemporary (TypeManager.idisposable_type);
5580 statement = new Dispose (enumerator_variable, tv, init, statement, loc);
5583 // No Dispose call needed
5585 this.init = new SimpleAssign (enumerator_variable, init);
5586 this.init.Resolve (ec);
5590 // Static Dispose check
5592 statement = new Dispose (enumerator_variable, null, init, statement, loc);
5595 return statement.Resolve (ec);
5598 protected override void DoEmit (EmitContext ec)
5600 enumerator_variable.LocalInfo.CreateBuilder (ec);
5603 init.EmitStatement (ec);
5605 statement.Emit (ec);
5608 #region IErrorHandler Members
5610 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5612 ec.Report.SymbolRelatedToPreviousError (best);
5613 ec.Report.Warning (278, 2, loc,
5614 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5615 expr.Type.GetSignatureForError (), "enumerable",
5616 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5618 ambiguous_getenumerator_name = true;
5622 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5627 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5632 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5641 LocalVariable variable;
5643 Statement statement;
5645 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
5648 this.variable = var;
5654 public Statement Statement {
5655 get { return statement; }
5658 public override bool Resolve (BlockContext ec)
5660 expr = expr.Resolve (ec);
5665 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5669 if (expr.Type == TypeManager.string_type) {
5670 statement = new ArrayForeach (this, 1);
5671 } else if (expr.Type is ArrayContainer) {
5672 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5674 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5675 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5676 expr.ExprClassName);
5680 statement = new CollectionForeach (type, variable, expr, statement, loc);
5683 return statement.Resolve (ec);
5686 protected override void DoEmit (EmitContext ec)
5688 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5689 ec.LoopBegin = ec.DefineLabel ();
5690 ec.LoopEnd = ec.DefineLabel ();
5692 statement.Emit (ec);
5694 ec.LoopBegin = old_begin;
5695 ec.LoopEnd = old_end;
5698 protected override void CloneTo (CloneContext clonectx, Statement t)
5700 Foreach target = (Foreach) t;
5702 target.type = type.Clone (clonectx);
5703 target.expr = expr.Clone (clonectx);
5704 target.statement = statement.Clone (clonectx);