2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@seznam.cz)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
14 using System.Reflection.Emit;
15 using System.Collections.Generic;
17 namespace Mono.CSharp {
19 public abstract class Statement {
23 /// Resolves the statement, true means that all sub-statements
26 public virtual bool Resolve (BlockContext ec)
32 /// We already know that the statement is unreachable, but we still
33 /// need to resolve it to catch errors.
35 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
38 // This conflicts with csc's way of doing this, but IMHO it's
39 // the right thing to do.
41 // If something is unreachable, we still check whether it's
42 // correct. This means that you cannot use unassigned variables
43 // in unreachable code, for instance.
47 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
49 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
50 bool ok = Resolve (ec);
51 ec.KillFlowBranching ();
57 /// Return value indicates whether all code paths emitted return.
59 protected abstract void DoEmit (EmitContext ec);
61 public virtual void Emit (EmitContext ec)
68 // This routine must be overrided in derived classes and make copies
69 // of all the data that might be modified if resolved
71 protected abstract void CloneTo (CloneContext clonectx, Statement target);
73 public Statement Clone (CloneContext clonectx)
75 Statement s = (Statement) this.MemberwiseClone ();
76 CloneTo (clonectx, s);
80 public virtual Expression CreateExpressionTree (ResolveContext ec)
82 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
86 public Statement PerformClone ()
88 CloneContext clonectx = new CloneContext ();
90 return Clone (clonectx);
94 public sealed class EmptyStatement : Statement
96 public EmptyStatement (Location loc)
101 public override bool Resolve (BlockContext ec)
106 public override bool ResolveUnreachable (BlockContext ec, bool warn)
111 public override void Emit (EmitContext ec)
115 protected override void DoEmit (EmitContext ec)
117 throw new NotSupportedException ();
120 protected override void CloneTo (CloneContext clonectx, Statement target)
126 public class If : Statement {
128 public Statement TrueStatement;
129 public Statement FalseStatement;
133 public If (Expression bool_expr, Statement true_statement, Location l)
134 : this (bool_expr, true_statement, null, l)
138 public If (Expression bool_expr,
139 Statement true_statement,
140 Statement false_statement,
143 this.expr = bool_expr;
144 TrueStatement = true_statement;
145 FalseStatement = false_statement;
149 public override bool Resolve (BlockContext ec)
153 Report.Debug (1, "START IF BLOCK", loc);
155 expr = expr.Resolve (ec);
160 // Dead code elimination
162 if (expr is Constant) {
163 bool take = !((Constant) expr).IsDefaultValue;
166 if (!TrueStatement.Resolve (ec))
169 if ((FalseStatement != null) &&
170 !FalseStatement.ResolveUnreachable (ec, true))
172 FalseStatement = null;
174 if (!TrueStatement.ResolveUnreachable (ec, true))
176 TrueStatement = null;
178 if ((FalseStatement != null) &&
179 !FalseStatement.Resolve (ec))
187 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
189 ok &= TrueStatement.Resolve (ec);
191 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
193 ec.CurrentBranching.CreateSibling ();
195 if (FalseStatement != null)
196 ok &= FalseStatement.Resolve (ec);
198 ec.EndFlowBranching ();
200 Report.Debug (1, "END IF BLOCK", loc);
205 protected override void DoEmit (EmitContext ec)
207 Label false_target = ec.DefineLabel ();
211 // If we're a boolean constant, Resolve() already
212 // eliminated dead code for us.
214 Constant c = expr as Constant;
216 c.EmitSideEffect (ec);
218 if (!c.IsDefaultValue)
219 TrueStatement.Emit (ec);
220 else if (FalseStatement != null)
221 FalseStatement.Emit (ec);
226 expr.EmitBranchable (ec, false_target, false);
228 TrueStatement.Emit (ec);
230 if (FalseStatement != null){
231 bool branch_emitted = false;
233 end = ec.DefineLabel ();
235 ec.Emit (OpCodes.Br, end);
236 branch_emitted = true;
239 ec.MarkLabel (false_target);
240 FalseStatement.Emit (ec);
245 ec.MarkLabel (false_target);
249 protected override void CloneTo (CloneContext clonectx, Statement t)
253 target.expr = expr.Clone (clonectx);
254 target.TrueStatement = TrueStatement.Clone (clonectx);
255 if (FalseStatement != null)
256 target.FalseStatement = FalseStatement.Clone (clonectx);
260 public class Do : Statement {
261 public Expression expr;
262 public Statement EmbeddedStatement;
264 public Do (Statement statement, BooleanExpression bool_expr, Location l)
267 EmbeddedStatement = statement;
271 public override bool Resolve (BlockContext ec)
275 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
277 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
279 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
280 if (!EmbeddedStatement.Resolve (ec))
282 ec.EndFlowBranching ();
284 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
285 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
287 expr = expr.Resolve (ec);
290 else if (expr is Constant){
291 bool infinite = !((Constant) expr).IsDefaultValue;
293 ec.CurrentBranching.CurrentUsageVector.Goto ();
296 ec.EndFlowBranching ();
301 protected override void DoEmit (EmitContext ec)
303 Label loop = ec.DefineLabel ();
304 Label old_begin = ec.LoopBegin;
305 Label old_end = ec.LoopEnd;
307 ec.LoopBegin = ec.DefineLabel ();
308 ec.LoopEnd = ec.DefineLabel ();
311 EmbeddedStatement.Emit (ec);
312 ec.MarkLabel (ec.LoopBegin);
315 // Dead code elimination
317 if (expr is Constant){
318 bool res = !((Constant) expr).IsDefaultValue;
320 expr.EmitSideEffect (ec);
322 ec.Emit (OpCodes.Br, loop);
324 expr.EmitBranchable (ec, loop, true);
326 ec.MarkLabel (ec.LoopEnd);
328 ec.LoopBegin = old_begin;
329 ec.LoopEnd = old_end;
332 protected override void CloneTo (CloneContext clonectx, Statement t)
336 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
337 target.expr = expr.Clone (clonectx);
341 public class While : Statement {
342 public Expression expr;
343 public Statement Statement;
344 bool infinite, empty;
346 public While (BooleanExpression bool_expr, Statement statement, Location l)
348 this.expr = bool_expr;
349 Statement = statement;
353 public override bool Resolve (BlockContext ec)
357 expr = expr.Resolve (ec);
362 // Inform whether we are infinite or not
364 if (expr is Constant){
365 bool value = !((Constant) expr).IsDefaultValue;
368 if (!Statement.ResolveUnreachable (ec, true))
376 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
378 ec.CurrentBranching.CreateSibling ();
380 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
381 if (!Statement.Resolve (ec))
383 ec.EndFlowBranching ();
385 // There's no direct control flow from the end of the embedded statement to the end of the loop
386 ec.CurrentBranching.CurrentUsageVector.Goto ();
388 ec.EndFlowBranching ();
393 protected override void DoEmit (EmitContext ec)
396 expr.EmitSideEffect (ec);
400 Label old_begin = ec.LoopBegin;
401 Label old_end = ec.LoopEnd;
403 ec.LoopBegin = ec.DefineLabel ();
404 ec.LoopEnd = ec.DefineLabel ();
407 // Inform whether we are infinite or not
409 if (expr is Constant){
410 // expr is 'true', since the 'empty' case above handles the 'false' case
411 ec.MarkLabel (ec.LoopBegin);
412 expr.EmitSideEffect (ec);
414 ec.Emit (OpCodes.Br, ec.LoopBegin);
417 // Inform that we are infinite (ie, `we return'), only
418 // if we do not `break' inside the code.
420 ec.MarkLabel (ec.LoopEnd);
422 Label while_loop = ec.DefineLabel ();
424 ec.Emit (OpCodes.Br, ec.LoopBegin);
425 ec.MarkLabel (while_loop);
429 ec.MarkLabel (ec.LoopBegin);
432 expr.EmitBranchable (ec, while_loop, true);
434 ec.MarkLabel (ec.LoopEnd);
437 ec.LoopBegin = old_begin;
438 ec.LoopEnd = old_end;
441 public override void Emit (EmitContext ec)
446 protected override void CloneTo (CloneContext clonectx, Statement t)
448 While target = (While) t;
450 target.expr = expr.Clone (clonectx);
451 target.Statement = Statement.Clone (clonectx);
455 public class For : Statement {
457 Statement InitStatement;
459 public Statement Statement;
460 bool infinite, empty;
462 public For (Statement init_statement,
463 BooleanExpression test,
468 InitStatement = init_statement;
470 Increment = increment;
471 Statement = statement;
475 public override bool Resolve (BlockContext ec)
479 if (InitStatement != null){
480 if (!InitStatement.Resolve (ec))
485 Test = Test.Resolve (ec);
488 else if (Test is Constant){
489 bool value = !((Constant) Test).IsDefaultValue;
492 if (!Statement.ResolveUnreachable (ec, true))
494 if ((Increment != null) &&
495 !Increment.ResolveUnreachable (ec, false))
505 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
507 ec.CurrentBranching.CreateSibling ();
509 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
511 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
512 if (!Statement.Resolve (ec))
514 ec.EndFlowBranching ();
516 if (Increment != null){
517 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
518 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
521 if (!Increment.Resolve (ec))
526 // There's no direct control flow from the end of the embedded statement to the end of the loop
527 ec.CurrentBranching.CurrentUsageVector.Goto ();
529 ec.EndFlowBranching ();
534 protected override void DoEmit (EmitContext ec)
536 if (InitStatement != null)
537 InitStatement.Emit (ec);
540 Test.EmitSideEffect (ec);
544 Label old_begin = ec.LoopBegin;
545 Label old_end = ec.LoopEnd;
546 Label loop = ec.DefineLabel ();
547 Label test = ec.DefineLabel ();
549 ec.LoopBegin = ec.DefineLabel ();
550 ec.LoopEnd = ec.DefineLabel ();
552 ec.Emit (OpCodes.Br, test);
556 ec.MarkLabel (ec.LoopBegin);
561 // If test is null, there is no test, and we are just
566 // The Resolve code already catches the case for
567 // Test == Constant (false) so we know that
570 if (Test is Constant) {
571 Test.EmitSideEffect (ec);
572 ec.Emit (OpCodes.Br, loop);
574 Test.EmitBranchable (ec, loop, true);
578 ec.Emit (OpCodes.Br, loop);
579 ec.MarkLabel (ec.LoopEnd);
581 ec.LoopBegin = old_begin;
582 ec.LoopEnd = old_end;
585 protected override void CloneTo (CloneContext clonectx, Statement t)
587 For target = (For) t;
589 if (InitStatement != null)
590 target.InitStatement = InitStatement.Clone (clonectx);
592 target.Test = Test.Clone (clonectx);
593 if (Increment != null)
594 target.Increment = Increment.Clone (clonectx);
595 target.Statement = Statement.Clone (clonectx);
599 public class StatementExpression : Statement {
600 ExpressionStatement expr;
602 public StatementExpression (ExpressionStatement expr)
608 public override bool Resolve (BlockContext ec)
610 expr = expr.ResolveStatement (ec);
614 protected override void DoEmit (EmitContext ec)
616 expr.EmitStatement (ec);
619 public override string ToString ()
621 return "StatementExpression (" + expr + ")";
624 protected override void CloneTo (CloneContext clonectx, Statement t)
626 StatementExpression target = (StatementExpression) t;
628 target.expr = (ExpressionStatement) expr.Clone (clonectx);
633 // Simple version of statement list not requiring a block
635 public class StatementList : Statement
637 List<Statement> statements;
639 public StatementList (Statement first, Statement second)
641 statements = new List<Statement> () { first, second };
645 public IList<Statement> Statements {
652 public void Add (Statement statement)
654 statements.Add (statement);
657 public override bool Resolve (BlockContext ec)
659 foreach (var s in statements)
665 protected override void DoEmit (EmitContext ec)
667 foreach (var s in statements)
671 protected override void CloneTo (CloneContext clonectx, Statement target)
673 StatementList t = (StatementList) target;
675 t.statements = new List<Statement> (statements.Count);
676 foreach (Statement s in statements)
677 t.statements.Add (s.Clone (clonectx));
681 // A 'return' or a 'yield break'
682 public abstract class ExitStatement : Statement
684 protected bool unwind_protect;
685 protected abstract bool DoResolve (BlockContext ec);
687 public virtual void Error_FinallyClause (Report Report)
689 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
692 public sealed override bool Resolve (BlockContext ec)
697 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
699 ec.NeedReturnLabel ();
700 ec.CurrentBranching.CurrentUsageVector.Goto ();
706 /// Implements the return statement
708 public class Return : ExitStatement
710 protected Expression Expr;
711 public Return (Expression expr, Location l)
718 public Expression Expression {
725 protected override bool DoResolve (BlockContext ec)
728 if (ec.ReturnType == TypeManager.void_type)
731 if (ec.CurrentIterator != null) {
732 Error_ReturnFromIterator (ec);
734 ec.Report.Error (126, loc,
735 "An object of a type convertible to `{0}' is required for the return statement",
736 ec.ReturnType.GetSignatureForError ());
742 Expr = Expr.Resolve (ec);
744 AnonymousExpression am = ec.CurrentAnonymousMethod;
746 if (ec.ReturnType == TypeManager.void_type) {
747 ec.Report.Error (127, loc,
748 "`{0}': A return keyword must not be followed by any expression when method returns void",
749 ec.GetSignatureForError ());
753 Error_ReturnFromIterator (ec);
757 var l = am as AnonymousMethodBody;
758 if (l != null && l.ReturnTypeInference != null && Expr != null) {
759 l.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
767 if (Expr.Type != ec.ReturnType) {
768 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
772 ec.Report.Error (1662, loc,
773 "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",
774 am.ContainerType, am.GetSignatureForError ());
783 protected override void DoEmit (EmitContext ec)
789 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
793 ec.Emit (OpCodes.Leave, ec.ReturnLabel);
795 ec.Emit (OpCodes.Ret);
798 void Error_ReturnFromIterator (ResolveContext rc)
800 rc.Report.Error (1622, loc,
801 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
804 protected override void CloneTo (CloneContext clonectx, Statement t)
806 Return target = (Return) t;
807 // It's null for simple return;
809 target.Expr = Expr.Clone (clonectx);
813 public class Goto : Statement {
815 LabeledStatement label;
818 public override bool Resolve (BlockContext ec)
820 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
821 ec.CurrentBranching.CurrentUsageVector.Goto ();
825 public Goto (string label, Location l)
831 public string Target {
832 get { return target; }
835 public void SetResolvedTarget (LabeledStatement label)
838 label.AddReference ();
841 protected override void CloneTo (CloneContext clonectx, Statement target)
846 protected override void DoEmit (EmitContext ec)
849 throw new InternalErrorException ("goto emitted before target resolved");
850 Label l = label.LabelTarget (ec);
851 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
855 public class LabeledStatement : Statement {
862 FlowBranching.UsageVector vectors;
864 public LabeledStatement (string name, Block block, Location l)
871 public Label LabelTarget (EmitContext ec)
876 label = ec.DefineLabel ();
891 public bool IsDefined {
892 get { return defined; }
895 public bool HasBeenReferenced {
896 get { return referenced; }
899 public FlowBranching.UsageVector JumpOrigins {
900 get { return vectors; }
903 public void AddUsageVector (FlowBranching.UsageVector vector)
905 vector = vector.Clone ();
906 vector.Next = vectors;
910 protected override void CloneTo (CloneContext clonectx, Statement target)
915 public override bool Resolve (BlockContext ec)
917 // this flow-branching will be terminated when the surrounding block ends
918 ec.StartFlowBranching (this);
922 protected override void DoEmit (EmitContext ec)
924 if (!HasBeenReferenced)
925 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
928 ec.MarkLabel (label);
931 public void AddReference ()
939 /// `goto default' statement
941 public class GotoDefault : Statement {
943 public GotoDefault (Location l)
948 protected override void CloneTo (CloneContext clonectx, Statement target)
953 public override bool Resolve (BlockContext ec)
955 ec.CurrentBranching.CurrentUsageVector.Goto ();
957 if (ec.Switch == null) {
958 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
962 if (!ec.Switch.GotDefault) {
963 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
970 protected override void DoEmit (EmitContext ec)
972 ec.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
977 /// `goto case' statement
979 public class GotoCase : Statement {
983 public GotoCase (Expression e, Location l)
989 public override bool Resolve (BlockContext ec)
991 if (ec.Switch == null){
992 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
996 ec.CurrentBranching.CurrentUsageVector.Goto ();
998 expr = expr.Resolve (ec);
1002 Constant c = expr as Constant;
1004 ec.Report.Error (150, expr.Location, "A constant value is expected");
1008 TypeSpec type = ec.Switch.SwitchType;
1009 Constant res = c.TryReduce (ec, type, c.Location);
1011 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1015 if (!Convert.ImplicitStandardConversionExists (c, type))
1016 ec.Report.Warning (469, 2, loc,
1017 "The `goto case' value is not implicitly convertible to type `{0}'",
1018 TypeManager.CSharpName (type));
1020 object val = res.GetValue ();
1022 val = SwitchLabel.NullStringCase;
1024 if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
1025 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1026 (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1033 protected override void DoEmit (EmitContext ec)
1035 ec.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1038 protected override void CloneTo (CloneContext clonectx, Statement t)
1040 GotoCase target = (GotoCase) t;
1042 target.expr = expr.Clone (clonectx);
1046 public class Throw : Statement {
1049 public Throw (Expression expr, Location l)
1055 public override bool Resolve (BlockContext ec)
1058 ec.CurrentBranching.CurrentUsageVector.Goto ();
1059 return ec.CurrentBranching.CheckRethrow (loc);
1062 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1063 ec.CurrentBranching.CurrentUsageVector.Goto ();
1068 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1069 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1071 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1076 protected override void DoEmit (EmitContext ec)
1079 ec.Emit (OpCodes.Rethrow);
1083 ec.Emit (OpCodes.Throw);
1087 protected override void CloneTo (CloneContext clonectx, Statement t)
1089 Throw target = (Throw) t;
1092 target.expr = expr.Clone (clonectx);
1096 public class Break : Statement {
1098 public Break (Location l)
1103 bool unwind_protect;
1105 public override bool Resolve (BlockContext ec)
1107 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1108 ec.CurrentBranching.CurrentUsageVector.Goto ();
1112 protected override void DoEmit (EmitContext ec)
1114 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1117 protected override void CloneTo (CloneContext clonectx, Statement t)
1123 public class Continue : Statement {
1125 public Continue (Location l)
1130 bool unwind_protect;
1132 public override bool Resolve (BlockContext ec)
1134 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1135 ec.CurrentBranching.CurrentUsageVector.Goto ();
1139 protected override void DoEmit (EmitContext ec)
1141 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1144 protected override void CloneTo (CloneContext clonectx, Statement t)
1150 public interface ILocalVariable
1152 void Emit (EmitContext ec);
1153 void EmitAssign (EmitContext ec);
1154 void EmitAddressOf (EmitContext ec);
1157 public interface INamedBlockVariable
1159 Block Block { get; }
1160 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1161 bool IsDeclared { get; }
1162 Location Location { get; }
1165 public class BlockVariableDeclaration : Statement
1167 public class Declarator
1170 Expression initializer;
1172 public Declarator (LocalVariable li, Expression initializer)
1174 if (li.Type != null)
1175 throw new ArgumentException ("Expected null variable type");
1178 this.initializer = initializer;
1181 public Declarator (Declarator clone, Expression initializer)
1184 this.initializer = initializer;
1189 public LocalVariable Variable {
1195 public Expression Initializer {
1200 initializer = value;
1207 Expression initializer;
1208 protected FullNamedExpression type_expr;
1209 protected LocalVariable li;
1210 protected List<Declarator> declarators;
1212 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1214 this.type_expr = type;
1216 this.loc = type_expr.Location;
1219 protected BlockVariableDeclaration (LocalVariable li)
1226 public List<Declarator> Declarators {
1232 public Expression Initializer {
1237 initializer = value;
1241 public FullNamedExpression TypeExpression {
1247 public LocalVariable Variable {
1255 public void AddDeclarator (Declarator decl)
1257 if (declarators == null)
1258 declarators = new List<Declarator> ();
1260 declarators.Add (decl);
1263 void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1265 var container = bc.CurrentMemberDefinition.Parent;
1267 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1268 new MemberName (li.Name, li.Location), null);
1270 container.AddField (f);
1272 Evaluator.QueueField (f);
1274 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1278 public override bool Resolve (BlockContext bc)
1280 if (li.Type == null) {
1281 TypeSpec type = null;
1282 if (type_expr is VarExpr) {
1284 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1285 // same name exists or as a keyword when no type was found
1287 var texpr = type_expr.ResolveAsTypeTerminal (bc, true);
1288 if (texpr == null) {
1289 if (RootContext.Version < LanguageVersion.V_3)
1290 bc.Report.FeatureIsNotAvailable (loc, "implicitly typed local variable");
1293 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1297 if (li.IsConstant) {
1298 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1302 if (Initializer == null) {
1303 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1307 if (declarators != null) {
1308 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1312 Initializer = Initializer.Resolve (bc);
1313 if (Initializer != null) {
1314 ((VarExpr) type_expr).InferType (bc, Initializer);
1315 type = type_expr.Type;
1321 var texpr = type_expr.ResolveAsTypeTerminal (bc, false);
1327 if (li.IsConstant && !type.IsConstantCompatible) {
1328 Const.Error_InvalidConstantType (type, loc, bc.Report);
1333 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1335 if (type.IsPointer && !bc.IsUnsafe)
1336 Expression.UnsafeError (bc, loc);
1341 bool eval_global = RootContext.StatementMode && bc.CurrentBlock is ToplevelBlock;
1343 CreateEvaluatorVariable (bc, li);
1345 li.PrepareForFlowAnalysis (bc);
1348 if (initializer != null) {
1349 initializer = ResolveInitializer (bc, li, initializer);
1350 // li.Variable.DefinitelyAssigned
1353 if (declarators != null) {
1354 foreach (var d in declarators) {
1355 d.Variable.Type = li.Type;
1357 CreateEvaluatorVariable (bc, d.Variable);
1359 d.Variable.PrepareForFlowAnalysis (bc);
1362 if (d.Initializer != null) {
1363 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1364 // d.Variable.DefinitelyAssigned
1372 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1374 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1375 return a.ResolveStatement (bc);
1378 protected override void DoEmit (EmitContext ec)
1383 li.CreateBuilder (ec);
1385 if (Initializer != null)
1386 ((ExpressionStatement) Initializer).EmitStatement (ec);
1388 if (declarators != null) {
1389 foreach (var d in declarators) {
1390 d.Variable.CreateBuilder (ec);
1391 if (d.Initializer != null)
1392 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1397 protected override void CloneTo (CloneContext clonectx, Statement target)
1399 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1401 if (type_expr != null)
1402 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1404 if (initializer != null)
1405 t.initializer = initializer.Clone (clonectx);
1407 if (declarators != null) {
1408 t.declarators = null;
1409 foreach (var d in declarators)
1410 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1415 class BlockConstantDeclaration : BlockVariableDeclaration
1417 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1422 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1424 initializer = initializer.Resolve (bc);
1425 if (initializer == null)
1428 var c = initializer as Constant;
1430 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1434 c = c.ConvertImplicitly (bc, li.Type);
1436 if (TypeManager.IsReferenceType (li.Type))
1437 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1439 initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
1444 li.ConstantValue = c;
1450 // The information about a user-perceived local variable
1452 public class LocalVariable : INamedBlockVariable, ILocalVariable
1459 AddressTaken = 1 << 2,
1460 CompilerGenerated = 1 << 3,
1462 ForeachVariable = 1 << 5,
1463 FixedVariable = 1 << 6,
1464 UsingVariable = 1 << 7,
1465 // DefinitelyAssigned = 1 << 8,
1468 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1472 readonly string name;
1473 readonly Location loc;
1474 readonly Block block;
1476 Constant const_value;
1478 public VariableInfo VariableInfo;
1479 HoistedVariable hoisted_variant;
1481 LocalBuilder builder;
1483 public LocalVariable (Block block, string name, Location loc)
1490 public LocalVariable (Block block, string name, Flags flags, Location loc)
1491 : this (block, name, loc)
1497 // Used by variable declarators
1499 public LocalVariable (LocalVariable li, string name, Location loc)
1500 : this (li.block, name, li.flags, loc)
1506 public bool AddressTaken {
1507 get { return (flags & Flags.AddressTaken) != 0; }
1508 set { flags |= Flags.AddressTaken; }
1511 public Block Block {
1517 public Constant ConstantValue {
1522 const_value = value;
1527 // Hoisted local variable variant
1529 public HoistedVariable HoistedVariant {
1531 return hoisted_variant;
1534 hoisted_variant = value;
1538 public bool IsDeclared {
1540 return type != null;
1544 public bool IsConstant {
1546 return (flags & Flags.Constant) != 0;
1550 public bool IsLocked {
1552 return (flags & Flags.IsLocked) != 0;
1555 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1559 public bool IsThis {
1561 return (flags & Flags.IsThis) != 0;
1565 public bool IsFixed {
1567 return (flags & Flags.FixedVariable) != 0;
1571 public bool IsReadonly {
1573 return (flags & Flags.ReadonlyMask) != 0;
1577 public Location Location {
1583 public string Name {
1589 public TypeSpec Type {
1600 public void CreateBuilder (EmitContext ec)
1602 if ((flags & Flags.Used) == 0) {
1603 if (VariableInfo == null) {
1604 // Missing flow analysis or wrong variable flags
1605 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1608 if (VariableInfo.IsEverAssigned)
1609 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1611 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1614 if (HoistedVariant != null)
1617 if (builder != null) {
1618 if ((flags & Flags.CompilerGenerated) != 0)
1621 // To avoid Used warning duplicates
1622 throw new InternalErrorException ("Already created variable `{0}'", name);
1626 // All fixed variabled are pinned, a slot has to be alocated
1628 builder = ec.DeclareLocal (Type, IsFixed);
1629 if (SymbolWriter.HasSymbolWriter)
1630 ec.DefineLocalVariable (name, builder);
1633 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1635 LocalVariable li = new LocalVariable (block, "<$$>", Flags.CompilerGenerated | Flags.Used, loc);
1640 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1642 if (IsConstant && const_value != null)
1643 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc).Resolve (rc);
1645 return new LocalVariableReference (this, loc);
1648 public void Emit (EmitContext ec)
1650 // TODO: Need something better for temporary variables
1651 if ((flags & Flags.CompilerGenerated) != 0)
1654 ec.Emit (OpCodes.Ldloc, builder);
1657 public void EmitAssign (EmitContext ec)
1659 // TODO: Need something better for temporary variables
1660 if ((flags & Flags.CompilerGenerated) != 0)
1663 ec.Emit (OpCodes.Stloc, builder);
1666 public void EmitAddressOf (EmitContext ec)
1668 ec.Emit (OpCodes.Ldloca, builder);
1671 public string GetReadOnlyContext ()
1673 switch (flags & Flags.ReadonlyMask) {
1674 case Flags.FixedVariable:
1675 return "fixed variable";
1676 case Flags.ForeachVariable:
1677 return "foreach iteration variable";
1678 case Flags.UsingVariable:
1679 return "using variable";
1682 throw new InternalErrorException ("Variable is not readonly");
1685 public bool IsThisAssigned (BlockContext ec, Block block)
1687 if (VariableInfo == null)
1688 throw new Exception ();
1690 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1693 return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1696 public bool IsAssigned (BlockContext ec)
1698 if (VariableInfo == null)
1699 throw new Exception ();
1701 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1704 public void PrepareForFlowAnalysis (BlockContext bc)
1707 // No need for definitely assigned check for these guys
1709 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1712 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1713 bc.FlowOffset += VariableInfo.Length;
1717 // Mark the variables as referenced in the user code
1719 public void SetIsUsed ()
1721 flags |= Flags.Used;
1724 public override string ToString ()
1726 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
1731 /// Block represents a C# block.
1735 /// This class is used in a number of places: either to represent
1736 /// explicit blocks that the programmer places or implicit blocks.
1738 /// Implicit blocks are used as labels or to introduce variable
1741 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1742 /// they contain extra information that is not necessary on normal blocks.
1744 public class Block : Statement {
1752 HasCapturedVariable = 64,
1753 HasCapturedThis = 1 << 7,
1754 IsExpressionTree = 1 << 8,
1755 CompilerGenerated = 1 << 9
1758 public Block Parent;
1759 public Location StartLocation;
1760 public Location EndLocation;
1762 public ExplicitBlock Explicit;
1763 public ParametersBlock ParametersBlock;
1765 protected Flags flags;
1768 // The statements in this block
1770 protected List<Statement> statements;
1772 protected List<Statement> scope_initializers;
1774 int? resolving_init_idx;
1780 public int ID = id++;
1782 static int clone_id_counter;
1786 // int assignable_slots;
1787 bool unreachable_shown;
1790 public Block (Block parent, Location start, Location end)
1791 : this (parent, 0, start, end)
1795 public Block (Block parent, Flags flags, Location start, Location end)
1797 if (parent != null) {
1798 // the appropriate constructors will fixup these fields
1799 ParametersBlock = parent.ParametersBlock;
1800 Explicit = parent.Explicit;
1803 this.Parent = parent;
1805 this.StartLocation = start;
1806 this.EndLocation = end;
1808 statements = new List<Statement> (4);
1810 this.original = this;
1815 public bool HasRet {
1816 get { return (flags & Flags.HasRet) != 0; }
1819 public Block Original {
1825 public bool IsCompilerGenerated {
1826 get { return (flags & Flags.CompilerGenerated) != 0; }
1827 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
1830 public bool Unchecked {
1831 get { return (flags & Flags.Unchecked) != 0; }
1832 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1835 public bool Unsafe {
1836 get { return (flags & Flags.Unsafe) != 0; }
1837 set { flags |= Flags.Unsafe; }
1842 public Block CreateSwitchBlock (Location start)
1844 // FIXME: Only explicit block should be created
1845 var new_block = new Block (this, start, start);
1846 new_block.IsCompilerGenerated = true;
1850 public void SetEndLocation (Location loc)
1855 public void AddLabel (LabeledStatement target)
1857 ParametersBlock.TopBlock.AddLabel (target.Name, target);
1860 public void AddLocalName (LocalVariable li)
1862 AddLocalName (li.Name, li);
1865 public virtual void AddLocalName (string name, INamedBlockVariable li)
1867 ParametersBlock.TopBlock.AddLocalName (name, li);
1870 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
1872 if (reason == null) {
1873 Error_AlreadyDeclared (name, variable);
1877 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
1878 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
1879 "to `{0}', which is already used in a `{1}' scope to denote something else",
1883 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
1885 var pi = variable as ParametersBlock.ParameterInfo;
1887 var p = pi.Parameter;
1888 if (p is AnonymousTypeClass.GeneratedParameter) {
1889 ParametersBlock.TopBlock.Report.Error (833, p.Location, "`{0}': An anonymous type cannot have multiple properties with the same name",
1892 ParametersBlock.TopBlock.Report.Error (100, p.Location, "The parameter name `{0}' is a duplicate", p.Name);
1898 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
1899 "A local variable named `{0}' is already defined in this scope", name);
1902 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
1904 ParametersBlock.TopBlock.Report.Error (412, loc,
1905 "The type parameter name `{0}' is the same as local variable or parameter name",
1910 // It should be used by expressions which require to
1911 // register a statement during resolve process.
1913 public void AddScopeStatement (Statement s)
1915 if (scope_initializers == null)
1916 scope_initializers = new List<Statement> ();
1919 // Simple recursive helper, when resolve scope initializer another
1920 // new scope initializer can be added, this ensures it's initialized
1921 // before existing one. For now this can happen with expression trees
1922 // in base ctor initializer only
1924 if (resolving_init_idx.HasValue) {
1925 scope_initializers.Insert (resolving_init_idx.Value, s);
1926 ++resolving_init_idx;
1928 scope_initializers.Add (s);
1932 public void AddStatement (Statement s)
1937 public int AssignableSlots {
1939 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
1941 // return assignable_slots;
1945 public LabeledStatement LookupLabel (string name)
1947 return ParametersBlock.TopBlock.GetLabel (name, this);
1950 public override bool Resolve (BlockContext ec)
1952 Block prev_block = ec.CurrentBlock;
1955 ec.CurrentBlock = this;
1956 ec.StartFlowBranching (this);
1958 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
1961 // Compiler generated scope statements
1963 if (scope_initializers != null) {
1964 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
1965 scope_initializers[resolving_init_idx.Value].Resolve (ec);
1968 resolving_init_idx = null;
1972 // This flag is used to notate nested statements as unreachable from the beginning of this block.
1973 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
1974 // from the beginning of the function. The outer Resolve() that detected the unreachability is
1975 // responsible for handling the situation.
1977 int statement_count = statements.Count;
1978 for (int ix = 0; ix < statement_count; ix++){
1979 Statement s = statements [ix];
1982 // Warn if we detect unreachable code.
1985 if (s is EmptyStatement)
1988 if (!unreachable_shown && !(s is LabeledStatement)) {
1989 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
1990 unreachable_shown = true;
1993 Block c_block = s as Block;
1994 if (c_block != null)
1995 c_block.unreachable = c_block.unreachable_shown = true;
1999 // Note that we're not using ResolveUnreachable() for unreachable
2000 // statements here. ResolveUnreachable() creates a temporary
2001 // flow branching and kills it afterwards. This leads to problems
2002 // if you have two unreachable statements where the first one
2003 // assigns a variable and the second one tries to access it.
2006 if (!s.Resolve (ec)) {
2008 if (ec.IsInProbingMode)
2011 statements [ix] = new EmptyStatement (s.loc);
2015 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2016 statements [ix] = new EmptyStatement (s.loc);
2018 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2019 if (unreachable && s is LabeledStatement)
2020 throw new InternalErrorException ("should not happen");
2023 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2024 ec.CurrentBranching, statement_count);
2026 while (ec.CurrentBranching is FlowBranchingLabeled)
2027 ec.EndFlowBranching ();
2029 bool flow_unreachable = ec.EndFlowBranching ();
2031 ec.CurrentBlock = prev_block;
2033 if (flow_unreachable)
2034 flags |= Flags.HasRet;
2036 // If we're a non-static `struct' constructor which doesn't have an
2037 // initializer, then we must initialize all of the struct's fields.
2038 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2044 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2046 unreachable_shown = true;
2050 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2052 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2053 bool ok = Resolve (ec);
2054 ec.KillFlowBranching ();
2059 protected override void DoEmit (EmitContext ec)
2061 for (int ix = 0; ix < statements.Count; ix++){
2062 statements [ix].Emit (ec);
2066 public override void Emit (EmitContext ec)
2068 if (scope_initializers != null)
2069 EmitScopeInitializers (ec);
2071 ec.Mark (StartLocation);
2074 if (SymbolWriter.HasSymbolWriter)
2075 EmitSymbolInfo (ec);
2078 protected void EmitScopeInitializers (EmitContext ec)
2080 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2082 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2083 foreach (Statement s in scope_initializers)
2087 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2090 protected virtual void EmitSymbolInfo (EmitContext ec)
2095 public override string ToString ()
2097 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2101 protected override void CloneTo (CloneContext clonectx, Statement t)
2103 Block target = (Block) t;
2105 target.clone_id = clone_id_counter++;
2108 clonectx.AddBlockMap (this, target);
2110 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2111 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2114 target.Parent = clonectx.RemapBlockCopy (Parent);
2116 target.statements = new List<Statement> (statements.Count);
2117 foreach (Statement s in statements)
2118 target.statements.Add (s.Clone (clonectx));
2122 public class ExplicitBlock : Block
2124 protected AnonymousMethodStorey am_storey;
2126 public ExplicitBlock (Block parent, Location start, Location end)
2127 : this (parent, (Flags) 0, start, end)
2131 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2132 : base (parent, flags, start, end)
2134 this.Explicit = this;
2139 public AnonymousMethodStorey AnonymousMethodStorey {
2145 public bool HasCapturedThis {
2146 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2148 return (flags & Flags.HasCapturedThis) != 0;
2152 public bool HasCapturedVariable {
2153 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2155 return (flags & Flags.HasCapturedVariable) != 0;
2162 // Creates anonymous method storey in current block
2164 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2167 // An iterator has only 1 storey block
2169 if (ec.CurrentIterator != null)
2170 return ec.CurrentIterator.Storey;
2173 // When referencing a variable in iterator storey from children anonymous method
2175 if (ParametersBlock.am_storey is IteratorStorey) {
2176 return ParametersBlock.am_storey;
2179 if (am_storey == null) {
2180 MemberBase mc = ec.MemberContext as MemberBase;
2183 // Creates anonymous method storey for this block
2185 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2191 public override void Emit (EmitContext ec)
2193 if (am_storey != null) {
2194 DefineAnonymousStorey (ec);
2195 am_storey.EmitStoreyInstantiation (ec, this);
2198 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2199 if (emit_debug_info)
2204 if (emit_debug_info)
2208 void DefineAnonymousStorey (EmitContext ec)
2211 // Creates anonymous method storey
2213 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2215 // Creates parent storey reference when hoisted this is accessible
2217 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2218 ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2221 // Hoisted this exists in top-level parent storey only
2223 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2224 parent = parent.Parent.Explicit;
2226 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2229 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2231 // TODO MemberCache: Review
2232 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2235 am_storey.CreateType ();
2236 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2237 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2239 am_storey.DefineType ();
2240 am_storey.ResolveTypeParameters ();
2242 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2243 if (ref_blocks != null) {
2244 foreach (ExplicitBlock ref_block in ref_blocks) {
2245 for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2246 if (b.am_storey != null) {
2247 b.am_storey.AddParentStoreyReference (ec, am_storey);
2249 // Stop propagation inside same top block
2250 if (b.ParametersBlock.am_storey == ParametersBlock.am_storey)
2253 b = b.ParametersBlock;
2256 b.HasCapturedVariable = true;
2261 am_storey.Define ();
2262 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2265 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2267 tryBlock.statements = statements;
2268 statements = new List<Statement> (1);
2269 statements.Add (tf);
2274 // ParametersBlock was introduced to support anonymous methods
2275 // and lambda expressions
2277 public class ParametersBlock : ExplicitBlock
2279 public class ParameterInfo : INamedBlockVariable
2281 readonly ParametersBlock block;
2283 public VariableInfo VariableInfo;
2286 public ParameterInfo (ParametersBlock block, int index)
2294 public Block Block {
2300 public bool IsDeclared {
2306 public bool IsLocked {
2315 public Location Location {
2317 return Parameter.Location;
2321 public Parameter Parameter {
2323 return block.Parameters [index];
2327 public TypeSpec ParameterType {
2329 return Parameter.Type;
2335 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2337 return new ParameterReference (this, loc);
2342 // Block is converted to an expression
2344 sealed class BlockScopeExpression : Expression
2347 readonly ParametersBlock block;
2349 public BlockScopeExpression (Expression child, ParametersBlock block)
2355 public override Expression CreateExpressionTree (ResolveContext ec)
2357 throw new NotSupportedException ();
2360 protected override Expression DoResolve (ResolveContext ec)
2365 child = child.Resolve (ec);
2369 eclass = child.eclass;
2374 public override void Emit (EmitContext ec)
2376 block.EmitScopeInitializers (ec);
2381 protected ParametersCompiled parameters;
2382 protected ParameterInfo[] parameter_info;
2384 protected bool unreachable;
2385 protected ToplevelBlock top_block;
2387 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2388 : base (parent, 0, start, start)
2390 if (parameters == null)
2391 throw new ArgumentNullException ("parameters");
2393 this.parameters = parameters;
2394 ParametersBlock = this;
2396 this.top_block = parent.ParametersBlock.top_block;
2397 ProcessParameters ();
2400 protected ParametersBlock (ParametersCompiled parameters, Location start)
2401 : base (null, 0, start, start)
2403 if (parameters == null)
2404 throw new ArgumentNullException ("parameters");
2406 this.parameters = parameters;
2407 ParametersBlock = this;
2410 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2411 : base (null, 0, source.StartLocation, source.EndLocation)
2413 this.parameters = parameters;
2414 this.statements = source.statements;
2415 this.scope_initializers = source.scope_initializers;
2417 this.resolved = true;
2418 this.unreachable = source.unreachable;
2419 this.am_storey = source.am_storey;
2421 ParametersBlock = this;
2427 // Block has been converted to expression tree
2429 public bool IsExpressionTree {
2431 return (flags & Flags.IsExpressionTree) != 0;
2436 // The parameters for the block.
2438 public ParametersCompiled Parameters {
2444 public ToplevelBlock TopBlock {
2450 public bool Resolved {
2459 // Check whether all `out' parameters have been assigned.
2461 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2463 if (vector.IsUnreachable)
2466 int n = parameter_info == null ? 0 : parameter_info.Length;
2468 for (int i = 0; i < n; i++) {
2469 VariableInfo var = parameter_info[i].VariableInfo;
2474 if (vector.IsAssigned (var, false))
2477 TopBlock.Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2482 public override Expression CreateExpressionTree (ResolveContext ec)
2484 if (statements.Count == 1) {
2485 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2486 if (scope_initializers != null)
2487 expr = new BlockScopeExpression (expr, this);
2492 return base.CreateExpressionTree (ec);
2495 public ParameterInfo GetParameterInfo (Parameter p)
2497 for (int i = 0; i < parameters.Count; ++i) {
2498 if (parameters[i] == p)
2499 return parameter_info[i];
2502 throw new ArgumentException ("Invalid parameter");
2505 public Expression GetParameterReference (int index, Location loc)
2507 return new ParameterReference (parameter_info[index], loc);
2510 protected void ProcessParameters ()
2512 if (parameters.Count == 0)
2515 parameter_info = new ParameterInfo[parameters.Count];
2516 for (int i = 0; i < parameter_info.Length; ++i) {
2517 var p = parameters.FixedParameters[i];
2521 // TODO: Should use Parameter only and more block there
2522 parameter_info[i] = new ParameterInfo (this, i);
2523 AddLocalName (p.Name, parameter_info[i]);
2527 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2534 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2535 flags |= Flags.IsExpressionTree;
2540 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2541 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2546 unreachable = top_level.End ();
2548 } catch (Exception e) {
2549 if (e is CompletionResult || rc.Report.IsDisabled)
2552 if (rc.CurrentBlock != null) {
2553 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2555 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2558 if (Report.DebugFlags > 0)
2562 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2563 if (rc.CurrentAnonymousMethod == null) {
2564 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2565 if (md is IteratorMethod) {
2568 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2572 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2573 rc.CurrentAnonymousMethod.GetSignatureForError ());
2581 void ResolveMeta (BlockContext ec)
2583 int orig_count = parameters.Count;
2585 for (int i = 0; i < orig_count; ++i) {
2586 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2588 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2591 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2592 parameter_info[i].VariableInfo = vi;
2593 ec.FlowOffset += vi.Length;
2597 public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
2599 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2600 pb.EndLocation = EndLocation;
2601 pb.statements = statements;
2603 var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2604 am_storey = new IteratorStorey (iterator);
2606 statements = new List<Statement> (1);
2607 AddStatement (new Return (iterator, iterator.Location));
2614 public class ToplevelBlock : ParametersBlock
2616 LocalVariable this_variable;
2617 CompilerContext compiler;
2618 Dictionary<string, object> names;
2619 Dictionary<string, object> labels;
2621 public HoistedVariable HoistedThisVariable;
2623 public Report Report {
2624 get { return compiler.Report; }
2627 public ToplevelBlock (CompilerContext ctx, Location loc)
2628 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2632 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2633 : base (parameters, start)
2635 this.compiler = ctx;
2638 ProcessParameters ();
2642 // Recreates a top level block from parameters block. Used for
2643 // compiler generated methods where the original block comes from
2644 // explicit child block. This works for already resolved blocks
2645 // only to ensure we resolve them in the correct flow order
2647 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
2648 : base (source, parameters)
2650 this.compiler = source.TopBlock.compiler;
2654 public override void AddLocalName (string name, INamedBlockVariable li)
2657 names = new Dictionary<string, object> ();
2660 if (!names.TryGetValue (name, out value)) {
2661 names.Add (name, li);
2665 INamedBlockVariable existing = value as INamedBlockVariable;
2666 List<INamedBlockVariable> existing_list;
2667 if (existing != null) {
2668 existing_list = new List<INamedBlockVariable> ();
2669 existing_list.Add (existing);
2670 names[name] = existing_list;
2672 existing_list = (List<INamedBlockVariable>) value;
2676 // A collision checking between local names
2678 for (int i = 0; i < existing_list.Count; ++i) {
2679 existing = existing_list[i];
2680 Block b = existing.Block;
2682 // Collision at same level
2683 if (li.Block == b) {
2684 li.Block.Error_AlreadyDeclared (name, li);
2688 // Collision with parent
2690 while ((b = b.Parent) != null) {
2691 if (existing.Block == b) {
2692 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
2693 i = existing_list.Count;
2698 // Collision with with children
2700 while ((b = b.Parent) != null) {
2701 if (li.Block == b) {
2702 li.Block.Error_AlreadyDeclared (name, li, "child");
2703 i = existing_list.Count;
2709 existing_list.Add (li);
2712 public void AddLabel (string name, LabeledStatement label)
2715 labels = new Dictionary<string, object> ();
2718 if (!labels.TryGetValue (name, out value)) {
2719 labels.Add (name, label);
2723 LabeledStatement existing = value as LabeledStatement;
2724 List<LabeledStatement> existing_list;
2725 if (existing != null) {
2726 existing_list = new List<LabeledStatement> ();
2727 existing_list.Add (existing);
2728 labels[name] = existing_list;
2730 existing_list = (List<LabeledStatement>) value;
2734 // A collision checking between labels
2736 for (int i = 0; i < existing_list.Count; ++i) {
2737 existing = existing_list[i];
2738 Block b = existing.Block;
2740 // Collision at same level
2741 if (label.Block == b) {
2742 Report.SymbolRelatedToPreviousError (existing.loc, name);
2743 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
2747 // Collision with parent
2749 while ((b = b.Parent) != null) {
2750 if (existing.Block == b) {
2751 Report.Error (158, label.loc,
2752 "The label `{0}' shadows another label by the same name in a contained scope", name);
2753 i = existing_list.Count;
2758 // Collision with with children
2760 while ((b = b.Parent) != null) {
2761 if (label.Block == b) {
2762 Report.Error (158, label.loc,
2763 "The label `{0}' shadows another label by the same name in a contained scope", name);
2764 i = existing_list.Count;
2770 existing_list.Add (label);
2774 // Creates an arguments set from all parameters, useful for method proxy calls
2776 public Arguments GetAllParametersArguments ()
2778 int count = parameters.Count;
2779 Arguments args = new Arguments (count);
2780 for (int i = 0; i < count; ++i) {
2781 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
2782 args.Add (new Argument (arg_expr));
2789 // Lookup inside a block, the returned value can represent 3 states
2791 // true+variable: A local name was found and it's valid
2792 // false+variable: A local name was found in a child block only
2793 // false+null: No local name was found
2795 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
2801 if (!names.TryGetValue (name, out value))
2804 variable = value as INamedBlockVariable;
2806 if (variable != null) {
2808 if (variable.Block == b.Original)
2812 } while (b != null);
2820 } while (b != null);
2822 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
2823 for (int i = 0; i < list.Count; ++i) {
2826 if (variable.Block == b.Original)
2830 } while (b != null);
2838 } while (b != null);
2848 public LabeledStatement GetLabel (string name, Block block)
2854 if (!labels.TryGetValue (name, out value)) {
2858 var label = value as LabeledStatement;
2860 if (label != null) {
2861 if (label.Block == b.Original)
2864 // TODO: Temporary workaround for the switch block implicit label block
2865 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2868 List<LabeledStatement> list = (List<LabeledStatement>) value;
2869 for (int i = 0; i < list.Count; ++i) {
2871 if (label.Block == b.Original)
2874 // TODO: Temporary workaround for the switch block implicit label block
2875 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2884 // Returns the "this" instance variable of this block.
2885 // See AddThisVariable() for more information.
2887 public LocalVariable ThisVariable {
2888 get { return this_variable; }
2892 // This is used by non-static `struct' constructors which do not have an
2893 // initializer - in this case, the constructor must initialize all of the
2894 // struct's fields. To do this, we add a "this" variable and use the flow
2895 // analysis code to ensure that it's been fully initialized before control
2896 // leaves the constructor.
2898 public LocalVariable AddThisVariable (BlockContext bc, TypeContainer ds, Location l)
2900 if (this_variable == null) {
2901 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, l);
2902 this_variable.Type = ds.CurrentType;
2903 this_variable.PrepareForFlowAnalysis (bc);
2906 return this_variable;
2909 public bool IsIterator {
2910 get { return (flags & Flags.IsIterator) != 0; }
2911 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2914 public bool IsThisAssigned (BlockContext ec)
2916 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2919 public override void Emit (EmitContext ec)
2921 if (Report.Errors > 0)
2927 if (ec.HasReturnLabel)
2928 ec.ReturnLabel = ec.DefineLabel ();
2932 ec.Mark (EndLocation);
2934 if (ec.HasReturnLabel)
2935 ec.MarkLabel (ec.ReturnLabel);
2937 if (ec.return_value != null) {
2938 ec.Emit (OpCodes.Ldloc, ec.return_value);
2939 ec.Emit (OpCodes.Ret);
2942 // If `HasReturnLabel' is set, then we already emitted a
2943 // jump to the end of the method, so we must emit a `ret'
2946 // Unfortunately, System.Reflection.Emit automatically emits
2947 // a leave to the end of a finally block. This is a problem
2948 // if no code is following the try/finally block since we may
2949 // jump to a point after the end of the method.
2950 // As a workaround, we're always creating a return label in
2954 if (ec.HasReturnLabel || !unreachable) {
2955 if (ec.ReturnType != TypeManager.void_type)
2956 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2957 ec.Emit (OpCodes.Ret);
2962 } catch (Exception e){
2963 Console.WriteLine ("Exception caught by the compiler while emitting:");
2964 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
2966 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2972 protected override void EmitSymbolInfo (EmitContext ec)
2974 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2975 if ((ae != null) && (ae.Storey != null))
2976 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2978 base.EmitSymbolInfo (ec);
2982 public class SwitchLabel {
2989 Label il_label_code;
2990 bool il_label_code_set;
2992 public static readonly object NullStringCase = new object ();
2995 // if expr == null, then it is the default case.
2997 public SwitchLabel (Expression expr, Location l)
3003 public Expression Label {
3009 public Location Location {
3013 public object Converted {
3019 public Label GetILLabel (EmitContext ec)
3022 il_label = ec.DefineLabel ();
3023 il_label_set = true;
3028 public Label GetILLabelCode (EmitContext ec)
3030 if (!il_label_code_set){
3031 il_label_code = ec.DefineLabel ();
3032 il_label_code_set = true;
3034 return il_label_code;
3038 // Resolves the expression, reduces it to a literal if possible
3039 // and then converts it to the requested type.
3041 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3043 Expression e = label.Resolve (ec);
3048 Constant c = e as Constant;
3050 ec.Report.Error (150, loc, "A constant value is expected");
3054 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3055 converted = NullStringCase;
3059 if (allow_nullable && c.GetValue () == null) {
3060 converted = NullStringCase;
3064 c = c.ImplicitConversionRequired (ec, required_type, loc);
3068 converted = c.GetValue ();
3072 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3075 if (converted == null)
3077 else if (converted == NullStringCase)
3080 label = converted.ToString ();
3082 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3083 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3086 public SwitchLabel Clone (CloneContext clonectx)
3088 return new SwitchLabel (label.Clone (clonectx), loc);
3092 public class SwitchSection {
3093 public readonly List<SwitchLabel> Labels;
3094 public readonly Block Block;
3096 public SwitchSection (List<SwitchLabel> labels, Block block)
3102 public SwitchSection Clone (CloneContext clonectx)
3104 var cloned_labels = new List<SwitchLabel> ();
3106 foreach (SwitchLabel sl in cloned_labels)
3107 cloned_labels.Add (sl.Clone (clonectx));
3109 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3113 public class Switch : Statement {
3114 public List<SwitchSection> Sections;
3115 public Expression Expr;
3118 /// Maps constants whose type type SwitchType to their SwitchLabels.
3120 public IDictionary<object, SwitchLabel> Elements;
3123 /// The governing switch type
3125 public TypeSpec SwitchType;
3130 Label default_target;
3132 Expression new_expr;
3135 SwitchSection constant_section;
3136 SwitchSection default_section;
3138 ExpressionStatement string_dictionary;
3139 FieldExpr switch_cache_field;
3140 static int unique_counter;
3141 ExplicitBlock block;
3144 // Nullable Types support
3146 Nullable.Unwrap unwrap;
3148 protected bool HaveUnwrap {
3149 get { return unwrap != null; }
3153 // The types allowed to be implicitly cast from
3154 // on the governing type
3156 static TypeSpec [] allowed_types;
3158 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3166 public ExplicitBlock Block {
3172 public bool GotDefault {
3174 return default_section != null;
3178 public Label DefaultTarget {
3180 return default_target;
3185 // Determines the governing type for a switch. The returned
3186 // expression might be the expression from the switch, or an
3187 // expression that includes any potential conversions to the
3188 // integral types or to string.
3190 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3192 TypeSpec t = expr.Type;
3194 if (t == TypeManager.byte_type ||
3195 t == TypeManager.sbyte_type ||
3196 t == TypeManager.ushort_type ||
3197 t == TypeManager.short_type ||
3198 t == TypeManager.uint32_type ||
3199 t == TypeManager.int32_type ||
3200 t == TypeManager.uint64_type ||
3201 t == TypeManager.int64_type ||
3202 t == TypeManager.char_type ||
3203 t == TypeManager.string_type ||
3204 t == TypeManager.bool_type ||
3205 TypeManager.IsEnumType (t))
3208 if (allowed_types == null){
3209 allowed_types = new TypeSpec [] {
3210 TypeManager.sbyte_type,
3211 TypeManager.byte_type,
3212 TypeManager.short_type,
3213 TypeManager.ushort_type,
3214 TypeManager.int32_type,
3215 TypeManager.uint32_type,
3216 TypeManager.int64_type,
3217 TypeManager.uint64_type,
3218 TypeManager.char_type,
3219 TypeManager.string_type
3224 // Try to find a *user* defined implicit conversion.
3226 // If there is no implicit conversion, or if there are multiple
3227 // conversions, we have to report an error
3229 Expression converted = null;
3230 foreach (TypeSpec tt in allowed_types){
3233 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3238 // Ignore over-worked ImplicitUserConversions that do
3239 // an implicit conversion in addition to the user conversion.
3241 if (!(e is UserCast))
3244 if (converted != null){
3245 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3255 // Performs the basic sanity checks on the switch statement
3256 // (looks for duplicate keys and non-constant expressions).
3258 // It also returns a hashtable with the keys that we will later
3259 // use to compute the switch tables
3261 bool CheckSwitch (ResolveContext ec)
3264 Elements = new Dictionary<object, SwitchLabel> ();
3266 foreach (SwitchSection ss in Sections){
3267 foreach (SwitchLabel sl in ss.Labels){
3268 if (sl.Label == null){
3269 if (default_section != null){
3270 sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3273 default_section = ss;
3277 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3282 object key = sl.Converted;
3283 if (key == SwitchLabel.NullStringCase)
3284 has_null_case = true;
3287 Elements.Add (key, sl);
3288 } catch (ArgumentException) {
3289 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3297 void EmitObjectInteger (EmitContext ec, object k)
3300 ec.EmitInt ((int) k);
3301 else if (k is Constant) {
3302 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3305 ec.EmitInt (unchecked ((int) (uint) k));
3308 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3310 ec.EmitInt ((int) (long) k);
3311 ec.Emit (OpCodes.Conv_I8);
3314 ec.EmitLong ((long) k);
3316 else if (k is ulong)
3318 ulong ul = (ulong) k;
3321 ec.EmitInt (unchecked ((int) ul));
3322 ec.Emit (OpCodes.Conv_U8);
3326 ec.EmitLong (unchecked ((long) ul));
3330 ec.EmitInt ((int) ((char) k));
3331 else if (k is sbyte)
3332 ec.EmitInt ((int) ((sbyte) k));
3334 ec.EmitInt ((int) ((byte) k));
3335 else if (k is short)
3336 ec.EmitInt ((int) ((short) k));
3337 else if (k is ushort)
3338 ec.EmitInt ((int) ((ushort) k));
3340 ec.EmitInt (((bool) k) ? 1 : 0);
3342 throw new Exception ("Unhandled case");
3345 // structure used to hold blocks of keys while calculating table switch
3346 class KeyBlock : IComparable
3348 public KeyBlock (long _first)
3350 first = last = _first;
3354 public List<object> element_keys;
3355 // how many items are in the bucket
3356 public int Size = 1;
3359 get { return (int) (last - first + 1); }
3361 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3363 return kb_last.last - kb_first.first + 1;
3365 public int CompareTo (object obj)
3367 KeyBlock kb = (KeyBlock) obj;
3368 int nLength = Length;
3369 int nLengthOther = kb.Length;
3370 if (nLengthOther == nLength)
3371 return (int) (kb.first - first);
3372 return nLength - nLengthOther;
3377 /// This method emits code for a lookup-based switch statement (non-string)
3378 /// Basically it groups the cases into blocks that are at least half full,
3379 /// and then spits out individual lookup opcodes for each block.
3380 /// It emits the longest blocks first, and short blocks are just
3381 /// handled with direct compares.
3383 /// <param name="ec"></param>
3384 /// <param name="val"></param>
3385 /// <returns></returns>
3386 void TableSwitchEmit (EmitContext ec, Expression val)
3388 int element_count = Elements.Count;
3389 object [] element_keys = new object [element_count];
3390 Elements.Keys.CopyTo (element_keys, 0);
3391 Array.Sort (element_keys);
3393 // initialize the block list with one element per key
3394 var key_blocks = new List<KeyBlock> (element_count);
3395 foreach (object key in element_keys)
3396 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3398 KeyBlock current_kb;
3399 // iteratively merge the blocks while they are at least half full
3400 // there's probably a really cool way to do this with a tree...
3401 while (key_blocks.Count > 1)
3403 var key_blocks_new = new List<KeyBlock> ();
3404 current_kb = (KeyBlock) key_blocks [0];
3405 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3407 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3408 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3411 current_kb.last = kb.last;
3412 current_kb.Size += kb.Size;
3416 // start a new block
3417 key_blocks_new.Add (current_kb);
3421 key_blocks_new.Add (current_kb);
3422 if (key_blocks.Count == key_blocks_new.Count)
3424 key_blocks = key_blocks_new;
3427 // initialize the key lists
3428 foreach (KeyBlock kb in key_blocks)
3429 kb.element_keys = new List<object> ();
3431 // fill the key lists
3433 if (key_blocks.Count > 0) {
3434 current_kb = (KeyBlock) key_blocks [0];
3435 foreach (object key in element_keys)
3437 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3438 System.Convert.ToInt64 (key) > current_kb.last;
3440 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3441 current_kb.element_keys.Add (key);
3445 // sort the blocks so we can tackle the largest ones first
3448 // okay now we can start...
3449 Label lbl_end = ec.DefineLabel (); // at the end ;-)
3450 Label lbl_default = default_target;
3452 Type type_keys = null;
3453 if (element_keys.Length > 0)
3454 type_keys = element_keys [0].GetType (); // used for conversions
3456 TypeSpec compare_type;
3458 if (TypeManager.IsEnumType (SwitchType))
3459 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3461 compare_type = SwitchType;
3463 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3465 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3466 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3469 foreach (object key in kb.element_keys) {
3470 SwitchLabel sl = (SwitchLabel) Elements [key];
3471 if (key is int && (int) key == 0) {
3472 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3475 EmitObjectInteger (ec, key);
3476 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3482 // TODO: if all the keys in the block are the same and there are
3483 // no gaps/defaults then just use a range-check.
3484 if (compare_type == TypeManager.int64_type ||
3485 compare_type == TypeManager.uint64_type)
3487 // TODO: optimize constant/I4 cases
3489 // check block range (could be > 2^31)
3491 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3492 ec.Emit (OpCodes.Blt, lbl_default);
3494 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3495 ec.Emit (OpCodes.Bgt, lbl_default);
3501 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3502 ec.Emit (OpCodes.Sub);
3504 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3510 int first = (int) kb.first;
3514 ec.Emit (OpCodes.Sub);
3518 ec.EmitInt (-first);
3519 ec.Emit (OpCodes.Add);
3523 // first, build the list of labels for the switch
3525 int cJumps = kb.Length;
3526 Label [] switch_labels = new Label [cJumps];
3527 for (int iJump = 0; iJump < cJumps; iJump++)
3529 object key = kb.element_keys [iKey];
3530 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3532 SwitchLabel sl = (SwitchLabel) Elements [key];
3533 switch_labels [iJump] = sl.GetILLabel (ec);
3537 switch_labels [iJump] = lbl_default;
3539 // emit the switch opcode
3540 ec.Emit (OpCodes.Switch, switch_labels);
3543 // mark the default for this block
3545 ec.MarkLabel (lbl_default);
3548 // TODO: find the default case and emit it here,
3549 // to prevent having to do the following jump.
3550 // make sure to mark other labels in the default section
3552 // the last default just goes to the end
3553 if (element_keys.Length > 0)
3554 ec.Emit (OpCodes.Br, lbl_default);
3556 // now emit the code for the sections
3557 bool found_default = false;
3559 foreach (SwitchSection ss in Sections) {
3560 foreach (SwitchLabel sl in ss.Labels) {
3561 if (sl.Converted == SwitchLabel.NullStringCase) {
3562 ec.MarkLabel (null_target);
3563 } else if (sl.Label == null) {
3564 ec.MarkLabel (lbl_default);
3565 found_default = true;
3567 ec.MarkLabel (null_target);
3569 ec.MarkLabel (sl.GetILLabel (ec));
3570 ec.MarkLabel (sl.GetILLabelCode (ec));
3575 if (!found_default) {
3576 ec.MarkLabel (lbl_default);
3577 if (!has_null_case) {
3578 ec.MarkLabel (null_target);
3582 ec.MarkLabel (lbl_end);
3585 SwitchSection FindSection (SwitchLabel label)
3587 foreach (SwitchSection ss in Sections){
3588 foreach (SwitchLabel sl in ss.Labels){
3597 public static void Reset ()
3600 allowed_types = null;
3603 public override bool Resolve (BlockContext ec)
3605 Expr = Expr.Resolve (ec);
3609 new_expr = SwitchGoverningType (ec, Expr);
3611 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3612 unwrap = Nullable.Unwrap.Create (Expr, false);
3616 new_expr = SwitchGoverningType (ec, unwrap);
3619 if (new_expr == null){
3620 ec.Report.Error (151, loc,
3621 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3622 TypeManager.CSharpName (Expr.Type));
3627 SwitchType = new_expr.Type;
3629 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3630 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3634 if (!CheckSwitch (ec))
3638 Elements.Remove (SwitchLabel.NullStringCase);
3640 Switch old_switch = ec.Switch;
3642 ec.Switch.SwitchType = SwitchType;
3644 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3645 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3647 var constant = new_expr as Constant;
3648 if (constant != null) {
3650 object key = constant.GetValue ();
3652 if (Elements.TryGetValue (key, out label))
3653 constant_section = FindSection (label);
3655 if (constant_section == null)
3656 constant_section = default_section;
3661 foreach (SwitchSection ss in Sections){
3663 ec.CurrentBranching.CreateSibling (
3664 null, FlowBranching.SiblingType.SwitchSection);
3668 if (is_constant && (ss != constant_section)) {
3669 // If we're a constant switch, we're only emitting
3670 // one single section - mark all the others as
3672 ec.CurrentBranching.CurrentUsageVector.Goto ();
3673 if (!ss.Block.ResolveUnreachable (ec, true)) {
3677 if (!ss.Block.Resolve (ec))
3682 if (default_section == null)
3683 ec.CurrentBranching.CreateSibling (
3684 null, FlowBranching.SiblingType.SwitchSection);
3686 ec.EndFlowBranching ();
3687 ec.Switch = old_switch;
3689 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3694 if (SwitchType == TypeManager.string_type && !is_constant) {
3695 // TODO: Optimize single case, and single+default case
3696 ResolveStringSwitchMap (ec);
3702 void ResolveStringSwitchMap (ResolveContext ec)
3704 FullNamedExpression string_dictionary_type;
3705 if (TypeManager.generic_ienumerable_type != null) {
3706 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3707 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3709 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3711 new TypeExpression (TypeManager.string_type, loc),
3712 new TypeExpression (TypeManager.int32_type, loc)), loc);
3714 MemberAccess system_collections_generic = new MemberAccess (
3715 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3717 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3720 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3721 Field field = new Field (ctype, string_dictionary_type,
3722 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3723 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3724 if (!field.Define ())
3726 ctype.AddField (field);
3728 var init = new List<Expression> ();
3731 string value = null;
3732 foreach (SwitchSection section in Sections) {
3733 int last_count = init.Count;
3734 foreach (SwitchLabel sl in section.Labels) {
3735 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3738 value = (string) sl.Converted;
3739 var init_args = new List<Expression> (2);
3740 init_args.Add (new StringLiteral (value, sl.Location));
3741 init_args.Add (new IntConstant (counter, loc));
3742 init.Add (new CollectionElementInitializer (init_args, loc));
3746 // Don't add empty sections
3748 if (last_count == init.Count)
3751 Elements.Add (counter, section.Labels [0]);
3755 Arguments args = new Arguments (1);
3756 args.Add (new Argument (new IntConstant (init.Count, loc)));
3757 Expression initializer = new NewInitialize (string_dictionary_type, args,
3758 new CollectionOrObjectInitializers (init, loc), loc);
3760 switch_cache_field = new FieldExpr (field, loc);
3761 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3764 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3766 Label l_initialized = ec.DefineLabel ();
3769 // Skip initialization when value is null
3771 value.EmitBranchable (ec, null_target, false);
3774 // Check if string dictionary is initialized and initialize
3776 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3777 string_dictionary.EmitStatement (ec);
3778 ec.MarkLabel (l_initialized);
3780 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3782 ResolveContext rc = new ResolveContext (ec.MemberContext);
3784 if (TypeManager.generic_ienumerable_type != null) {
3785 Arguments get_value_args = new Arguments (2);
3786 get_value_args.Add (new Argument (value));
3787 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3788 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3789 if (get_item == null)
3793 // A value was not found, go to default case
3795 get_item.EmitBranchable (ec, default_target, false);
3797 Arguments get_value_args = new Arguments (1);
3798 get_value_args.Add (new Argument (value));
3800 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3801 if (get_item == null)
3804 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3805 get_item_object.EmitAssign (ec, get_item, true, false);
3806 ec.Emit (OpCodes.Brfalse, default_target);
3808 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3809 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3811 get_item_int.EmitStatement (ec);
3812 get_item_object.Release (ec);
3815 TableSwitchEmit (ec, string_switch_variable);
3816 string_switch_variable.Release (ec);
3819 protected override void DoEmit (EmitContext ec)
3822 // Needed to emit anonymous storey initialization
3823 // Otherwise it does not contain any statements for now
3827 default_target = ec.DefineLabel ();
3828 null_target = ec.DefineLabel ();
3830 // Store variable for comparission purposes
3831 // TODO: Don't duplicate non-captured VariableReference
3832 LocalTemporary value;
3834 value = new LocalTemporary (SwitchType);
3835 unwrap.EmitCheck (ec);
3836 ec.Emit (OpCodes.Brfalse, null_target);
3839 } else if (!is_constant) {
3840 value = new LocalTemporary (SwitchType);
3847 // Setup the codegen context
3849 Label old_end = ec.LoopEnd;
3850 Switch old_switch = ec.Switch;
3852 ec.LoopEnd = ec.DefineLabel ();
3857 if (constant_section != null)
3858 constant_section.Block.Emit (ec);
3859 } else if (string_dictionary != null) {
3860 DoEmitStringSwitch (value, ec);
3862 TableSwitchEmit (ec, value);
3868 // Restore context state.
3869 ec.MarkLabel (ec.LoopEnd);
3872 // Restore the previous context
3874 ec.LoopEnd = old_end;
3875 ec.Switch = old_switch;
3878 protected override void CloneTo (CloneContext clonectx, Statement t)
3880 Switch target = (Switch) t;
3882 target.Expr = Expr.Clone (clonectx);
3883 target.Sections = new List<SwitchSection> ();
3884 foreach (SwitchSection ss in Sections){
3885 target.Sections.Add (ss.Clone (clonectx));
3890 // A place where execution can restart in an iterator
3891 public abstract class ResumableStatement : Statement
3894 protected Label resume_point;
3896 public Label PrepareForEmit (EmitContext ec)
3900 resume_point = ec.DefineLabel ();
3902 return resume_point;
3905 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3909 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3914 // Base class for statements that are implemented in terms of try...finally
3915 public abstract class ExceptionStatement : ResumableStatement
3919 List<ResumableStatement> resume_points;
3920 int first_resume_pc;
3921 protected Statement stmt;
3922 Label dispose_try_block;
3923 bool prepared_for_dispose, emitted_dispose;
3925 protected ExceptionStatement (Statement stmt, Location loc)
3933 public Statement Statement {
3941 protected abstract void EmitPreTryBody (EmitContext ec);
3942 protected abstract void EmitTryBody (EmitContext ec);
3943 protected abstract void EmitFinallyBody (EmitContext ec);
3945 protected sealed override void DoEmit (EmitContext ec)
3947 EmitPreTryBody (ec);
3949 if (resume_points != null) {
3950 ec.EmitInt ((int) Iterator.State.Running);
3951 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3954 ec.BeginExceptionBlock ();
3956 if (resume_points != null) {
3957 ec.MarkLabel (resume_point);
3959 // For normal control flow, we want to fall-through the Switch
3960 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3961 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3962 ec.EmitInt (first_resume_pc);
3963 ec.Emit (OpCodes.Sub);
3965 Label [] labels = new Label [resume_points.Count];
3966 for (int i = 0; i < resume_points.Count; ++i)
3967 labels [i] = resume_points [i].PrepareForEmit (ec);
3968 ec.Emit (OpCodes.Switch, labels);
3973 ec.BeginFinallyBlock ();
3975 Label start_finally = ec.DefineLabel ();
3976 if (resume_points != null) {
3977 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3978 ec.Emit (OpCodes.Brfalse_S, start_finally);
3979 ec.Emit (OpCodes.Endfinally);
3982 ec.MarkLabel (start_finally);
3983 EmitFinallyBody (ec);
3985 ec.EndExceptionBlock ();
3988 public void SomeCodeFollows ()
3990 code_follows = true;
3993 public override bool Resolve (BlockContext ec)
3995 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3996 // So, ensure there's some IL code after this statement.
3997 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3998 ec.NeedReturnLabel ();
4000 iter = ec.CurrentIterator;
4004 public void AddResumePoint (ResumableStatement stmt, int pc)
4006 if (resume_points == null) {
4007 resume_points = new List<ResumableStatement> ();
4008 first_resume_pc = pc;
4011 if (pc != first_resume_pc + resume_points.Count)
4012 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4014 resume_points.Add (stmt);
4017 public override Label PrepareForDispose (EmitContext ec, Label end)
4019 if (!prepared_for_dispose) {
4020 prepared_for_dispose = true;
4021 dispose_try_block = ec.DefineLabel ();
4023 return dispose_try_block;
4026 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4028 if (emitted_dispose)
4031 emitted_dispose = true;
4033 Label end_of_try = ec.DefineLabel ();
4035 // Ensure that the only way we can get into this code is through a dispatcher
4036 if (have_dispatcher)
4037 ec.Emit (OpCodes.Br, end);
4039 ec.BeginExceptionBlock ();
4041 ec.MarkLabel (dispose_try_block);
4043 Label [] labels = null;
4044 for (int i = 0; i < resume_points.Count; ++i) {
4045 ResumableStatement s = (ResumableStatement) resume_points [i];
4046 Label ret = s.PrepareForDispose (ec, end_of_try);
4047 if (ret.Equals (end_of_try) && labels == null)
4049 if (labels == null) {
4050 labels = new Label [resume_points.Count];
4051 for (int j = 0; j < i; ++j)
4052 labels [j] = end_of_try;
4057 if (labels != null) {
4059 for (j = 1; j < labels.Length; ++j)
4060 if (!labels [0].Equals (labels [j]))
4062 bool emit_dispatcher = j < labels.Length;
4064 if (emit_dispatcher) {
4065 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4066 ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4067 ec.EmitInt (first_resume_pc);
4068 ec.Emit (OpCodes.Sub);
4069 ec.Emit (OpCodes.Switch, labels);
4070 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4073 foreach (ResumableStatement s in resume_points)
4074 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4077 ec.MarkLabel (end_of_try);
4079 ec.BeginFinallyBlock ();
4081 EmitFinallyBody (ec);
4083 ec.EndExceptionBlock ();
4087 public class Lock : ExceptionStatement {
4089 TemporaryVariableReference expr_copy;
4090 TemporaryVariableReference lock_taken;
4092 public Lock (Expression expr, Statement stmt, Location loc)
4098 public override bool Resolve (BlockContext ec)
4100 expr = expr.Resolve (ec);
4104 if (!TypeManager.IsReferenceType (expr.Type)){
4105 ec.Report.Error (185, loc,
4106 "`{0}' is not a reference type as required by the lock statement",
4107 expr.Type.GetSignatureForError ());
4110 if (expr.Type.IsGenericParameter) {
4111 expr = Convert.ImplicitTypeParameterConversion (expr, TypeManager.object_type);
4114 VariableReference lv = expr as VariableReference;
4117 locked = lv.IsLockedByStatement;
4118 lv.IsLockedByStatement = true;
4124 ec.StartFlowBranching (this);
4125 Statement.Resolve (ec);
4126 ec.EndFlowBranching ();
4129 lv.IsLockedByStatement = locked;
4135 // Have to keep original lock value around to unlock same location
4136 // in the case the original has changed or is null
4138 expr_copy = TemporaryVariableReference.Create (TypeManager.object_type, ec.CurrentBlock.Parent, loc);
4139 expr_copy.Resolve (ec);
4142 // Ensure Monitor methods are available
4144 if (ResolvePredefinedMethods (ec) > 1) {
4145 lock_taken = TemporaryVariableReference.Create (TypeManager.bool_type, ec.CurrentBlock.Parent, loc);
4146 lock_taken.Resolve (ec);
4152 protected override void EmitPreTryBody (EmitContext ec)
4154 expr_copy.EmitAssign (ec, expr);
4156 if (lock_taken != null) {
4158 // Initialize ref variable
4160 lock_taken.EmitAssign (ec, new BoolLiteral (false, loc));
4163 // Monitor.Enter (expr_copy)
4165 expr_copy.Emit (ec);
4166 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4170 protected override void EmitTryBody (EmitContext ec)
4173 // Monitor.Enter (expr_copy, ref lock_taken)
4175 if (lock_taken != null) {
4176 expr_copy.Emit (ec);
4177 lock_taken.LocalInfo.CreateBuilder (ec);
4178 lock_taken.AddressOf (ec, AddressOp.Load);
4179 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4182 Statement.Emit (ec);
4185 protected override void EmitFinallyBody (EmitContext ec)
4188 // if (lock_taken) Monitor.Exit (expr_copy)
4190 Label skip = ec.DefineLabel ();
4192 if (lock_taken != null) {
4193 lock_taken.Emit (ec);
4194 ec.Emit (OpCodes.Brfalse_S, skip);
4197 expr_copy.Emit (ec);
4198 ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4199 ec.MarkLabel (skip);
4202 int ResolvePredefinedMethods (ResolveContext rc)
4204 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4205 TypeSpec monitor_type = TypeManager.CoreLookupType (rc.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4207 if (monitor_type == null)
4210 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4211 var filter = MemberFilter.Method ("Enter", 0, new ParametersImported (
4213 new ParameterData (null, Parameter.Modifier.NONE),
4214 new ParameterData (null, Parameter.Modifier.REF)
4217 TypeManager.object_type,
4218 TypeManager.bool_type
4221 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (monitor_type, filter, true, loc);
4222 if (TypeManager.void_monitor_enter_object == null) {
4223 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4224 monitor_type, "Enter", loc, TypeManager.object_type);
4227 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4228 monitor_type, "Exit", loc, TypeManager.object_type);
4231 return TypeManager.void_monitor_enter_object.Parameters.Count;
4234 protected override void CloneTo (CloneContext clonectx, Statement t)
4236 Lock target = (Lock) t;
4238 target.expr = expr.Clone (clonectx);
4239 target.stmt = Statement.Clone (clonectx);
4243 public class Unchecked : Statement {
4246 public Unchecked (Block b, Location loc)
4253 public override bool Resolve (BlockContext ec)
4255 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4256 return Block.Resolve (ec);
4259 protected override void DoEmit (EmitContext ec)
4261 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4265 protected override void CloneTo (CloneContext clonectx, Statement t)
4267 Unchecked target = (Unchecked) t;
4269 target.Block = clonectx.LookupBlock (Block);
4273 public class Checked : Statement {
4276 public Checked (Block b, Location loc)
4279 b.Unchecked = false;
4283 public override bool Resolve (BlockContext ec)
4285 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4286 return Block.Resolve (ec);
4289 protected override void DoEmit (EmitContext ec)
4291 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4295 protected override void CloneTo (CloneContext clonectx, Statement t)
4297 Checked target = (Checked) t;
4299 target.Block = clonectx.LookupBlock (Block);
4303 public class Unsafe : Statement {
4306 public Unsafe (Block b, Location loc)
4309 Block.Unsafe = true;
4313 public override bool Resolve (BlockContext ec)
4315 if (ec.CurrentIterator != null)
4316 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4318 using (ec.Set (ResolveContext.Options.UnsafeScope))
4319 return Block.Resolve (ec);
4322 protected override void DoEmit (EmitContext ec)
4327 protected override void CloneTo (CloneContext clonectx, Statement t)
4329 Unsafe target = (Unsafe) t;
4331 target.Block = clonectx.LookupBlock (Block);
4338 public class Fixed : Statement
4340 abstract class Emitter : ShimExpression
4342 protected LocalVariable vi;
4344 protected Emitter (Expression expr, LocalVariable li)
4350 public abstract void EmitExit (EmitContext ec);
4353 class ExpressionEmitter : Emitter {
4354 public ExpressionEmitter (Expression converted, LocalVariable li) :
4355 base (converted, li)
4359 protected override Expression DoResolve (ResolveContext rc)
4361 throw new NotImplementedException ();
4364 public override void Emit (EmitContext ec) {
4366 // Store pointer in pinned location
4372 public override void EmitExit (EmitContext ec)
4374 ec.Emit (OpCodes.Ldc_I4_0);
4375 ec.Emit (OpCodes.Conv_U);
4380 class StringEmitter : Emitter
4382 LocalVariable pinned_string;
4384 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4389 protected override Expression DoResolve (ResolveContext rc)
4391 pinned_string = new LocalVariable (vi.Block, "$pinned",
4392 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4395 pinned_string.Type = TypeManager.string_type;
4397 if (TypeManager.int_get_offset_to_string_data == null) {
4398 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4399 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4402 eclass = ExprClass.Variable;
4403 type = TypeManager.int32_type;
4407 public override void Emit (EmitContext ec)
4409 pinned_string.CreateBuilder (ec);
4412 pinned_string.EmitAssign (ec);
4414 // TODO: Should use Binary::Add
4415 pinned_string.Emit (ec);
4416 ec.Emit (OpCodes.Conv_I);
4418 PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4419 //pe.InstanceExpression = pinned_string;
4420 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4422 ec.Emit (OpCodes.Add);
4426 public override void EmitExit (EmitContext ec)
4428 ec.Emit (OpCodes.Ldnull);
4429 pinned_string.EmitAssign (ec);
4433 public class VariableDeclaration : BlockVariableDeclaration
4435 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4440 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4442 if (!Variable.Type.IsPointer && li == Variable) {
4443 bc.Report.Error (209, TypeExpression.Location,
4444 "The type of locals declared in a fixed statement must be a pointer type");
4449 // The rules for the possible declarators are pretty wise,
4450 // but the production on the grammar is more concise.
4452 // So we have to enforce these rules here.
4454 // We do not resolve before doing the case 1 test,
4455 // because the grammar is explicit in that the token &
4456 // is present, so we need to test for this particular case.
4459 if (initializer is Cast) {
4460 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4464 initializer = initializer.Resolve (bc);
4466 if (initializer == null)
4472 if (initializer.Type.IsArray) {
4473 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4476 // Provided that array_type is unmanaged,
4478 if (!TypeManager.VerifyUnmanaged (bc.Compiler, array_type, loc))
4482 // and T* is implicitly convertible to the
4483 // pointer type given in the fixed statement.
4485 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4487 Expression converted = Convert.ImplicitConversionRequired (
4488 bc, array_ptr, li.Type, loc);
4489 if (converted == null)
4493 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4495 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4496 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4497 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (0, loc), loc), loc)),
4498 new NullPointer (loc),
4501 converted = converted.Resolve (bc);
4503 return new ExpressionEmitter (converted, li);
4509 if (initializer.Type == TypeManager.string_type) {
4510 return new StringEmitter (initializer, li, loc).Resolve (bc);
4513 // Case 3: fixed buffer
4514 if (initializer is FixedBufferPtr) {
4515 return new ExpressionEmitter (initializer, li);
4519 // Case 4: & object.
4521 bool already_fixed = true;
4522 Unary u = initializer as Unary;
4523 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4524 IVariableReference vr = u.Expr as IVariableReference;
4525 if (vr == null || !vr.IsFixed) {
4526 already_fixed = false;
4530 if (already_fixed) {
4531 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4534 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4535 return new ExpressionEmitter (initializer, li);
4540 VariableDeclaration decl;
4541 Statement statement;
4544 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4553 public Statement Statement {
4559 public BlockVariableDeclaration Variables {
4567 public override bool Resolve (BlockContext ec)
4569 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4570 if (!decl.Resolve (ec))
4574 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4575 bool ok = statement.Resolve (ec);
4576 bool flow_unreachable = ec.EndFlowBranching ();
4577 has_ret = flow_unreachable;
4582 protected override void DoEmit (EmitContext ec)
4584 decl.Variable.CreateBuilder (ec);
4585 decl.Initializer.Emit (ec);
4586 if (decl.Declarators != null) {
4587 foreach (var d in decl.Declarators) {
4588 d.Variable.CreateBuilder (ec);
4589 d.Initializer.Emit (ec);
4593 statement.Emit (ec);
4599 // Clear the pinned variable
4601 ((Emitter) decl.Initializer).EmitExit (ec);
4602 if (decl.Declarators != null) {
4603 foreach (var d in decl.Declarators) {
4604 ((Emitter)d.Initializer).EmitExit (ec);
4609 protected override void CloneTo (CloneContext clonectx, Statement t)
4611 Fixed target = (Fixed) t;
4613 target.decl = (VariableDeclaration) decl.Clone (clonectx);
4614 target.statement = statement.Clone (clonectx);
4618 public class Catch : Statement
4622 FullNamedExpression type_expr;
4623 CompilerAssign assign;
4626 public Catch (Block block, Location loc)
4634 public Block Block {
4640 public TypeSpec CatchType {
4646 public bool IsGeneral {
4648 return type_expr == null;
4652 public FullNamedExpression TypeExpression {
4661 public LocalVariable Variable {
4672 protected override void DoEmit (EmitContext ec)
4675 ec.BeginCatchBlock (TypeManager.object_type);
4677 ec.BeginCatchBlock (CatchType);
4680 li.CreateBuilder (ec);
4683 // Special case hoisted catch variable, we have to use a temporary variable
4684 // to pass via anonymous storey initialization with the value still on top
4687 if (li.HoistedVariant != null) {
4688 LocalTemporary lt = new LocalTemporary (li.Type);
4689 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4691 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4693 // switch to assigning from the temporary variable and not from top of the stack
4694 assign.UpdateSource (lt);
4697 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4698 ec.Emit (OpCodes.Pop);
4699 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4705 public override bool Resolve (BlockContext ec)
4707 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4708 if (type_expr != null) {
4709 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4714 if (type != TypeManager.exception_type && !TypeSpec.IsBaseClass (type, TypeManager.exception_type, false)) {
4715 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4716 } else if (li != null) {
4718 li.PrepareForFlowAnalysis (ec);
4720 // source variable is at the top of the stack
4721 Expression source = new EmptyExpression (li.Type);
4722 if (li.Type.IsGenericParameter)
4723 source = new UnboxCast (source, li.Type);
4725 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
4726 Block.AddScopeStatement (new StatementExpression (assign));
4730 return Block.Resolve (ec);
4734 protected override void CloneTo (CloneContext clonectx, Statement t)
4736 Catch target = (Catch) t;
4738 if (type_expr != null)
4739 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
4741 target.block = clonectx.LookupBlock (block);
4745 public class TryFinally : ExceptionStatement {
4748 public TryFinally (Statement stmt, Block fini, Location loc)
4754 public override bool Resolve (BlockContext ec)
4758 ec.StartFlowBranching (this);
4760 if (!stmt.Resolve (ec))
4764 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4765 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4766 if (!fini.Resolve (ec))
4770 ec.EndFlowBranching ();
4772 ok &= base.Resolve (ec);
4777 protected override void EmitPreTryBody (EmitContext ec)
4781 protected override void EmitTryBody (EmitContext ec)
4786 protected override void EmitFinallyBody (EmitContext ec)
4791 protected override void CloneTo (CloneContext clonectx, Statement t)
4793 TryFinally target = (TryFinally) t;
4795 target.stmt = (Statement) stmt.Clone (clonectx);
4797 target.fini = clonectx.LookupBlock (fini);
4801 public class TryCatch : Statement {
4803 public List<Catch> Specific;
4804 public Catch General;
4805 bool inside_try_finally, code_follows;
4807 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4810 this.Specific = catch_clauses;
4811 this.inside_try_finally = inside_try_finally;
4813 Catch c = catch_clauses [0];
4816 catch_clauses.RemoveAt (0);
4822 public override bool Resolve (BlockContext ec)
4826 ec.StartFlowBranching (this);
4828 if (!Block.Resolve (ec))
4831 TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4833 foreach (Catch c in Specific){
4834 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4836 if (!c.Resolve (ec)) {
4841 TypeSpec resolved_type = c.CatchType;
4842 for (int ii = 0; ii < last_index; ++ii) {
4843 if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4844 ec.Report.Error (160, c.loc,
4845 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4846 TypeManager.CSharpName (prev_catches [ii]));
4851 prev_catches [last_index++] = resolved_type;
4854 if (General != null) {
4855 foreach (Catch c in Specific) {
4856 if (c.CatchType != TypeManager.exception_type)
4859 if (!ec.CurrentMemberDefinition.Module.DeclaringAssembly.WrapNonExceptionThrows)
4862 if (!ec.Compiler.PredefinedAttributes.RuntimeCompatibility.IsDefined)
4865 ec.Report.Warning (1058, 1, c.loc,
4866 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4869 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4871 if (!General.Resolve (ec))
4875 ec.EndFlowBranching ();
4877 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4878 // So, ensure there's some IL code after this statement
4879 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4880 ec.NeedReturnLabel ();
4885 public void SomeCodeFollows ()
4887 code_follows = true;
4890 protected override void DoEmit (EmitContext ec)
4892 if (!inside_try_finally)
4893 ec.BeginExceptionBlock ();
4897 foreach (Catch c in Specific)
4900 if (General != null)
4903 if (!inside_try_finally)
4904 ec.EndExceptionBlock ();
4907 protected override void CloneTo (CloneContext clonectx, Statement t)
4909 TryCatch target = (TryCatch) t;
4911 target.Block = clonectx.LookupBlock (Block);
4912 if (General != null)
4913 target.General = (Catch) General.Clone (clonectx);
4914 if (Specific != null){
4915 target.Specific = new List<Catch> ();
4916 foreach (Catch c in Specific)
4917 target.Specific.Add ((Catch) c.Clone (clonectx));
4922 public class Using : ExceptionStatement
4924 public class VariableDeclaration : BlockVariableDeclaration
4926 Statement dispose_call;
4928 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4933 public VariableDeclaration (LocalVariable li, Location loc)
4939 public VariableDeclaration (Expression expr)
4942 loc = expr.Location;
4948 public bool IsNested { get; private set; }
4952 public void EmitDispose (EmitContext ec)
4954 dispose_call.Emit (ec);
4957 public override bool Resolve (BlockContext bc)
4962 return base.Resolve (bc);
4965 public Expression ResolveExpression (BlockContext bc)
4967 var e = Initializer.Resolve (bc);
4971 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
4972 Initializer = ResolveInitializer (bc, Variable, e);
4976 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4978 if (li.Type == InternalType.Dynamic) {
4979 initializer = initializer.Resolve (bc);
4980 if (initializer == null)
4983 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
4984 Arguments args = new Arguments (1);
4985 args.Add (new Argument (initializer));
4986 initializer = new DynamicConversion (TypeManager.idisposable_type, 0, args, initializer.Location).Resolve (bc);
4987 if (initializer == null)
4990 var var = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
4991 dispose_call = CreateDisposeCall (bc, var);
4992 dispose_call.Resolve (bc);
4994 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
4997 if (li == Variable) {
4998 CheckIDiposableConversion (bc, li, initializer);
4999 dispose_call = CreateDisposeCall (bc, li);
5000 dispose_call.Resolve (bc);
5003 return base.ResolveInitializer (bc, li, initializer);
5006 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5010 if (type != TypeManager.idisposable_type && !type.ImplementsInterface (TypeManager.idisposable_type, false)) {
5011 if (TypeManager.IsNullableType (type)) {
5012 // it's handled in CreateDisposeCall
5016 bc.Report.SymbolRelatedToPreviousError (type);
5017 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5018 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5019 type.GetSignatureForError ());
5025 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5027 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5029 var loc = lv.Location;
5031 if (TypeManager.void_dispose_void == null) {
5032 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5033 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5036 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5037 dispose_mg.InstanceExpression = TypeManager.IsNullableType (type) ?
5038 new Cast (new TypeExpression (TypeManager.idisposable_type, loc), lvr, loc).Resolve (bc) :
5041 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5043 // Add conditional call when disposing possible null variable
5044 if (!type.IsStruct || TypeManager.IsNullableType (type))
5045 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
5050 public Statement RewriteForDeclarators (BlockContext bc, Statement stmt)
5052 for (int i = declarators.Count - 1; i >= 0; --i) {
5053 var d = declarators [i];
5054 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5055 vd.Initializer = d.Initializer;
5057 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5058 vd.dispose_call.Resolve (bc);
5060 stmt = new Using (vd, stmt, d.Variable.Location);
5068 VariableDeclaration decl;
5070 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5076 public Using (Expression expr, Statement stmt, Location loc)
5079 this.decl = new VariableDeclaration (expr);
5084 public Expression Expression {
5086 return decl.Variable == null ? decl.Initializer : null;
5090 public BlockVariableDeclaration Variables {
5098 protected override void EmitPreTryBody (EmitContext ec)
5103 protected override void EmitTryBody (EmitContext ec)
5108 protected override void EmitFinallyBody (EmitContext ec)
5110 decl.EmitDispose (ec);
5113 public override bool Resolve (BlockContext ec)
5115 VariableReference vr;
5116 bool vr_locked = false;
5118 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5119 if (decl.Variable == null) {
5120 vr = decl.ResolveExpression (ec) as VariableReference;
5122 vr_locked = vr.IsLockedByStatement;
5123 vr.IsLockedByStatement = true;
5126 if (!decl.Resolve (ec))
5129 if (decl.Declarators != null) {
5130 stmt = decl.RewriteForDeclarators (ec, stmt);
5137 ec.StartFlowBranching (this);
5141 ec.EndFlowBranching ();
5144 vr.IsLockedByStatement = vr_locked;
5151 protected override void CloneTo (CloneContext clonectx, Statement t)
5153 Using target = (Using) t;
5155 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5156 target.stmt = stmt.Clone (clonectx);
5161 /// Implementation of the foreach C# statement
5163 public class Foreach : Statement {
5165 sealed class ArrayForeach : Statement
5167 readonly Foreach for_each;
5168 readonly Statement statement;
5171 TemporaryVariableReference[] lengths;
5172 Expression [] length_exprs;
5173 StatementExpression[] counter;
5174 TemporaryVariableReference[] variables;
5176 TemporaryVariableReference copy;
5178 LocalVariableReference variable;
5180 public ArrayForeach (Foreach @foreach, int rank)
5182 for_each = @foreach;
5183 statement = for_each.statement;
5185 variable = new LocalVariableReference (for_each.variable, loc);
5187 counter = new StatementExpression[rank];
5188 variables = new TemporaryVariableReference[rank];
5189 length_exprs = new Expression [rank];
5192 // Only use temporary length variables when dealing with
5193 // multi-dimensional arrays
5196 lengths = new TemporaryVariableReference [rank];
5199 protected override void CloneTo (CloneContext clonectx, Statement target)
5201 throw new NotImplementedException ();
5204 public override bool Resolve (BlockContext ec)
5206 Block variables_block = variable.local_info.Block;
5207 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5210 int rank = length_exprs.Length;
5211 Arguments list = new Arguments (rank);
5212 for (int i = 0; i < rank; i++) {
5213 var v = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5215 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5216 counter[i].Resolve (ec);
5219 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5221 lengths[i] = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5222 lengths[i].Resolve (ec);
5224 Arguments args = new Arguments (1);
5225 args.Add (new Argument (new IntConstant (i, loc)));
5226 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5229 list.Add (new Argument (v));
5232 access = new ElementAccess (copy, list, loc).Resolve (ec);
5236 Expression var_type = for_each.type;
5237 VarExpr ve = var_type as VarExpr;
5239 // Infer implicitly typed local variable from foreach array type
5240 var_type = new TypeExpression (access.Type, ve.Location);
5243 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5244 if (var_type == null)
5247 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5253 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5254 ec.CurrentBranching.CreateSibling ();
5256 variable.local_info.Type = conv.Type;
5257 variable.Resolve (ec);
5259 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5260 if (!statement.Resolve (ec))
5262 ec.EndFlowBranching ();
5264 // There's no direct control flow from the end of the embedded statement to the end of the loop
5265 ec.CurrentBranching.CurrentUsageVector.Goto ();
5267 ec.EndFlowBranching ();
5272 protected override void DoEmit (EmitContext ec)
5274 copy.EmitAssign (ec, for_each.expr);
5276 int rank = length_exprs.Length;
5277 Label[] test = new Label [rank];
5278 Label[] loop = new Label [rank];
5280 for (int i = 0; i < rank; i++) {
5281 test [i] = ec.DefineLabel ();
5282 loop [i] = ec.DefineLabel ();
5284 if (lengths != null)
5285 lengths [i].EmitAssign (ec, length_exprs [i]);
5288 IntConstant zero = new IntConstant (0, loc);
5289 for (int i = 0; i < rank; i++) {
5290 variables [i].EmitAssign (ec, zero);
5292 ec.Emit (OpCodes.Br, test [i]);
5293 ec.MarkLabel (loop [i]);
5296 variable.local_info.CreateBuilder (ec);
5297 variable.EmitAssign (ec, conv, false, false);
5299 statement.Emit (ec);
5301 ec.MarkLabel (ec.LoopBegin);
5303 for (int i = rank - 1; i >= 0; i--){
5304 counter [i].Emit (ec);
5306 ec.MarkLabel (test [i]);
5307 variables [i].Emit (ec);
5309 if (lengths != null)
5310 lengths [i].Emit (ec);
5312 length_exprs [i].Emit (ec);
5314 ec.Emit (OpCodes.Blt, loop [i]);
5317 ec.MarkLabel (ec.LoopEnd);
5321 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5323 class Body : Statement
5326 LocalVariableReference variable;
5327 Expression current, conv;
5328 Statement statement;
5330 public Body (TypeSpec type, LocalVariable variable,
5331 Expression current, Statement statement,
5335 this.variable = new LocalVariableReference (variable, loc);
5336 this.current = current;
5337 this.statement = statement;
5341 protected override void CloneTo (CloneContext clonectx, Statement target)
5343 throw new NotImplementedException ();
5346 public override bool Resolve (BlockContext ec)
5348 current = current.Resolve (ec);
5349 if (current == null)
5352 conv = Convert.ExplicitConversion (ec, current, type, loc);
5356 variable.local_info.Type = conv.Type;
5357 variable.Resolve (ec);
5359 if (!statement.Resolve (ec))
5365 protected override void DoEmit (EmitContext ec)
5367 variable.local_info.CreateBuilder (ec);
5368 variable.EmitAssign (ec, conv, false, false);
5370 statement.Emit (ec);
5374 class RuntimeDispose : Using.VariableDeclaration
5376 public RuntimeDispose (LocalVariable lv, Location loc)
5381 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5383 // Defered to runtime check
5386 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5388 if (TypeManager.void_dispose_void == null) {
5389 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5390 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5394 // Fabricates code like
5396 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5399 var dispose_variable = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
5401 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5402 dispose_variable.CreateReferenceExpression (bc, loc),
5403 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5404 loc), new NullLiteral (loc), loc);
5406 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5407 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5409 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5410 return new If (idisaposable_test, dispose, loc);
5414 LocalVariable variable;
5416 Statement statement;
5417 Expression var_type;
5418 ExpressionStatement init;
5419 TemporaryVariableReference enumerator_variable;
5420 bool ambiguous_getenumerator_name;
5422 public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5424 this.var_type = var_type;
5425 this.variable = var;
5431 protected override void CloneTo (CloneContext clonectx, Statement target)
5433 throw new NotImplementedException ();
5436 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5438 rc.Report.SymbolRelatedToPreviousError (enumerator);
5439 rc.Report.Error (202, loc,
5440 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5441 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5444 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5447 // Option 1: Try to match by name GetEnumerator first
5449 var mexpr = Expression.MemberLookup (rc, rc.CurrentType, expr.Type,
5450 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5452 var mg = mexpr as MethodGroupExpr;
5454 mg.InstanceExpression = expr;
5455 Arguments args = new Arguments (0);
5456 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5458 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5459 if (ambiguous_getenumerator_name)
5462 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5468 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5470 TypeSpec iface_candidate = null;
5471 for (TypeSpec t = expr.Type; t != null && t != TypeManager.object_type; t = t.BaseType) {
5472 var ifaces = t.Interfaces;
5473 if (ifaces != null) {
5474 foreach (var iface in ifaces) {
5475 if (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) {
5476 if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) {
5477 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5478 rc.Report.Error (1640, loc,
5479 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5480 expr.Type.GetSignatureForError (), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5485 iface_candidate = iface;
5489 if (iface == TypeManager.ienumerable_type && iface_candidate == null) {
5490 iface_candidate = iface;
5496 if (iface_candidate == null) {
5497 rc.Report.Error (1579, loc,
5498 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5499 expr.Type.GetSignatureForError (), "GetEnumerator");
5504 var method = TypeManager.GetPredefinedMethod (iface_candidate,
5505 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null), loc);
5510 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5511 mg.InstanceExpression = expr;
5515 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5517 var ms = MemberCache.FindMember (enumerator.ReturnType,
5518 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5519 BindingRestriction.InstanceOnly) as MethodSpec;
5521 if (ms == null || !ms.IsPublic) {
5522 Error_WrongEnumerator (rc, enumerator);
5526 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5529 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5531 var ps = MemberCache.FindMember (enumerator.ReturnType,
5532 MemberFilter.Property ("Current", null),
5533 BindingRestriction.InstanceOnly) as PropertySpec;
5535 if (ps == null || !ps.IsPublic) {
5536 Error_WrongEnumerator (rc, enumerator);
5543 public override bool Resolve (BlockContext ec)
5545 bool is_dynamic = expr.Type == InternalType.Dynamic;
5548 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5549 } else if (TypeManager.IsNullableType (expr.Type)) {
5550 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
5553 var get_enumerator_mg = ResolveGetEnumerator (ec);
5554 if (get_enumerator_mg == null) {
5558 var get_enumerator = get_enumerator_mg.BestCandidate;
5559 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
5560 enumerator_variable.Resolve (ec);
5562 // Prepare bool MoveNext ()
5563 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5564 if (move_next_mg == null) {
5568 move_next_mg.InstanceExpression = enumerator_variable;
5570 // Prepare ~T~ Current { get; }
5571 var current_prop = ResolveCurrent (ec, get_enumerator);
5572 if (current_prop == null) {
5576 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
5577 if (current_pe == null)
5580 VarExpr ve = var_type as VarExpr;
5583 // Source type is dynamic, set element type to dynamic too
5584 var_type = new TypeExpression (InternalType.Dynamic, var_type.Location);
5586 // Infer implicitly typed local variable from foreach enumerable type
5587 var_type = new TypeExpression (current_pe.Type, var_type.Location);
5589 } else if (is_dynamic) {
5590 // Explicit cast of dynamic collection elements has to be done at runtime
5591 current_pe = EmptyCast.Create (current_pe, InternalType.Dynamic);
5594 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5595 if (var_type == null)
5598 variable.Type = var_type.Type;
5600 var init = new Invocation (get_enumerator_mg, null);
5602 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5603 new Body (var_type.Type, variable, current_pe, statement, loc), loc);
5605 var enum_type = enumerator_variable.Type;
5608 // Add Dispose method call when enumerator can be IDisposable
5610 if (!enum_type.ImplementsInterface (TypeManager.idisposable_type, false)) {
5611 if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) {
5613 // Runtime Dispose check
5615 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
5616 vd.Initializer = init;
5617 statement = new Using (vd, statement, loc);
5620 // No Dispose call needed
5622 this.init = new SimpleAssign (enumerator_variable, init);
5623 this.init.Resolve (ec);
5627 // Static Dispose check
5629 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
5630 vd.Initializer = init;
5631 statement = new Using (vd, statement, loc);
5634 return statement.Resolve (ec);
5637 protected override void DoEmit (EmitContext ec)
5639 enumerator_variable.LocalInfo.CreateBuilder (ec);
5642 init.EmitStatement (ec);
5644 statement.Emit (ec);
5647 #region IErrorHandler Members
5649 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5651 ec.Report.SymbolRelatedToPreviousError (best);
5652 ec.Report.Warning (278, 2, loc,
5653 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5654 expr.Type.GetSignatureForError (), "enumerable",
5655 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5657 ambiguous_getenumerator_name = true;
5661 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5666 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5671 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5680 LocalVariable variable;
5682 Statement statement;
5684 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
5687 this.variable = var;
5693 public Statement Statement {
5694 get { return statement; }
5697 public override bool Resolve (BlockContext ec)
5699 expr = expr.Resolve (ec);
5704 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5708 if (expr.Type == TypeManager.string_type) {
5709 statement = new ArrayForeach (this, 1);
5710 } else if (expr.Type is ArrayContainer) {
5711 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5713 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5714 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5715 expr.ExprClassName);
5719 statement = new CollectionForeach (type, variable, expr, statement, loc);
5722 return statement.Resolve (ec);
5725 protected override void DoEmit (EmitContext ec)
5727 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5728 ec.LoopBegin = ec.DefineLabel ();
5729 ec.LoopEnd = ec.DefineLabel ();
5731 statement.Emit (ec);
5733 ec.LoopBegin = old_begin;
5734 ec.LoopEnd = old_end;
5737 protected override void CloneTo (CloneContext clonectx, Statement t)
5739 Foreach target = (Foreach) t;
5741 target.type = type.Clone (clonectx);
5742 target.expr = expr.Clone (clonectx);
5743 target.statement = statement.Clone (clonectx);