2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@seznam.cz)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
15 using System
.Reflection
;
16 using System
.Reflection
.Emit
;
17 using System
.Diagnostics
;
18 using System
.Collections
.Generic
;
20 namespace Mono
.CSharp
{
22 public abstract class Statement
{
26 /// Resolves the statement, true means that all sub-statements
29 public virtual bool Resolve (BlockContext ec
)
35 /// We already know that the statement is unreachable, but we still
36 /// need to resolve it to catch errors.
38 public virtual bool ResolveUnreachable (BlockContext ec
, bool warn
)
41 // This conflicts with csc's way of doing this, but IMHO it's
42 // the right thing to do.
44 // If something is unreachable, we still check whether it's
45 // correct. This means that you cannot use unassigned variables
46 // in unreachable code, for instance.
50 ec
.Report
.Warning (162, 2, loc
, "Unreachable code detected");
52 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Block
, loc
);
53 bool ok
= Resolve (ec
);
54 ec
.KillFlowBranching ();
60 /// Return value indicates whether all code paths emitted return.
62 protected abstract void DoEmit (EmitContext ec
);
64 public virtual void Emit (EmitContext ec
)
71 // This routine must be overrided in derived classes and make copies
72 // of all the data that might be modified if resolved
74 protected abstract void CloneTo (CloneContext clonectx
, Statement target
);
76 public Statement
Clone (CloneContext clonectx
)
78 Statement s
= (Statement
) this.MemberwiseClone ();
79 CloneTo (clonectx
, s
);
83 public virtual Expression
CreateExpressionTree (ResolveContext ec
)
85 ec
.Report
.Error (834, loc
, "A lambda expression with statement body cannot be converted to an expresion tree");
89 public Statement
PerformClone ()
91 CloneContext clonectx
= new CloneContext ();
93 return Clone (clonectx
);
97 public sealed class EmptyStatement
: Statement
99 public EmptyStatement (Location loc
)
104 public override bool Resolve (BlockContext ec
)
109 public override bool ResolveUnreachable (BlockContext ec
, bool warn
)
114 public override void Emit (EmitContext ec
)
118 protected override void DoEmit (EmitContext ec
)
120 throw new NotSupportedException ();
123 protected override void CloneTo (CloneContext clonectx
, Statement target
)
129 public class If
: Statement
{
131 public Statement TrueStatement
;
132 public Statement FalseStatement
;
136 public If (Expression bool_expr
, Statement true_statement
, Location l
)
137 : this (bool_expr
, true_statement
, null, l
)
141 public If (Expression bool_expr
,
142 Statement true_statement
,
143 Statement false_statement
,
146 this.expr
= bool_expr
;
147 TrueStatement
= true_statement
;
148 FalseStatement
= false_statement
;
152 public override bool Resolve (BlockContext ec
)
156 Report
.Debug (1, "START IF BLOCK", loc
);
158 expr
= expr
.Resolve (ec
);
163 // Dead code elimination
165 if (expr
is Constant
) {
166 bool take
= !((Constant
) expr
).IsDefaultValue
;
169 if (!TrueStatement
.Resolve (ec
))
172 if ((FalseStatement
!= null) &&
173 !FalseStatement
.ResolveUnreachable (ec
, true))
175 FalseStatement
= null;
177 if (!TrueStatement
.ResolveUnreachable (ec
, true))
179 TrueStatement
= null;
181 if ((FalseStatement
!= null) &&
182 !FalseStatement
.Resolve (ec
))
190 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
192 ok
&= TrueStatement
.Resolve (ec
);
194 is_true_ret
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
196 ec
.CurrentBranching
.CreateSibling ();
198 if (FalseStatement
!= null)
199 ok
&= FalseStatement
.Resolve (ec
);
201 ec
.EndFlowBranching ();
203 Report
.Debug (1, "END IF BLOCK", loc
);
208 protected override void DoEmit (EmitContext ec
)
210 Label false_target
= ec
.DefineLabel ();
214 // If we're a boolean constant, Resolve() already
215 // eliminated dead code for us.
217 Constant c
= expr
as Constant
;
219 c
.EmitSideEffect (ec
);
221 if (!c
.IsDefaultValue
)
222 TrueStatement
.Emit (ec
);
223 else if (FalseStatement
!= null)
224 FalseStatement
.Emit (ec
);
229 expr
.EmitBranchable (ec
, false_target
, false);
231 TrueStatement
.Emit (ec
);
233 if (FalseStatement
!= null){
234 bool branch_emitted
= false;
236 end
= ec
.DefineLabel ();
238 ec
.Emit (OpCodes
.Br
, end
);
239 branch_emitted
= true;
242 ec
.MarkLabel (false_target
);
243 FalseStatement
.Emit (ec
);
248 ec
.MarkLabel (false_target
);
252 protected override void CloneTo (CloneContext clonectx
, Statement t
)
256 target
.expr
= expr
.Clone (clonectx
);
257 target
.TrueStatement
= TrueStatement
.Clone (clonectx
);
258 if (FalseStatement
!= null)
259 target
.FalseStatement
= FalseStatement
.Clone (clonectx
);
263 public class Do
: Statement
{
264 public Expression expr
;
265 public Statement EmbeddedStatement
;
267 public Do (Statement statement
, BooleanExpression bool_expr
, Location l
)
270 EmbeddedStatement
= statement
;
274 public override bool Resolve (BlockContext ec
)
278 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
280 bool was_unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
282 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
283 if (!EmbeddedStatement
.Resolve (ec
))
285 ec
.EndFlowBranching ();
287 if (ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
&& !was_unreachable
)
288 ec
.Report
.Warning (162, 2, expr
.Location
, "Unreachable code detected");
290 expr
= expr
.Resolve (ec
);
293 else if (expr
is Constant
){
294 bool infinite
= !((Constant
) expr
).IsDefaultValue
;
296 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
299 ec
.EndFlowBranching ();
304 protected override void DoEmit (EmitContext ec
)
306 Label loop
= ec
.DefineLabel ();
307 Label old_begin
= ec
.LoopBegin
;
308 Label old_end
= ec
.LoopEnd
;
310 ec
.LoopBegin
= ec
.DefineLabel ();
311 ec
.LoopEnd
= ec
.DefineLabel ();
314 EmbeddedStatement
.Emit (ec
);
315 ec
.MarkLabel (ec
.LoopBegin
);
318 // Dead code elimination
320 if (expr
is Constant
){
321 bool res
= !((Constant
) expr
).IsDefaultValue
;
323 expr
.EmitSideEffect (ec
);
325 ec
.Emit (OpCodes
.Br
, loop
);
327 expr
.EmitBranchable (ec
, loop
, true);
329 ec
.MarkLabel (ec
.LoopEnd
);
331 ec
.LoopBegin
= old_begin
;
332 ec
.LoopEnd
= old_end
;
335 protected override void CloneTo (CloneContext clonectx
, Statement t
)
339 target
.EmbeddedStatement
= EmbeddedStatement
.Clone (clonectx
);
340 target
.expr
= expr
.Clone (clonectx
);
344 public class While
: Statement
{
345 public Expression expr
;
346 public Statement Statement
;
347 bool infinite
, empty
;
349 public While (BooleanExpression bool_expr
, Statement statement
, Location l
)
351 this.expr
= bool_expr
;
352 Statement
= statement
;
356 public override bool Resolve (BlockContext ec
)
360 expr
= expr
.Resolve (ec
);
365 // Inform whether we are infinite or not
367 if (expr
is Constant
){
368 bool value = !((Constant
) expr
).IsDefaultValue
;
371 if (!Statement
.ResolveUnreachable (ec
, true))
379 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
381 ec
.CurrentBranching
.CreateSibling ();
383 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
384 if (!Statement
.Resolve (ec
))
386 ec
.EndFlowBranching ();
388 // There's no direct control flow from the end of the embedded statement to the end of the loop
389 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
391 ec
.EndFlowBranching ();
396 protected override void DoEmit (EmitContext ec
)
399 expr
.EmitSideEffect (ec
);
403 Label old_begin
= ec
.LoopBegin
;
404 Label old_end
= ec
.LoopEnd
;
406 ec
.LoopBegin
= ec
.DefineLabel ();
407 ec
.LoopEnd
= ec
.DefineLabel ();
410 // Inform whether we are infinite or not
412 if (expr
is Constant
){
413 // expr is 'true', since the 'empty' case above handles the 'false' case
414 ec
.MarkLabel (ec
.LoopBegin
);
415 expr
.EmitSideEffect (ec
);
417 ec
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
420 // Inform that we are infinite (ie, `we return'), only
421 // if we do not `break' inside the code.
423 ec
.MarkLabel (ec
.LoopEnd
);
425 Label while_loop
= ec
.DefineLabel ();
427 ec
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
428 ec
.MarkLabel (while_loop
);
432 ec
.MarkLabel (ec
.LoopBegin
);
435 expr
.EmitBranchable (ec
, while_loop
, true);
437 ec
.MarkLabel (ec
.LoopEnd
);
440 ec
.LoopBegin
= old_begin
;
441 ec
.LoopEnd
= old_end
;
444 public override void Emit (EmitContext ec
)
449 protected override void CloneTo (CloneContext clonectx
, Statement t
)
451 While target
= (While
) t
;
453 target
.expr
= expr
.Clone (clonectx
);
454 target
.Statement
= Statement
.Clone (clonectx
);
458 public class For
: Statement
{
460 Statement InitStatement
;
462 public Statement Statement
;
463 bool infinite
, empty
;
465 public For (Statement init_statement
,
466 BooleanExpression test
,
471 InitStatement
= init_statement
;
473 Increment
= increment
;
474 Statement
= statement
;
478 public override bool Resolve (BlockContext ec
)
482 if (InitStatement
!= null){
483 if (!InitStatement
.Resolve (ec
))
488 Test
= Test
.Resolve (ec
);
491 else if (Test
is Constant
){
492 bool value = !((Constant
) Test
).IsDefaultValue
;
495 if (!Statement
.ResolveUnreachable (ec
, true))
497 if ((Increment
!= null) &&
498 !Increment
.ResolveUnreachable (ec
, false))
508 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
510 ec
.CurrentBranching
.CreateSibling ();
512 bool was_unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
514 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
515 if (!Statement
.Resolve (ec
))
517 ec
.EndFlowBranching ();
519 if (Increment
!= null){
520 if (ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
) {
521 if (!Increment
.ResolveUnreachable (ec
, !was_unreachable
))
524 if (!Increment
.Resolve (ec
))
529 // There's no direct control flow from the end of the embedded statement to the end of the loop
530 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
532 ec
.EndFlowBranching ();
537 protected override void DoEmit (EmitContext ec
)
539 if (InitStatement
!= null)
540 InitStatement
.Emit (ec
);
543 Test
.EmitSideEffect (ec
);
547 Label old_begin
= ec
.LoopBegin
;
548 Label old_end
= ec
.LoopEnd
;
549 Label loop
= ec
.DefineLabel ();
550 Label test
= ec
.DefineLabel ();
552 ec
.LoopBegin
= ec
.DefineLabel ();
553 ec
.LoopEnd
= ec
.DefineLabel ();
555 ec
.Emit (OpCodes
.Br
, test
);
559 ec
.MarkLabel (ec
.LoopBegin
);
564 // If test is null, there is no test, and we are just
569 // The Resolve code already catches the case for
570 // Test == Constant (false) so we know that
573 if (Test
is Constant
) {
574 Test
.EmitSideEffect (ec
);
575 ec
.Emit (OpCodes
.Br
, loop
);
577 Test
.EmitBranchable (ec
, loop
, true);
581 ec
.Emit (OpCodes
.Br
, loop
);
582 ec
.MarkLabel (ec
.LoopEnd
);
584 ec
.LoopBegin
= old_begin
;
585 ec
.LoopEnd
= old_end
;
588 protected override void CloneTo (CloneContext clonectx
, Statement t
)
590 For target
= (For
) t
;
592 if (InitStatement
!= null)
593 target
.InitStatement
= InitStatement
.Clone (clonectx
);
595 target
.Test
= Test
.Clone (clonectx
);
596 if (Increment
!= null)
597 target
.Increment
= Increment
.Clone (clonectx
);
598 target
.Statement
= Statement
.Clone (clonectx
);
602 public class StatementExpression
: Statement
{
603 ExpressionStatement expr
;
605 public StatementExpression (ExpressionStatement expr
)
611 public override bool Resolve (BlockContext ec
)
613 expr
= expr
.ResolveStatement (ec
);
617 protected override void DoEmit (EmitContext ec
)
619 expr
.EmitStatement (ec
);
622 public override string ToString ()
624 return "StatementExpression (" + expr
+ ")";
627 protected override void CloneTo (CloneContext clonectx
, Statement t
)
629 StatementExpression target
= (StatementExpression
) t
;
631 target
.expr
= (ExpressionStatement
) expr
.Clone (clonectx
);
635 // A 'return' or a 'yield break'
636 public abstract class ExitStatement
: Statement
638 protected bool unwind_protect
;
639 protected abstract bool DoResolve (BlockContext ec
);
641 public virtual void Error_FinallyClause (Report Report
)
643 Report
.Error (157, loc
, "Control cannot leave the body of a finally clause");
646 public sealed override bool Resolve (BlockContext ec
)
651 unwind_protect
= ec
.CurrentBranching
.AddReturnOrigin (ec
.CurrentBranching
.CurrentUsageVector
, this);
653 ec
.NeedReturnLabel ();
654 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
660 /// Implements the return statement
662 public class Return
: ExitStatement
{
663 protected Expression Expr
;
664 public Return (Expression expr
, Location l
)
670 protected override bool DoResolve (BlockContext ec
)
673 if (ec
.ReturnType
== TypeManager
.void_type
)
676 ec
.Report
.Error (126, loc
,
677 "An object of a type convertible to `{0}' is required for the return statement",
678 TypeManager
.CSharpName (ec
.ReturnType
));
682 if (ec
.CurrentBlock
.Toplevel
.IsIterator
) {
683 ec
.Report
.Error (1622, loc
, "Cannot return a value from iterators. Use the yield return " +
684 "statement to return a value, or yield break to end the iteration");
687 AnonymousExpression am
= ec
.CurrentAnonymousMethod
;
688 if (am
== null && ec
.ReturnType
== TypeManager
.void_type
) {
689 ec
.Report
.Error (127, loc
, "`{0}': A return keyword must not be followed by any expression when method returns void",
690 ec
.GetSignatureForError ());
693 Expr
= Expr
.Resolve (ec
);
697 if (ec
.HasSet (ResolveContext
.Options
.InferReturnType
)) {
698 ec
.ReturnTypeInference
.AddCommonTypeBound (Expr
.Type
);
702 if (Expr
.Type
!= ec
.ReturnType
) {
703 Expr
= Convert
.ImplicitConversionRequired (ec
, Expr
, ec
.ReturnType
, loc
);
707 ec
.Report
.Error (1662, loc
,
708 "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",
709 am
.ContainerType
, am
.GetSignatureForError ());
718 protected override void DoEmit (EmitContext ec
)
724 ec
.Emit (OpCodes
.Stloc
, ec
.TemporaryReturn ());
728 ec
.Emit (OpCodes
.Leave
, ec
.ReturnLabel
);
730 ec
.Emit (OpCodes
.Ret
);
733 protected override void CloneTo (CloneContext clonectx
, Statement t
)
735 Return target
= (Return
) t
;
736 // It's null for simple return;
738 target
.Expr
= Expr
.Clone (clonectx
);
742 public class Goto
: Statement
{
744 LabeledStatement label
;
747 public override bool Resolve (BlockContext ec
)
749 unwind_protect
= ec
.CurrentBranching
.AddGotoOrigin (ec
.CurrentBranching
.CurrentUsageVector
, this);
750 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
754 public Goto (string label
, Location l
)
760 public string Target
{
761 get { return target; }
764 public void SetResolvedTarget (LabeledStatement label
)
767 label
.AddReference ();
770 protected override void CloneTo (CloneContext clonectx
, Statement target
)
775 protected override void DoEmit (EmitContext ec
)
778 throw new InternalErrorException ("goto emitted before target resolved");
779 Label l
= label
.LabelTarget (ec
);
780 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, l
);
784 public class LabeledStatement
: Statement
{
790 FlowBranching
.UsageVector vectors
;
792 public LabeledStatement (string name
, Location l
)
798 public Label
LabelTarget (EmitContext ec
)
803 label
= ec
.DefineLabel ();
812 public bool IsDefined
{
813 get { return defined; }
816 public bool HasBeenReferenced
{
817 get { return referenced; }
820 public FlowBranching
.UsageVector JumpOrigins
{
821 get { return vectors; }
824 public void AddUsageVector (FlowBranching
.UsageVector vector
)
826 vector
= vector
.Clone ();
827 vector
.Next
= vectors
;
831 protected override void CloneTo (CloneContext clonectx
, Statement target
)
836 public override bool Resolve (BlockContext ec
)
838 // this flow-branching will be terminated when the surrounding block ends
839 ec
.StartFlowBranching (this);
843 protected override void DoEmit (EmitContext ec
)
846 ec
.MarkLabel (label
);
849 public void AddReference ()
857 /// `goto default' statement
859 public class GotoDefault
: Statement
{
861 public GotoDefault (Location l
)
866 protected override void CloneTo (CloneContext clonectx
, Statement target
)
871 public override bool Resolve (BlockContext ec
)
873 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
875 if (ec
.Switch
== null) {
876 ec
.Report
.Error (153, loc
, "A goto case is only valid inside a switch statement");
880 if (!ec
.Switch
.GotDefault
) {
881 FlowBranchingBlock
.Error_UnknownLabel (loc
, "default", ec
.Report
);
888 protected override void DoEmit (EmitContext ec
)
890 ec
.Emit (OpCodes
.Br
, ec
.Switch
.DefaultTarget
);
895 /// `goto case' statement
897 public class GotoCase
: Statement
{
901 public GotoCase (Expression e
, Location l
)
907 public override bool Resolve (BlockContext ec
)
909 if (ec
.Switch
== null){
910 ec
.Report
.Error (153, loc
, "A goto case is only valid inside a switch statement");
914 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
916 expr
= expr
.Resolve (ec
);
920 Constant c
= expr
as Constant
;
922 ec
.Report
.Error (150, expr
.Location
, "A constant value is expected");
926 TypeSpec type
= ec
.Switch
.SwitchType
;
927 Constant res
= c
.TryReduce (ec
, type
, c
.Location
);
929 c
.Error_ValueCannotBeConverted (ec
, loc
, type
, true);
933 if (!Convert
.ImplicitStandardConversionExists (c
, type
))
934 ec
.Report
.Warning (469, 2, loc
,
935 "The `goto case' value is not implicitly convertible to type `{0}'",
936 TypeManager
.CSharpName (type
));
938 object val
= res
.GetValue ();
940 val
= SwitchLabel
.NullStringCase
;
942 if (!ec
.Switch
.Elements
.TryGetValue (val
, out sl
)) {
943 FlowBranchingBlock
.Error_UnknownLabel (loc
, "case " +
944 (c
.GetValue () == null ? "null" : val
.ToString ()), ec
.Report
);
951 protected override void DoEmit (EmitContext ec
)
953 ec
.Emit (OpCodes
.Br
, sl
.GetILLabelCode (ec
));
956 protected override void CloneTo (CloneContext clonectx
, Statement t
)
958 GotoCase target
= (GotoCase
) t
;
960 target
.expr
= expr
.Clone (clonectx
);
964 public class Throw
: Statement
{
967 public Throw (Expression expr
, Location l
)
973 public override bool Resolve (BlockContext ec
)
976 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
977 return ec
.CurrentBranching
.CheckRethrow (loc
);
980 expr
= expr
.Resolve (ec
, ResolveFlags
.Type
| ResolveFlags
.VariableOrValue
);
981 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
986 if (Convert
.ImplicitConversionExists (ec
, expr
, TypeManager
.exception_type
))
987 expr
= Convert
.ImplicitConversion (ec
, expr
, TypeManager
.exception_type
, loc
);
989 ec
.Report
.Error (155, expr
.Location
, "The type caught or thrown must be derived from System.Exception");
994 protected override void DoEmit (EmitContext ec
)
997 ec
.Emit (OpCodes
.Rethrow
);
1001 ec
.Emit (OpCodes
.Throw
);
1005 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1007 Throw target
= (Throw
) t
;
1010 target
.expr
= expr
.Clone (clonectx
);
1014 public class Break
: Statement
{
1016 public Break (Location l
)
1021 bool unwind_protect
;
1023 public override bool Resolve (BlockContext ec
)
1025 unwind_protect
= ec
.CurrentBranching
.AddBreakOrigin (ec
.CurrentBranching
.CurrentUsageVector
, loc
);
1026 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1030 protected override void DoEmit (EmitContext ec
)
1032 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, ec
.LoopEnd
);
1035 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1041 public class Continue
: Statement
{
1043 public Continue (Location l
)
1048 bool unwind_protect
;
1050 public override bool Resolve (BlockContext ec
)
1052 unwind_protect
= ec
.CurrentBranching
.AddContinueOrigin (ec
.CurrentBranching
.CurrentUsageVector
, loc
);
1053 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1057 protected override void DoEmit (EmitContext ec
)
1059 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, ec
.LoopBegin
);
1062 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1068 public interface ILocalVariable
1070 void Emit (EmitContext ec
);
1071 void EmitAssign (EmitContext ec
);
1072 void EmitAddressOf (EmitContext ec
);
1075 public interface IKnownVariable
{
1076 Block Block { get; }
1077 Location Location { get; }
1081 // The information about a user-perceived local variable
1083 public class LocalInfo
: IKnownVariable
, ILocalVariable
{
1084 public readonly FullNamedExpression Type
;
1086 public TypeSpec VariableType
;
1087 public readonly string Name
;
1088 public readonly Location Location
;
1089 public readonly Block Block
;
1091 public VariableInfo VariableInfo
;
1092 HoistedVariable hoisted_variant
;
1101 CompilerGenerated
= 64,
1105 public enum ReadOnlyContext
: byte {
1112 ReadOnlyContext ro_context
;
1113 LocalBuilder builder
;
1115 public LocalInfo (FullNamedExpression type
, string name
, Block block
, Location l
)
1123 public LocalInfo (TypeContainer ds
, Block block
, Location l
)
1125 VariableType
= ds
.IsGeneric
? ds
.CurrentType
: ds
.Definition
;
1130 public void ResolveVariable (EmitContext ec
)
1132 if (HoistedVariant
!= null)
1135 if (builder
== null) {
1136 builder
= ec
.DeclareLocal (VariableType
, Pinned
);
1140 public void Emit (EmitContext ec
)
1142 ec
.Emit (OpCodes
.Ldloc
, builder
);
1145 public void EmitAssign (EmitContext ec
)
1147 ec
.Emit (OpCodes
.Stloc
, builder
);
1150 public void EmitAddressOf (EmitContext ec
)
1152 ec
.Emit (OpCodes
.Ldloca
, builder
);
1155 public void EmitSymbolInfo (EmitContext ec
)
1157 if (builder
!= null)
1158 ec
.DefineLocalVariable (Name
, builder
);
1162 // Hoisted local variable variant
1164 public HoistedVariable HoistedVariant
{
1166 return hoisted_variant
;
1169 hoisted_variant
= value;
1173 public bool IsThisAssigned (BlockContext ec
, Block block
)
1175 if (VariableInfo
== null)
1176 throw new Exception ();
1178 if (!ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
))
1181 return VariableInfo
.TypeInfo
.IsFullyInitialized (ec
, VariableInfo
, block
.StartLocation
);
1184 public bool IsAssigned (BlockContext ec
)
1186 if (VariableInfo
== null)
1187 throw new Exception ();
1189 return !ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
);
1192 public bool Resolve (ResolveContext ec
)
1194 if (VariableType
!= null)
1197 TypeExpr texpr
= Type
.ResolveAsContextualType (ec
, false);
1201 VariableType
= texpr
.Type
;
1203 if (VariableType
.IsStatic
) {
1204 FieldBase
.Error_VariableOfStaticClass (Location
, Name
, VariableType
, ec
.Report
);
1208 if (VariableType
.IsPointer
&& !ec
.IsUnsafe
)
1209 Expression
.UnsafeError (ec
, Location
);
1214 public bool IsConstant
{
1215 get { return (flags & Flags.IsConstant) != 0; }
1216 set { flags |= Flags.IsConstant; }
1219 public bool AddressTaken
{
1220 get { return (flags & Flags.AddressTaken) != 0; }
1221 set { flags |= Flags.AddressTaken; }
1224 public bool CompilerGenerated
{
1225 get { return (flags & Flags.CompilerGenerated) != 0; }
1226 set { flags |= Flags.CompilerGenerated; }
1229 public override string ToString ()
1231 return String
.Format ("LocalInfo ({0},{1},{2},{3})",
1232 Name
, Type
, VariableInfo
, Location
);
1236 get { return (flags & Flags.Used) != 0; }
1237 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1240 public bool ReadOnly
{
1241 get { return (flags & Flags.ReadOnly) != 0; }
1244 public void SetReadOnlyContext (ReadOnlyContext context
)
1246 flags
|= Flags
.ReadOnly
;
1247 ro_context
= context
;
1250 public string GetReadOnlyContext ()
1253 throw new InternalErrorException ("Variable is not readonly");
1255 switch (ro_context
) {
1256 case ReadOnlyContext
.Fixed
:
1257 return "fixed variable";
1258 case ReadOnlyContext
.Foreach
:
1259 return "foreach iteration variable";
1260 case ReadOnlyContext
.Using
:
1261 return "using variable";
1263 throw new NotImplementedException ();
1267 // Whether the variable is pinned, if Pinned the variable has been
1268 // allocated in a pinned slot with DeclareLocal.
1270 public bool Pinned
{
1271 get { return (flags & Flags.Pinned) != 0; }
1272 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1275 public bool IsThis
{
1276 get { return (flags & Flags.IsThis) != 0; }
1277 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1280 Block IKnownVariable
.Block
{
1281 get { return Block; }
1284 Location IKnownVariable
.Location
{
1285 get { return Location; }
1288 public LocalInfo
Clone (CloneContext clonectx
)
1291 // Variables in anonymous block are not resolved yet
1293 if (VariableType
== null)
1294 return new LocalInfo ((FullNamedExpression
) Type
.Clone (clonectx
), Name
, clonectx
.LookupBlock (Block
), Location
);
1297 // Variables in method block are resolved
1299 LocalInfo li
= new LocalInfo (null, Name
, clonectx
.LookupBlock (Block
), Location
);
1300 li
.VariableType
= VariableType
;
1306 /// Block represents a C# block.
1310 /// This class is used in a number of places: either to represent
1311 /// explicit blocks that the programmer places or implicit blocks.
1313 /// Implicit blocks are used as labels or to introduce variable
1316 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1317 /// they contain extra information that is not necessary on normal blocks.
1319 public class Block
: Statement
{
1320 public Block Parent
;
1321 public Location StartLocation
;
1322 public Location EndLocation
= Location
.Null
;
1324 public ExplicitBlock Explicit
;
1325 public ToplevelBlock Toplevel
; // TODO: Use Explicit
1332 VariablesInitialized
= 4,
1336 HasCapturedVariable
= 64,
1337 HasCapturedThis
= 1 << 7,
1338 IsExpressionTree
= 1 << 8
1341 protected Flags flags
;
1343 public bool Unchecked
{
1344 get { return (flags & Flags.Unchecked) != 0; }
1345 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1348 public bool Unsafe
{
1349 get { return (flags & Flags.Unsafe) != 0; }
1350 set { flags |= Flags.Unsafe; }
1354 // The statements in this block
1356 protected List
<Statement
> statements
;
1359 // An array of Blocks. We keep track of children just
1360 // to generate the local variable declarations.
1362 // Statements and child statements are handled through the
1365 List
<Block
> children
;
1368 // Labels. (label, block) pairs.
1370 protected Dictionary
<string, LabeledStatement
> labels
;
1373 // Keeps track of (name, type) pairs
1375 Dictionary
<string, LocalInfo
> variables
;
1378 // Keeps track of constants
1379 Dictionary
<string, Expression
> constants
;
1382 // Temporary variables.
1384 List
<LocalInfo
> temporary_variables
;
1387 // If this is a switch section, the enclosing switch block.
1391 protected List
<Statement
> scope_initializers
;
1393 List
<ToplevelBlock
> anonymous_children
;
1395 int? resolving_init_idx
;
1397 protected static int id
;
1401 int assignable_slots
;
1402 bool unreachable_shown
;
1405 public Block (Block parent
)
1406 : this (parent
, (Flags
) 0, Location
.Null
, Location
.Null
)
1409 public Block (Block parent
, Flags flags
)
1410 : this (parent
, flags
, Location
.Null
, Location
.Null
)
1413 public Block (Block parent
, Location start
, Location end
)
1414 : this (parent
, (Flags
) 0, start
, end
)
1418 // Useful when TopLevel block is downgraded to normal block
1420 public Block (ToplevelBlock parent
, ToplevelBlock source
)
1421 : this (parent
, source
.flags
, source
.StartLocation
, source
.EndLocation
)
1423 statements
= source
.statements
;
1424 children
= source
.children
;
1425 labels
= source
.labels
;
1426 variables
= source
.variables
;
1427 constants
= source
.constants
;
1428 switch_block
= source
.switch_block
;
1431 public Block (Block parent
, Flags flags
, Location start
, Location end
)
1433 if (parent
!= null) {
1434 parent
.AddChild (this);
1436 // the appropriate constructors will fixup these fields
1437 Toplevel
= parent
.Toplevel
;
1438 Explicit
= parent
.Explicit
;
1441 this.Parent
= parent
;
1443 this.StartLocation
= start
;
1444 this.EndLocation
= end
;
1447 statements
= new List
<Statement
> (4);
1450 public Block
CreateSwitchBlock (Location start
)
1452 // FIXME: should this be implicit?
1453 Block new_block
= new ExplicitBlock (this, start
, start
);
1454 new_block
.switch_block
= this;
1459 get { return this_id; }
1462 public IDictionary
<string, LocalInfo
> Variables
{
1464 if (variables
== null)
1465 variables
= new Dictionary
<string, LocalInfo
> ();
1470 void AddChild (Block b
)
1472 if (children
== null)
1473 children
= new List
<Block
> (1);
1478 public void SetEndLocation (Location loc
)
1483 protected void Error_158 (string name
, Location loc
)
1485 Toplevel
.Report
.Error (158, loc
, "The label `{0}' shadows another label " +
1486 "by the same name in a contained scope", name
);
1490 /// Adds a label to the current block.
1494 /// false if the name already exists in this block. true
1498 public bool AddLabel (LabeledStatement target
)
1500 if (switch_block
!= null)
1501 return switch_block
.AddLabel (target
);
1503 string name
= target
.Name
;
1506 while (cur
!= null) {
1507 LabeledStatement s
= cur
.DoLookupLabel (name
);
1509 Toplevel
.Report
.SymbolRelatedToPreviousError (s
.loc
, s
.Name
);
1510 Toplevel
.Report
.Error (140, target
.loc
, "The label `{0}' is a duplicate", name
);
1514 if (this == Explicit
)
1520 while (cur
!= null) {
1521 if (cur
.DoLookupLabel (name
) != null) {
1522 Error_158 (name
, target
.loc
);
1526 if (children
!= null) {
1527 foreach (Block b
in children
) {
1528 LabeledStatement s
= b
.DoLookupLabel (name
);
1532 Toplevel
.Report
.SymbolRelatedToPreviousError (s
.loc
, s
.Name
);
1533 Error_158 (name
, target
.loc
);
1541 Toplevel
.CheckError158 (name
, target
.loc
);
1544 labels
= new Dictionary
<string, LabeledStatement
> ();
1546 labels
.Add (name
, target
);
1550 public LabeledStatement
LookupLabel (string name
)
1552 LabeledStatement s
= DoLookupLabel (name
);
1556 if (children
== null)
1559 foreach (Block child
in children
) {
1560 if (Explicit
!= child
.Explicit
)
1563 s
= child
.LookupLabel (name
);
1571 LabeledStatement
DoLookupLabel (string name
)
1573 if (switch_block
!= null)
1574 return switch_block
.LookupLabel (name
);
1577 if (labels
.ContainsKey (name
))
1578 return labels
[name
];
1583 public bool CheckInvariantMeaningInBlock (string name
, Expression e
, Location loc
)
1586 IKnownVariable kvi
= b
.Explicit
.GetKnownVariable (name
);
1587 while (kvi
== null) {
1588 b
= b
.Explicit
.Parent
;
1591 kvi
= b
.Explicit
.GetKnownVariable (name
);
1597 // Is kvi.Block nested inside 'b'
1598 if (b
.Explicit
!= kvi
.Block
.Explicit
) {
1600 // If a variable by the same name it defined in a nested block of this
1601 // block, we violate the invariant meaning in a block.
1604 Toplevel
.Report
.SymbolRelatedToPreviousError (kvi
.Location
, name
);
1605 Toplevel
.Report
.Error (135, loc
, "`{0}' conflicts with a declaration in a child block", name
);
1610 // It's ok if the definition is in a nested subblock of b, but not
1611 // nested inside this block -- a definition in a sibling block
1612 // should not affect us.
1618 // Block 'b' and kvi.Block are the same textual block.
1619 // However, different variables are extant.
1621 // Check if the variable is in scope in both blocks. We use
1622 // an indirect check that depends on AddVariable doing its
1623 // part in maintaining the invariant-meaning-in-block property.
1625 if (e
is VariableReference
|| (e
is Constant
&& b
.GetLocalInfo (name
) != null))
1628 if (this is ToplevelBlock
) {
1629 Toplevel
.Report
.SymbolRelatedToPreviousError (kvi
.Location
, name
);
1630 e
.Error_VariableIsUsedBeforeItIsDeclared (Toplevel
.Report
, name
);
1635 // Even though we detected the error when the name is used, we
1636 // treat it as if the variable declaration was in error.
1638 Toplevel
.Report
.SymbolRelatedToPreviousError (loc
, name
);
1639 Error_AlreadyDeclared (kvi
.Location
, name
, "parent or current");
1643 protected bool CheckParentConflictName (ToplevelBlock block
, string name
, Location l
)
1645 LocalInfo vi
= GetLocalInfo (name
);
1647 block
.Report
.SymbolRelatedToPreviousError (vi
.Location
, name
);
1648 if (Explicit
== vi
.Block
.Explicit
) {
1649 Error_AlreadyDeclared (l
, name
, null);
1651 Error_AlreadyDeclared (l
, name
, this is ToplevelBlock
?
1652 "parent or current" : "parent");
1657 if (block
!= null) {
1658 var tblock
= block
.CheckParameterNameConflict (name
);
1659 if (tblock
!= null) {
1660 if (block
== tblock
&& block
is Linq
.QueryBlock
)
1661 Error_AlreadyDeclared (loc
, name
);
1663 Error_AlreadyDeclared (loc
, name
, "parent or current");
1672 public LocalInfo
AddVariable (Expression type
, string name
, Location l
)
1674 if (!CheckParentConflictName (Toplevel
, name
, l
))
1677 IKnownVariable kvi
= Explicit
.GetKnownVariable (name
);
1679 Toplevel
.Report
.SymbolRelatedToPreviousError (kvi
.Location
, name
);
1680 Error_AlreadyDeclared (l
, name
, "child");
1684 LocalInfo vi
= new LocalInfo ((FullNamedExpression
) type
, name
, this, l
);
1687 if ((flags
& Flags
.VariablesInitialized
) != 0)
1688 throw new InternalErrorException ("block has already been resolved");
1693 protected virtual void AddVariable (LocalInfo li
)
1695 Variables
.Add (li
.Name
, li
);
1696 Explicit
.AddKnownVariable (li
.Name
, li
);
1699 protected virtual void Error_AlreadyDeclared (Location loc
, string var, string reason
)
1701 if (reason
== null) {
1702 Error_AlreadyDeclared (loc
, var);
1706 Toplevel
.Report
.Error (136, loc
, "A local variable named `{0}' cannot be declared " +
1707 "in this scope because it would give a different meaning " +
1708 "to `{0}', which is already used in a `{1}' scope " +
1709 "to denote something else", var, reason
);
1712 protected virtual void Error_AlreadyDeclared (Location loc
, string name
)
1714 Toplevel
.Report
.Error (128, loc
,
1715 "A local variable named `{0}' is already defined in this scope", name
);
1718 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc
, string name
, string conflict
)
1720 Toplevel
.Report
.Error (412, loc
, "The type parameter name `{0}' is the same as `{1}'",
1724 public bool AddConstant (Expression type
, string name
, Expression
value, Location l
)
1726 if (AddVariable (type
, name
, l
) == null)
1729 if (constants
== null)
1730 constants
= new Dictionary
<string, Expression
> ();
1732 constants
.Add (name
, value);
1734 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1739 static int next_temp_id
= 0;
1741 public LocalInfo
AddTemporaryVariable (TypeExpr te
, Location loc
)
1743 Report
.Debug (64, "ADD TEMPORARY", this, Toplevel
, loc
);
1745 if (temporary_variables
== null)
1746 temporary_variables
= new List
<LocalInfo
> ();
1748 int id
= ++next_temp_id
;
1749 string name
= "$s_" + id
.ToString ();
1751 LocalInfo li
= new LocalInfo (te
, name
, this, loc
);
1752 li
.CompilerGenerated
= true;
1753 temporary_variables
.Add (li
);
1757 public LocalInfo
GetLocalInfo (string name
)
1760 for (Block b
= this; b
!= null; b
= b
.Parent
) {
1761 if (b
.variables
!= null && b
.variables
.TryGetValue (name
, out ret
)) {
1769 public Expression
GetVariableType (string name
)
1771 LocalInfo vi
= GetLocalInfo (name
);
1772 return vi
== null ? null : vi
.Type
;
1775 public Expression
GetConstantExpression (string name
)
1778 for (Block b
= this; b
!= null; b
= b
.Parent
) {
1779 if (b
.constants
!= null) {
1780 if (b
.constants
.TryGetValue (name
, out ret
))
1788 // It should be used by expressions which require to
1789 // register a statement during resolve process.
1791 public void AddScopeStatement (Statement s
)
1793 if (scope_initializers
== null)
1794 scope_initializers
= new List
<Statement
> ();
1797 // Simple recursive helper, when resolve scope initializer another
1798 // new scope initializer can be added, this ensures it's initialized
1799 // before existing one. For now this can happen with expression trees
1800 // in base ctor initializer only
1802 if (resolving_init_idx
.HasValue
) {
1803 scope_initializers
.Insert (resolving_init_idx
.Value
, s
);
1804 ++resolving_init_idx
;
1806 scope_initializers
.Add (s
);
1810 public void AddStatement (Statement s
)
1813 flags
|= Flags
.BlockUsed
;
1817 get { return (flags & Flags.BlockUsed) != 0; }
1822 flags
|= Flags
.BlockUsed
;
1825 public bool HasRet
{
1826 get { return (flags & Flags.HasRet) != 0; }
1829 public int AssignableSlots
{
1832 // if ((flags & Flags.VariablesInitialized) == 0)
1833 // throw new Exception ("Variables have not been initialized yet");
1834 return assignable_slots
;
1838 public IList
<ToplevelBlock
> AnonymousChildren
{
1839 get { return anonymous_children; }
1842 public void AddAnonymousChild (ToplevelBlock b
)
1844 if (anonymous_children
== null)
1845 anonymous_children
= new List
<ToplevelBlock
> ();
1847 anonymous_children
.Add (b
);
1850 void DoResolveConstants (BlockContext ec
)
1852 if (constants
== null)
1855 if (variables
== null)
1856 throw new InternalErrorException ("cannot happen");
1858 foreach (var de
in variables
) {
1859 string name
= de
.Key
;
1860 LocalInfo vi
= de
.Value
;
1861 TypeSpec variable_type
= vi
.VariableType
;
1863 if (variable_type
== null) {
1864 if (vi
.Type
is VarExpr
)
1865 ec
.Report
.Error (822, vi
.Type
.Location
, "An implicitly typed local variable cannot be a constant");
1871 if (!constants
.TryGetValue (name
, out cv
))
1874 // Don't let 'const int Foo = Foo;' succeed.
1875 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1876 // which in turn causes the 'must be constant' error to be triggered.
1877 constants
.Remove (name
);
1879 if (!variable_type
.IsConstantCompatible
) {
1880 Const
.Error_InvalidConstantType (variable_type
, loc
, ec
.Report
);
1884 ec
.CurrentBlock
= this;
1886 using (ec
.With (ResolveContext
.Options
.ConstantCheckState
, (flags
& Flags
.Unchecked
) == 0)) {
1887 e
= cv
.Resolve (ec
);
1892 Constant ce
= e
as Constant
;
1894 e
.Error_ExpressionMustBeConstant (ec
, vi
.Location
, name
);
1898 e
= ce
.ConvertImplicitly (ec
, variable_type
);
1900 if (TypeManager
.IsReferenceType (variable_type
))
1901 ce
.Error_ConstantCanBeInitializedWithNullOnly (ec
, variable_type
, vi
.Location
, vi
.Name
);
1903 ce
.Error_ValueCannotBeConverted (ec
, vi
.Location
, variable_type
, false);
1907 constants
.Add (name
, e
);
1908 vi
.IsConstant
= true;
1912 protected void ResolveMeta (BlockContext ec
, int offset
)
1914 Report
.Debug (64, "BLOCK RESOLVE META", this, Parent
);
1916 // If some parent block was unsafe, we remain unsafe even if this block
1917 // isn't explicitly marked as such.
1918 using (ec
.With (ResolveContext
.Options
.UnsafeScope
, ec
.IsUnsafe
| Unsafe
)) {
1919 flags
|= Flags
.VariablesInitialized
;
1921 if (variables
!= null) {
1922 foreach (LocalInfo li
in variables
.Values
) {
1923 if (!li
.Resolve (ec
))
1925 li
.VariableInfo
= new VariableInfo (li
, offset
);
1926 offset
+= li
.VariableInfo
.Length
;
1929 assignable_slots
= offset
;
1931 DoResolveConstants (ec
);
1933 if (children
== null)
1935 foreach (Block b
in children
)
1936 b
.ResolveMeta (ec
, offset
);
1941 // Emits the local variable declarations for a block
1943 public virtual void EmitMeta (EmitContext ec
)
1945 if (variables
!= null){
1946 foreach (LocalInfo vi
in variables
.Values
)
1947 vi
.ResolveVariable (ec
);
1950 if (temporary_variables
!= null) {
1951 for (int i
= 0; i
< temporary_variables
.Count
; i
++)
1952 ((LocalInfo
)temporary_variables
[i
]).ResolveVariable(ec
);
1955 if (children
!= null) {
1956 for (int i
= 0; i
< children
.Count
; i
++)
1957 ((Block
)children
[i
]).EmitMeta(ec
);
1961 void UsageWarning (BlockContext ec
)
1963 if (variables
== null || ec
.Report
.WarningLevel
< 3)
1966 foreach (var de
in variables
) {
1967 LocalInfo vi
= de
.Value
;
1970 string name
= de
.Key
;
1972 // vi.VariableInfo can be null for 'catch' variables
1973 if (vi
.VariableInfo
!= null && vi
.VariableInfo
.IsEverAssigned
)
1974 ec
.Report
.Warning (219, 3, vi
.Location
, "The variable `{0}' is assigned but its value is never used", name
);
1976 ec
.Report
.Warning (168, 3, vi
.Location
, "The variable `{0}' is declared but never used", name
);
1981 static void CheckPossibleMistakenEmptyStatement (BlockContext ec
, Statement s
)
1985 // Some statements are wrapped by a Block. Since
1986 // others' internal could be changed, here I treat
1987 // them as possibly wrapped by Block equally.
1988 Block b
= s
as Block
;
1989 if (b
!= null && b
.statements
.Count
== 1)
1990 s
= (Statement
) b
.statements
[0];
1993 body
= ((Lock
) s
).Statement
;
1995 body
= ((For
) s
).Statement
;
1996 else if (s
is Foreach
)
1997 body
= ((Foreach
) s
).Statement
;
1998 else if (s
is While
)
1999 body
= ((While
) s
).Statement
;
2000 else if (s
is Fixed
)
2001 body
= ((Fixed
) s
).Statement
;
2002 else if (s
is Using
)
2003 body
= ((Using
) s
).EmbeddedStatement
;
2004 else if (s
is UsingTemporary
)
2005 body
= ((UsingTemporary
) s
).Statement
;
2009 if (body
== null || body
is EmptyStatement
)
2010 ec
.Report
.Warning (642, 3, s
.loc
, "Possible mistaken empty statement");
2013 public override bool Resolve (BlockContext ec
)
2015 Block prev_block
= ec
.CurrentBlock
;
2018 int errors
= ec
.Report
.Errors
;
2020 ec
.CurrentBlock
= this;
2021 ec
.StartFlowBranching (this);
2023 Report
.Debug (4, "RESOLVE BLOCK", StartLocation
, ec
.CurrentBranching
);
2026 // Compiler generated scope statements
2028 if (scope_initializers
!= null) {
2029 for (resolving_init_idx
= 0; resolving_init_idx
< scope_initializers
.Count
; ++resolving_init_idx
) {
2030 scope_initializers
[resolving_init_idx
.Value
].Resolve (ec
);
2033 resolving_init_idx
= null;
2037 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2038 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2039 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2040 // responsible for handling the situation.
2042 int statement_count
= statements
.Count
;
2043 for (int ix
= 0; ix
< statement_count
; ix
++){
2044 Statement s
= statements
[ix
];
2045 // Check possible empty statement (CS0642)
2046 if (ix
+ 1 < statement_count
&& ec
.Report
.WarningLevel
>= 3 &&
2047 statements
[ix
+ 1] is ExplicitBlock
)
2048 CheckPossibleMistakenEmptyStatement (ec
, s
);
2051 // Warn if we detect unreachable code.
2054 if (s
is EmptyStatement
)
2057 if (!unreachable_shown
&& !(s
is LabeledStatement
)) {
2058 ec
.Report
.Warning (162, 2, s
.loc
, "Unreachable code detected");
2059 unreachable_shown
= true;
2062 Block c_block
= s
as Block
;
2063 if (c_block
!= null)
2064 c_block
.unreachable
= c_block
.unreachable_shown
= true;
2068 // Note that we're not using ResolveUnreachable() for unreachable
2069 // statements here. ResolveUnreachable() creates a temporary
2070 // flow branching and kills it afterwards. This leads to problems
2071 // if you have two unreachable statements where the first one
2072 // assigns a variable and the second one tries to access it.
2075 if (!s
.Resolve (ec
)) {
2077 if (ec
.IsInProbingMode
)
2080 statements
[ix
] = new EmptyStatement (s
.loc
);
2084 if (unreachable
&& !(s
is LabeledStatement
) && !(s
is Block
))
2085 statements
[ix
] = new EmptyStatement (s
.loc
);
2087 unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
2088 if (unreachable
&& s
is LabeledStatement
)
2089 throw new InternalErrorException ("should not happen");
2092 Report
.Debug (4, "RESOLVE BLOCK DONE", StartLocation
,
2093 ec
.CurrentBranching
, statement_count
);
2095 while (ec
.CurrentBranching
is FlowBranchingLabeled
)
2096 ec
.EndFlowBranching ();
2098 bool flow_unreachable
= ec
.EndFlowBranching ();
2100 ec
.CurrentBlock
= prev_block
;
2102 if (flow_unreachable
)
2103 flags
|= Flags
.HasRet
;
2105 // If we're a non-static `struct' constructor which doesn't have an
2106 // initializer, then we must initialize all of the struct's fields.
2107 if (this == Toplevel
&& !Toplevel
.IsThisAssigned (ec
) && !flow_unreachable
)
2110 if ((labels
!= null) && (ec
.Report
.WarningLevel
>= 2)) {
2111 foreach (LabeledStatement label
in labels
.Values
)
2112 if (!label
.HasBeenReferenced
)
2113 ec
.Report
.Warning (164, 2, label
.loc
, "This label has not been referenced");
2116 if (ok
&& errors
== ec
.Report
.Errors
)
2122 public override bool ResolveUnreachable (BlockContext ec
, bool warn
)
2124 unreachable_shown
= true;
2128 ec
.Report
.Warning (162, 2, loc
, "Unreachable code detected");
2130 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Block
, loc
);
2131 bool ok
= Resolve (ec
);
2132 ec
.KillFlowBranching ();
2137 protected override void DoEmit (EmitContext ec
)
2139 for (int ix
= 0; ix
< statements
.Count
; ix
++){
2140 Statement s
= (Statement
) statements
[ix
];
2145 public override void Emit (EmitContext ec
)
2147 if (scope_initializers
!= null)
2148 EmitScopeInitializers (ec
);
2150 ec
.Mark (StartLocation
);
2153 if (SymbolWriter
.HasSymbolWriter
)
2154 EmitSymbolInfo (ec
);
2157 protected void EmitScopeInitializers (EmitContext ec
)
2159 SymbolWriter
.OpenCompilerGeneratedBlock (ec
);
2161 using (ec
.With (EmitContext
.Options
.OmitDebugInfo
, true)) {
2162 foreach (Statement s
in scope_initializers
)
2166 SymbolWriter
.CloseCompilerGeneratedBlock (ec
);
2169 protected virtual void EmitSymbolInfo (EmitContext ec
)
2171 if (variables
!= null) {
2172 foreach (LocalInfo vi
in variables
.Values
) {
2173 vi
.EmitSymbolInfo (ec
);
2178 public override string ToString ()
2180 return String
.Format ("{0} ({1}:{2})", GetType (), this_id
, StartLocation
);
2183 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2185 Block target
= (Block
) t
;
2187 clonectx
.AddBlockMap (this, target
);
2189 target
.Toplevel
= (ToplevelBlock
) clonectx
.LookupBlock (Toplevel
);
2190 target
.Explicit
= (ExplicitBlock
) clonectx
.LookupBlock (Explicit
);
2192 target
.Parent
= clonectx
.RemapBlockCopy (Parent
);
2194 if (variables
!= null){
2195 target
.variables
= new Dictionary
<string, LocalInfo
> ();
2197 foreach (var de
in variables
){
2198 LocalInfo newlocal
= de
.Value
.Clone (clonectx
);
2199 target
.variables
[de
.Key
] = newlocal
;
2200 clonectx
.AddVariableMap (de
.Value
, newlocal
);
2204 target
.statements
= new List
<Statement
> (statements
.Count
);
2205 foreach (Statement s
in statements
)
2206 target
.statements
.Add (s
.Clone (clonectx
));
2208 if (target
.children
!= null){
2209 target
.children
= new List
<Block
> (children
.Count
);
2210 foreach (Block b
in children
){
2211 target
.children
.Add (clonectx
.LookupBlock (b
));
2216 // TODO: labels, switch_block, constants (?), anonymous_children
2221 public class ExplicitBlock
: Block
2223 Dictionary
<string, IKnownVariable
> known_variables
;
2224 protected AnonymousMethodStorey am_storey
;
2226 public ExplicitBlock (Block parent
, Location start
, Location end
)
2227 : this (parent
, (Flags
) 0, start
, end
)
2231 public ExplicitBlock (Block parent
, Flags flags
, Location start
, Location end
)
2232 : base (parent
, flags
, start
, end
)
2234 this.Explicit
= this;
2238 // Marks a variable with name @name as being used in this or a child block.
2239 // If a variable name has been used in a child block, it's illegal to
2240 // declare a variable with the same name in the current block.
2242 internal void AddKnownVariable (string name
, IKnownVariable info
)
2244 if (known_variables
== null)
2245 known_variables
= new Dictionary
<string, IKnownVariable
> ();
2247 known_variables
[name
] = info
;
2250 Parent
.Explicit
.AddKnownVariable (name
, info
);
2253 public AnonymousMethodStorey AnonymousMethodStorey
{
2254 get { return am_storey; }
2258 // Creates anonymous method storey in current block
2260 public AnonymousMethodStorey
CreateAnonymousMethodStorey (ResolveContext ec
)
2263 // When referencing a variable in iterator storey from children anonymous method
2265 if (Toplevel
.am_storey
is IteratorStorey
) {
2266 return Toplevel
.am_storey
;
2270 // An iterator has only 1 storey block
2272 if (ec
.CurrentIterator
!= null)
2273 return ec
.CurrentIterator
.Storey
;
2275 if (am_storey
== null) {
2276 MemberBase mc
= ec
.MemberContext
as MemberBase
;
2277 GenericMethod gm
= mc
== null ? null : mc
.GenericMethod
;
2280 // Creates anonymous method storey for this block
2282 am_storey
= new AnonymousMethodStorey (this, ec
.CurrentMemberDefinition
.Parent
.PartialContainer
, mc
, gm
, "AnonStorey");
2288 public override void Emit (EmitContext ec
)
2290 if (am_storey
!= null)
2291 am_storey
.EmitStoreyInstantiation (ec
);
2293 bool emit_debug_info
= SymbolWriter
.HasSymbolWriter
&& Parent
!= null && !(am_storey
is IteratorStorey
);
2294 if (emit_debug_info
)
2299 if (emit_debug_info
)
2303 public override void EmitMeta (EmitContext ec
)
2306 // Creates anonymous method storey
2308 if (am_storey
!= null) {
2309 if (ec
.CurrentAnonymousMethod
!= null && ec
.CurrentAnonymousMethod
.Storey
!= null) {
2311 // Creates parent storey reference when hoisted this is accessible
2313 if (am_storey
.OriginalSourceBlock
.Explicit
.HasCapturedThis
) {
2314 ExplicitBlock parent
= Toplevel
.Parent
.Explicit
;
2317 // Hoisted this exists in top-level parent storey only
2319 while (parent
.am_storey
== null || parent
.am_storey
.Parent
is AnonymousMethodStorey
)
2320 parent
= parent
.Parent
.Explicit
;
2322 am_storey
.AddParentStoreyReference (ec
, parent
.am_storey
);
2325 am_storey
.SetNestedStoryParent (ec
.CurrentAnonymousMethod
.Storey
);
2327 // TODO MemberCache: Review
2328 am_storey
.Mutator
= ec
.CurrentAnonymousMethod
.Storey
.Mutator
;
2331 am_storey
.CreateType ();
2332 if (am_storey
.Mutator
== null && ec
.CurrentTypeParameters
!= null)
2333 am_storey
.Mutator
= new TypeParameterMutator (ec
.CurrentTypeParameters
, am_storey
.CurrentTypeParameters
);
2335 am_storey
.DefineType ();
2336 am_storey
.ResolveTypeParameters ();
2337 am_storey
.Define ();
2338 am_storey
.Parent
.PartialContainer
.AddCompilerGeneratedClass (am_storey
);
2340 var ref_blocks
= am_storey
.ReferencesFromChildrenBlock
;
2341 if (ref_blocks
!= null) {
2342 foreach (ExplicitBlock ref_block
in ref_blocks
) {
2343 for (ExplicitBlock b
= ref_block
.Explicit
; b
!= this; b
= b
.Parent
.Explicit
) {
2344 if (b
.am_storey
!= null) {
2345 b
.am_storey
.AddParentStoreyReference (ec
, am_storey
);
2347 // Stop propagation inside same top block
2348 if (b
.Toplevel
== Toplevel
)
2353 b
.HasCapturedVariable
= true;
2362 public IKnownVariable
GetKnownVariable (string name
)
2364 if (known_variables
== null)
2368 known_variables
.TryGetValue (name
, out kw
);
2372 public bool HasCapturedThis
2374 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2375 get { return (flags & Flags.HasCapturedThis) != 0; }
2378 public bool HasCapturedVariable
2380 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2381 get { return (flags & Flags.HasCapturedVariable) != 0; }
2384 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2386 ExplicitBlock target
= (ExplicitBlock
) t
;
2387 target
.known_variables
= null;
2388 base.CloneTo (clonectx
, t
);
2392 public class ToplevelParameterInfo
: IKnownVariable
{
2393 public readonly ToplevelBlock Block
;
2394 public readonly int Index
;
2395 public VariableInfo VariableInfo
;
2397 Block IKnownVariable
.Block
{
2398 get { return Block; }
2400 public Parameter Parameter
{
2401 get { return Block.Parameters [Index]; }
2404 public TypeSpec ParameterType
{
2405 get { return Block.Parameters.Types [Index]; }
2408 public Location Location
{
2409 get { return Parameter.Location; }
2412 public ToplevelParameterInfo (ToplevelBlock block
, int idx
)
2420 // A toplevel block contains extra information, the split is done
2421 // only to separate information that would otherwise bloat the more
2422 // lightweight Block.
2424 // In particular, this was introduced when the support for Anonymous
2425 // Methods was implemented.
2427 public class ToplevelBlock
: ExplicitBlock
2430 // Block is converted to an expression
2432 sealed class BlockScopeExpression
: Expression
2435 readonly ToplevelBlock block
;
2437 public BlockScopeExpression (Expression child
, ToplevelBlock block
)
2443 public override Expression
CreateExpressionTree (ResolveContext ec
)
2445 throw new NotSupportedException ();
2448 protected override Expression
DoResolve (ResolveContext ec
)
2453 child
= child
.Resolve (ec
);
2457 eclass
= child
.eclass
;
2462 public override void Emit (EmitContext ec
)
2464 block
.EmitMeta (ec
);
2465 block
.EmitScopeInitializers (ec
);
2470 protected ParametersCompiled parameters
;
2471 protected ToplevelParameterInfo
[] parameter_info
;
2472 LocalInfo this_variable
;
2475 CompilerContext compiler
;
2477 public HoistedVariable HoistedThisVariable
;
2479 public bool Resolved
{
2486 // The parameters for the block.
2488 public ParametersCompiled Parameters
{
2489 get { return parameters; }
2492 public Report Report
{
2493 get { return compiler.Report; }
2496 public ToplevelBlock Container
{
2497 get { return Parent == null ? null : Parent.Toplevel; }
2500 public ToplevelBlock (CompilerContext ctx
, Block parent
, ParametersCompiled parameters
, Location start
) :
2501 this (ctx
, parent
, (Flags
) 0, parameters
, start
)
2505 public ToplevelBlock (CompilerContext ctx
, ParametersCompiled parameters
, Location start
) :
2506 this (ctx
, null, (Flags
) 0, parameters
, start
)
2510 ToplevelBlock (CompilerContext ctx
, Flags flags
, ParametersCompiled parameters
, Location start
) :
2511 this (ctx
, null, flags
, parameters
, start
)
2515 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2516 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2517 public ToplevelBlock (CompilerContext ctx
, Block parent
, Flags flags
, ParametersCompiled parameters
, Location start
) :
2518 base (null, flags
, start
, Location
.Null
)
2520 this.compiler
= ctx
;
2521 this.Toplevel
= this;
2523 this.parameters
= parameters
;
2524 this.Parent
= parent
;
2526 parent
.AddAnonymousChild (this);
2528 if (!this.parameters
.IsEmpty
)
2529 ProcessParameters ();
2532 public ToplevelBlock (CompilerContext ctx
, Location loc
)
2533 : this (ctx
, null, (Flags
) 0, ParametersCompiled
.EmptyReadOnlyParameters
, loc
)
2537 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2539 ToplevelBlock target
= (ToplevelBlock
) t
;
2540 base.CloneTo (clonectx
, t
);
2542 if (parameters
.Count
!= 0) {
2543 target
.parameter_info
= new ToplevelParameterInfo
[parameters
.Count
];
2544 for (int i
= 0; i
< parameters
.Count
; ++i
)
2545 target
.parameter_info
[i
] = new ToplevelParameterInfo (target
, i
);
2549 public bool CheckError158 (string name
, Location loc
)
2551 if (AnonymousChildren
!= null) {
2552 foreach (ToplevelBlock child
in AnonymousChildren
) {
2553 if (!child
.CheckError158 (name
, loc
))
2558 for (ToplevelBlock c
= Container
; c
!= null; c
= c
.Container
) {
2559 if (!c
.DoCheckError158 (name
, loc
))
2566 void ProcessParameters ()
2568 int n
= parameters
.Count
;
2569 parameter_info
= new ToplevelParameterInfo
[n
];
2570 ToplevelBlock top_parent
= Parent
== null ? null : Parent
.Toplevel
;
2571 for (int i
= 0; i
< n
; ++i
) {
2572 parameter_info
[i
] = new ToplevelParameterInfo (this, i
);
2574 Parameter p
= parameters
[i
];
2578 string name
= p
.Name
;
2579 if (CheckParentConflictName (top_parent
, name
, loc
))
2580 AddKnownVariable (name
, parameter_info
[i
]);
2583 // mark this block as "used" so that we create local declarations in a sub-block
2584 // FIXME: This appears to uncover a lot of bugs
2588 bool DoCheckError158 (string name
, Location loc
)
2590 LabeledStatement s
= LookupLabel (name
);
2592 Report
.SymbolRelatedToPreviousError (s
.loc
, s
.Name
);
2593 Error_158 (name
, loc
);
2600 public override Expression
CreateExpressionTree (ResolveContext ec
)
2602 if (statements
.Count
== 1) {
2603 Expression expr
= ((Statement
) statements
[0]).CreateExpressionTree (ec
);
2604 if (scope_initializers
!= null)
2605 expr
= new BlockScopeExpression (expr
, this);
2610 return base.CreateExpressionTree (ec
);
2614 // Reformats this block to be top-level iterator block
2616 public IteratorStorey
ChangeToIterator (Iterator iterator
, ToplevelBlock source
)
2620 // Creates block with original statements
2621 AddStatement (new IteratorStatement (iterator
, new Block (this, source
)));
2623 source
.statements
= new List
<Statement
> (1);
2624 source
.AddStatement (new Return (iterator
, iterator
.Location
));
2625 source
.IsIterator
= false;
2627 IteratorStorey iterator_storey
= new IteratorStorey (iterator
);
2628 source
.am_storey
= iterator_storey
;
2629 return iterator_storey
;
2633 // Returns a parameter reference expression for the given name,
2634 // or null if there is no such parameter
2636 public Expression
GetParameterReference (string name
, Location loc
)
2638 for (ToplevelBlock t
= this; t
!= null; t
= t
.Container
) {
2639 if (t
.parameters
.IsEmpty
)
2642 Expression expr
= t
.GetParameterReferenceExpression (name
, loc
);
2650 protected virtual Expression
GetParameterReferenceExpression (string name
, Location loc
)
2652 int idx
= parameters
.GetParameterIndexByName (name
);
2654 null : new ParameterReference (parameter_info
[idx
], loc
);
2657 public ToplevelBlock
CheckParameterNameConflict (string name
)
2659 for (ToplevelBlock t
= this; t
!= null; t
= t
.Container
) {
2660 if (t
.HasParameterWithName (name
))
2667 protected virtual bool HasParameterWithName (string name
)
2669 return parameters
.GetParameterIndexByName (name
) >= 0;
2673 // Returns the "this" instance variable of this block.
2674 // See AddThisVariable() for more information.
2676 public LocalInfo ThisVariable
{
2677 get { return this_variable; }
2681 // This is used by non-static `struct' constructors which do not have an
2682 // initializer - in this case, the constructor must initialize all of the
2683 // struct's fields. To do this, we add a "this" variable and use the flow
2684 // analysis code to ensure that it's been fully initialized before control
2685 // leaves the constructor.
2687 public LocalInfo
AddThisVariable (TypeContainer ds
, Location l
)
2689 if (this_variable
== null) {
2690 this_variable
= new LocalInfo (ds
, this, l
);
2691 this_variable
.Used
= true;
2692 this_variable
.IsThis
= true;
2694 Variables
.Add ("this", this_variable
);
2697 return this_variable
;
2700 public bool IsIterator
{
2701 get { return (flags & Flags.IsIterator) != 0; }
2702 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2706 // Block has been converted to expression tree
2708 public bool IsExpressionTree
{
2709 get { return (flags & Flags.IsExpressionTree) != 0; }
2712 public bool IsThisAssigned (BlockContext ec
)
2714 return this_variable
== null || this_variable
.IsThisAssigned (ec
, this);
2717 public bool Resolve (FlowBranching parent
, BlockContext rc
, ParametersCompiled ip
, IMethodData md
)
2724 if (rc
.HasSet (ResolveContext
.Options
.ExpressionTreeConversion
))
2725 flags
|= Flags
.IsExpressionTree
;
2728 if (!ResolveMeta (rc
, ip
))
2731 using (rc
.With (ResolveContext
.Options
.DoFlowAnalysis
, true)) {
2732 FlowBranchingToplevel top_level
= rc
.StartFlowBranching (this, parent
);
2737 unreachable
= top_level
.End ();
2739 } catch (Exception e
) {
2740 if (rc
.CurrentBlock
!= null) {
2741 rc
.Report
.Error (584, rc
.CurrentBlock
.StartLocation
, "Internal compiler error: {0}", e
.Message
);
2743 rc
.Report
.Error (587, "Internal compiler error: {0}", e
.Message
);
2746 if (Report
.DebugFlags
> 0)
2750 if (rc
.ReturnType
!= TypeManager
.void_type
&& !unreachable
) {
2751 if (rc
.CurrentAnonymousMethod
== null) {
2752 rc
.Report
.Error (161, md
.Location
, "`{0}': not all code paths return a value", md
.GetSignatureForError ());
2754 } else if (!rc
.CurrentAnonymousMethod
.IsIterator
) {
2755 rc
.Report
.Error (1643, rc
.CurrentAnonymousMethod
.Location
, "Not all code paths return a value in anonymous method of type `{0}'",
2756 rc
.CurrentAnonymousMethod
.GetSignatureForError ());
2764 bool ResolveMeta (BlockContext ec
, ParametersCompiled ip
)
2766 int errors
= ec
.Report
.Errors
;
2767 int orig_count
= parameters
.Count
;
2772 // Assert: orig_count != parameter.Count => orig_count == 0
2773 if (orig_count
!= 0 && orig_count
!= parameters
.Count
)
2774 throw new InternalErrorException ("parameter information mismatch");
2776 int offset
= Parent
== null ? 0 : Parent
.AssignableSlots
;
2778 for (int i
= 0; i
< orig_count
; ++i
) {
2779 Parameter
.Modifier mod
= parameters
.FixedParameters
[i
].ModFlags
;
2781 if ((mod
& Parameter
.Modifier
.OUT
) != Parameter
.Modifier
.OUT
)
2784 VariableInfo vi
= new VariableInfo (ip
, i
, offset
);
2785 parameter_info
[i
].VariableInfo
= vi
;
2786 offset
+= vi
.Length
;
2789 ResolveMeta (ec
, offset
);
2791 return ec
.Report
.Errors
== errors
;
2795 // Check whether all `out' parameters have been assigned.
2797 public void CheckOutParameters (FlowBranching
.UsageVector vector
, Location loc
)
2799 if (vector
.IsUnreachable
)
2802 int n
= parameter_info
== null ? 0 : parameter_info
.Length
;
2804 for (int i
= 0; i
< n
; i
++) {
2805 VariableInfo
var = parameter_info
[i
].VariableInfo
;
2810 if (vector
.IsAssigned (var, false))
2813 Report
.Error (177, loc
, "The out parameter `{0}' must be assigned to before control leaves the current method",
2818 public override void Emit (EmitContext ec
)
2820 if (Report
.Errors
> 0)
2828 if (ec
.HasReturnLabel
)
2829 ec
.ReturnLabel
= ec
.DefineLabel ();
2833 ec
.Mark (EndLocation
);
2835 if (ec
.HasReturnLabel
)
2836 ec
.MarkLabel (ec
.ReturnLabel
);
2838 if (ec
.return_value
!= null) {
2839 ec
.Emit (OpCodes
.Ldloc
, ec
.return_value
);
2840 ec
.Emit (OpCodes
.Ret
);
2843 // If `HasReturnLabel' is set, then we already emitted a
2844 // jump to the end of the method, so we must emit a `ret'
2847 // Unfortunately, System.Reflection.Emit automatically emits
2848 // a leave to the end of a finally block. This is a problem
2849 // if no code is following the try/finally block since we may
2850 // jump to a point after the end of the method.
2851 // As a workaround, we're always creating a return label in
2855 if (ec
.HasReturnLabel
|| !unreachable
) {
2856 if (ec
.ReturnType
!= TypeManager
.void_type
)
2857 ec
.Emit (OpCodes
.Ldloc
, ec
.TemporaryReturn ());
2858 ec
.Emit (OpCodes
.Ret
);
2863 } catch (Exception e
){
2864 Console
.WriteLine ("Exception caught by the compiler while emitting:");
2865 Console
.WriteLine (" Block that caused the problem begin at: " + block
.loc
);
2867 Console
.WriteLine (e
.GetType ().FullName
+ ": " + e
.Message
);
2873 public override void EmitMeta (EmitContext ec
)
2875 // Avoid declaring an IL variable for this_variable since it is not accessed
2876 // from the generated IL
2877 if (this_variable
!= null)
2878 Variables
.Remove ("this");
2882 protected override void EmitSymbolInfo (EmitContext ec
)
2884 AnonymousExpression ae
= ec
.CurrentAnonymousMethod
;
2885 if ((ae
!= null) && (ae
.Storey
!= null))
2886 SymbolWriter
.DefineScopeVariable (ae
.Storey
.ID
);
2888 base.EmitSymbolInfo (ec
);
2892 public class SwitchLabel
{
2899 Label il_label_code
;
2900 bool il_label_code_set
;
2902 public static readonly object NullStringCase
= new object ();
2905 // if expr == null, then it is the default case.
2907 public SwitchLabel (Expression expr
, Location l
)
2913 public Expression Label
{
2919 public Location Location
{
2923 public object Converted
{
2929 public Label
GetILLabel (EmitContext ec
)
2932 il_label
= ec
.DefineLabel ();
2933 il_label_set
= true;
2938 public Label
GetILLabelCode (EmitContext ec
)
2940 if (!il_label_code_set
){
2941 il_label_code
= ec
.DefineLabel ();
2942 il_label_code_set
= true;
2944 return il_label_code
;
2948 // Resolves the expression, reduces it to a literal if possible
2949 // and then converts it to the requested type.
2951 public bool ResolveAndReduce (ResolveContext ec
, TypeSpec required_type
, bool allow_nullable
)
2953 Expression e
= label
.Resolve (ec
);
2958 Constant c
= e
as Constant
;
2960 ec
.Report
.Error (150, loc
, "A constant value is expected");
2964 if (required_type
== TypeManager
.string_type
&& c
.GetValue () == null) {
2965 converted
= NullStringCase
;
2969 if (allow_nullable
&& c
.GetValue () == null) {
2970 converted
= NullStringCase
;
2974 c
= c
.ImplicitConversionRequired (ec
, required_type
, loc
);
2978 converted
= c
.GetValue ();
2982 public void Error_AlreadyOccurs (ResolveContext ec
, TypeSpec switch_type
, SwitchLabel collision_with
)
2985 if (converted
== null)
2987 else if (converted
== NullStringCase
)
2990 label
= converted
.ToString ();
2992 ec
.Report
.SymbolRelatedToPreviousError (collision_with
.loc
, null);
2993 ec
.Report
.Error (152, loc
, "The label `case {0}:' already occurs in this switch statement", label
);
2996 public SwitchLabel
Clone (CloneContext clonectx
)
2998 return new SwitchLabel (label
.Clone (clonectx
), loc
);
3002 public class SwitchSection
{
3003 // An array of SwitchLabels.
3004 public readonly List
<SwitchLabel
> Labels
;
3005 public readonly Block Block
;
3007 public SwitchSection (List
<SwitchLabel
> labels
, Block block
)
3013 public SwitchSection
Clone (CloneContext clonectx
)
3015 var cloned_labels
= new List
<SwitchLabel
> ();
3017 foreach (SwitchLabel sl
in cloned_labels
)
3018 cloned_labels
.Add (sl
.Clone (clonectx
));
3020 return new SwitchSection (cloned_labels
, clonectx
.LookupBlock (Block
));
3024 public class Switch
: Statement
{
3025 public List
<SwitchSection
> Sections
;
3026 public Expression Expr
;
3029 /// Maps constants whose type type SwitchType to their SwitchLabels.
3031 public IDictionary
<object, SwitchLabel
> Elements
;
3034 /// The governing switch type
3036 public TypeSpec SwitchType
;
3041 Label default_target
;
3043 Expression new_expr
;
3046 SwitchSection constant_section
;
3047 SwitchSection default_section
;
3049 ExpressionStatement string_dictionary
;
3050 FieldExpr switch_cache_field
;
3051 static int unique_counter
;
3054 // Nullable Types support
3056 Nullable
.Unwrap unwrap
;
3058 protected bool HaveUnwrap
{
3059 get { return unwrap != null; }
3063 // The types allowed to be implicitly cast from
3064 // on the governing type
3066 static TypeSpec
[] allowed_types
;
3068 public Switch (Expression e
, List
<SwitchSection
> sects
, Location l
)
3075 public bool GotDefault
{
3077 return default_section
!= null;
3081 public Label DefaultTarget
{
3083 return default_target
;
3088 // Determines the governing type for a switch. The returned
3089 // expression might be the expression from the switch, or an
3090 // expression that includes any potential conversions to the
3091 // integral types or to string.
3093 Expression
SwitchGoverningType (ResolveContext ec
, Expression expr
)
3095 TypeSpec t
= expr
.Type
;
3097 if (t
== TypeManager
.byte_type
||
3098 t
== TypeManager
.sbyte_type
||
3099 t
== TypeManager
.ushort_type
||
3100 t
== TypeManager
.short_type
||
3101 t
== TypeManager
.uint32_type
||
3102 t
== TypeManager
.int32_type
||
3103 t
== TypeManager
.uint64_type
||
3104 t
== TypeManager
.int64_type
||
3105 t
== TypeManager
.char_type
||
3106 t
== TypeManager
.string_type
||
3107 t
== TypeManager
.bool_type
||
3108 TypeManager
.IsEnumType (t
))
3111 if (allowed_types
== null){
3112 allowed_types
= new TypeSpec
[] {
3113 TypeManager
.sbyte_type
,
3114 TypeManager
.byte_type
,
3115 TypeManager
.short_type
,
3116 TypeManager
.ushort_type
,
3117 TypeManager
.int32_type
,
3118 TypeManager
.uint32_type
,
3119 TypeManager
.int64_type
,
3120 TypeManager
.uint64_type
,
3121 TypeManager
.char_type
,
3122 TypeManager
.string_type
3127 // Try to find a *user* defined implicit conversion.
3129 // If there is no implicit conversion, or if there are multiple
3130 // conversions, we have to report an error
3132 Expression converted
= null;
3133 foreach (TypeSpec tt
in allowed_types
){
3136 e
= Convert
.ImplicitUserConversion (ec
, expr
, tt
, loc
);
3141 // Ignore over-worked ImplicitUserConversions that do
3142 // an implicit conversion in addition to the user conversion.
3144 if (!(e
is UserCast
))
3147 if (converted
!= null){
3148 ec
.Report
.ExtraInformation (loc
, "(Ambiguous implicit user defined conversion in previous ");
3158 // Performs the basic sanity checks on the switch statement
3159 // (looks for duplicate keys and non-constant expressions).
3161 // It also returns a hashtable with the keys that we will later
3162 // use to compute the switch tables
3164 bool CheckSwitch (ResolveContext ec
)
3167 Elements
= new Dictionary
<object, SwitchLabel
> ();
3169 foreach (SwitchSection ss
in Sections
){
3170 foreach (SwitchLabel sl
in ss
.Labels
){
3171 if (sl
.Label
== null){
3172 if (default_section
!= null){
3173 sl
.Error_AlreadyOccurs (ec
, SwitchType
, (SwitchLabel
)default_section
.Labels
[0]);
3176 default_section
= ss
;
3180 if (!sl
.ResolveAndReduce (ec
, SwitchType
, HaveUnwrap
)) {
3185 object key
= sl
.Converted
;
3186 if (key
== SwitchLabel
.NullStringCase
)
3187 has_null_case
= true;
3190 Elements
.Add (key
, sl
);
3191 } catch (ArgumentException
) {
3192 sl
.Error_AlreadyOccurs (ec
, SwitchType
, Elements
[key
]);
3200 void EmitObjectInteger (EmitContext ec
, object k
)
3203 ec
.EmitInt ((int) k
);
3204 else if (k
is Constant
) {
3205 EmitObjectInteger (ec
, ((Constant
) k
).GetValue ());
3208 ec
.EmitInt (unchecked ((int) (uint) k
));
3211 if ((long) k
>= int.MinValue
&& (long) k
<= int.MaxValue
)
3213 ec
.EmitInt ((int) (long) k
);
3214 ec
.Emit (OpCodes
.Conv_I8
);
3217 ec
.EmitLong ((long) k
);
3219 else if (k
is ulong)
3221 ulong ul
= (ulong) k
;
3224 ec
.EmitInt (unchecked ((int) ul
));
3225 ec
.Emit (OpCodes
.Conv_U8
);
3229 ec
.EmitLong (unchecked ((long) ul
));
3233 ec
.EmitInt ((int) ((char) k
));
3234 else if (k
is sbyte)
3235 ec
.EmitInt ((int) ((sbyte) k
));
3237 ec
.EmitInt ((int) ((byte) k
));
3238 else if (k
is short)
3239 ec
.EmitInt ((int) ((short) k
));
3240 else if (k
is ushort)
3241 ec
.EmitInt ((int) ((ushort) k
));
3243 ec
.EmitInt (((bool) k
) ? 1 : 0);
3245 throw new Exception ("Unhandled case");
3248 // structure used to hold blocks of keys while calculating table switch
3249 class KeyBlock
: IComparable
3251 public KeyBlock (long _first
)
3253 first
= last
= _first
;
3257 public List
<object> element_keys
;
3258 // how many items are in the bucket
3259 public int Size
= 1;
3262 get { return (int) (last - first + 1); }
3264 public static long TotalLength (KeyBlock kb_first
, KeyBlock kb_last
)
3266 return kb_last
.last
- kb_first
.first
+ 1;
3268 public int CompareTo (object obj
)
3270 KeyBlock kb
= (KeyBlock
) obj
;
3271 int nLength
= Length
;
3272 int nLengthOther
= kb
.Length
;
3273 if (nLengthOther
== nLength
)
3274 return (int) (kb
.first
- first
);
3275 return nLength
- nLengthOther
;
3280 /// This method emits code for a lookup-based switch statement (non-string)
3281 /// Basically it groups the cases into blocks that are at least half full,
3282 /// and then spits out individual lookup opcodes for each block.
3283 /// It emits the longest blocks first, and short blocks are just
3284 /// handled with direct compares.
3286 /// <param name="ec"></param>
3287 /// <param name="val"></param>
3288 /// <returns></returns>
3289 void TableSwitchEmit (EmitContext ec
, Expression val
)
3291 int element_count
= Elements
.Count
;
3292 object [] element_keys
= new object [element_count
];
3293 Elements
.Keys
.CopyTo (element_keys
, 0);
3294 Array
.Sort (element_keys
);
3296 // initialize the block list with one element per key
3297 var key_blocks
= new List
<KeyBlock
> (element_count
);
3298 foreach (object key
in element_keys
)
3299 key_blocks
.Add (new KeyBlock (System
.Convert
.ToInt64 (key
)));
3301 KeyBlock current_kb
;
3302 // iteratively merge the blocks while they are at least half full
3303 // there's probably a really cool way to do this with a tree...
3304 while (key_blocks
.Count
> 1)
3306 var key_blocks_new
= new List
<KeyBlock
> ();
3307 current_kb
= (KeyBlock
) key_blocks
[0];
3308 for (int ikb
= 1; ikb
< key_blocks
.Count
; ikb
++)
3310 KeyBlock kb
= (KeyBlock
) key_blocks
[ikb
];
3311 if ((current_kb
.Size
+ kb
.Size
) * 2 >= KeyBlock
.TotalLength (current_kb
, kb
))
3314 current_kb
.last
= kb
.last
;
3315 current_kb
.Size
+= kb
.Size
;
3319 // start a new block
3320 key_blocks_new
.Add (current_kb
);
3324 key_blocks_new
.Add (current_kb
);
3325 if (key_blocks
.Count
== key_blocks_new
.Count
)
3327 key_blocks
= key_blocks_new
;
3330 // initialize the key lists
3331 foreach (KeyBlock kb
in key_blocks
)
3332 kb
.element_keys
= new List
<object> ();
3334 // fill the key lists
3336 if (key_blocks
.Count
> 0) {
3337 current_kb
= (KeyBlock
) key_blocks
[0];
3338 foreach (object key
in element_keys
)
3340 bool next_block
= (key
is UInt64
) ? (ulong) key
> (ulong) current_kb
.last
:
3341 System
.Convert
.ToInt64 (key
) > current_kb
.last
;
3343 current_kb
= (KeyBlock
) key_blocks
[++iBlockCurr
];
3344 current_kb
.element_keys
.Add (key
);
3348 // sort the blocks so we can tackle the largest ones first
3351 // okay now we can start...
3352 Label lbl_end
= ec
.DefineLabel (); // at the end ;-)
3353 Label lbl_default
= default_target
;
3355 Type type_keys
= null;
3356 if (element_keys
.Length
> 0)
3357 type_keys
= element_keys
[0].GetType (); // used for conversions
3359 TypeSpec compare_type
;
3361 if (TypeManager
.IsEnumType (SwitchType
))
3362 compare_type
= EnumSpec
.GetUnderlyingType (SwitchType
);
3364 compare_type
= SwitchType
;
3366 for (int iBlock
= key_blocks
.Count
- 1; iBlock
>= 0; --iBlock
)
3368 KeyBlock kb
= ((KeyBlock
) key_blocks
[iBlock
]);
3369 lbl_default
= (iBlock
== 0) ? default_target
: ec
.DefineLabel ();
3372 foreach (object key
in kb
.element_keys
) {
3373 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
3374 if (key
is int && (int) key
== 0) {
3375 val
.EmitBranchable (ec
, sl
.GetILLabel (ec
), false);
3378 EmitObjectInteger (ec
, key
);
3379 ec
.Emit (OpCodes
.Beq
, sl
.GetILLabel (ec
));
3385 // TODO: if all the keys in the block are the same and there are
3386 // no gaps/defaults then just use a range-check.
3387 if (compare_type
== TypeManager
.int64_type
||
3388 compare_type
== TypeManager
.uint64_type
)
3390 // TODO: optimize constant/I4 cases
3392 // check block range (could be > 2^31)
3394 EmitObjectInteger (ec
, System
.Convert
.ChangeType (kb
.first
, type_keys
));
3395 ec
.Emit (OpCodes
.Blt
, lbl_default
);
3397 EmitObjectInteger (ec
, System
.Convert
.ChangeType (kb
.last
, type_keys
));
3398 ec
.Emit (OpCodes
.Bgt
, lbl_default
);
3404 EmitObjectInteger (ec
, System
.Convert
.ChangeType (kb
.first
, type_keys
));
3405 ec
.Emit (OpCodes
.Sub
);
3407 ec
.Emit (OpCodes
.Conv_I4
); // assumes < 2^31 labels!
3413 int first
= (int) kb
.first
;
3417 ec
.Emit (OpCodes
.Sub
);
3421 ec
.EmitInt (-first
);
3422 ec
.Emit (OpCodes
.Add
);
3426 // first, build the list of labels for the switch
3428 int cJumps
= kb
.Length
;
3429 Label
[] switch_labels
= new Label
[cJumps
];
3430 for (int iJump
= 0; iJump
< cJumps
; iJump
++)
3432 object key
= kb
.element_keys
[iKey
];
3433 if (System
.Convert
.ToInt64 (key
) == kb
.first
+ iJump
)
3435 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
3436 switch_labels
[iJump
] = sl
.GetILLabel (ec
);
3440 switch_labels
[iJump
] = lbl_default
;
3442 // emit the switch opcode
3443 ec
.Emit (OpCodes
.Switch
, switch_labels
);
3446 // mark the default for this block
3448 ec
.MarkLabel (lbl_default
);
3451 // TODO: find the default case and emit it here,
3452 // to prevent having to do the following jump.
3453 // make sure to mark other labels in the default section
3455 // the last default just goes to the end
3456 if (element_keys
.Length
> 0)
3457 ec
.Emit (OpCodes
.Br
, lbl_default
);
3459 // now emit the code for the sections
3460 bool found_default
= false;
3462 foreach (SwitchSection ss
in Sections
) {
3463 foreach (SwitchLabel sl
in ss
.Labels
) {
3464 if (sl
.Converted
== SwitchLabel
.NullStringCase
) {
3465 ec
.MarkLabel (null_target
);
3466 } else if (sl
.Label
== null) {
3467 ec
.MarkLabel (lbl_default
);
3468 found_default
= true;
3470 ec
.MarkLabel (null_target
);
3472 ec
.MarkLabel (sl
.GetILLabel (ec
));
3473 ec
.MarkLabel (sl
.GetILLabelCode (ec
));
3478 if (!found_default
) {
3479 ec
.MarkLabel (lbl_default
);
3480 if (!has_null_case
) {
3481 ec
.MarkLabel (null_target
);
3485 ec
.MarkLabel (lbl_end
);
3488 SwitchSection
FindSection (SwitchLabel label
)
3490 foreach (SwitchSection ss
in Sections
){
3491 foreach (SwitchLabel sl
in ss
.Labels
){
3500 public static void Reset ()
3503 allowed_types
= null;
3506 public override bool Resolve (BlockContext ec
)
3508 Expr
= Expr
.Resolve (ec
);
3512 new_expr
= SwitchGoverningType (ec
, Expr
);
3514 if ((new_expr
== null) && TypeManager
.IsNullableType (Expr
.Type
)) {
3515 unwrap
= Nullable
.Unwrap
.Create (Expr
, false);
3519 new_expr
= SwitchGoverningType (ec
, unwrap
);
3522 if (new_expr
== null){
3523 ec
.Report
.Error (151, loc
,
3524 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3525 TypeManager
.CSharpName (Expr
.Type
));
3530 SwitchType
= new_expr
.Type
;
3532 if (RootContext
.Version
== LanguageVersion
.ISO_1
&& SwitchType
== TypeManager
.bool_type
) {
3533 ec
.Report
.FeatureIsNotAvailable (loc
, "switch expression of boolean type");
3537 if (!CheckSwitch (ec
))
3541 Elements
.Remove (SwitchLabel
.NullStringCase
);
3543 Switch old_switch
= ec
.Switch
;
3545 ec
.Switch
.SwitchType
= SwitchType
;
3547 Report
.Debug (1, "START OF SWITCH BLOCK", loc
, ec
.CurrentBranching
);
3548 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Switch
, loc
);
3550 var constant
= new_expr
as Constant
;
3551 if (constant
!= null) {
3553 object key
= constant
.GetValue ();
3555 if (Elements
.TryGetValue (key
, out label
))
3556 constant_section
= FindSection (label
);
3558 if (constant_section
== null)
3559 constant_section
= default_section
;
3564 foreach (SwitchSection ss
in Sections
){
3566 ec
.CurrentBranching
.CreateSibling (
3567 null, FlowBranching
.SiblingType
.SwitchSection
);
3571 if (is_constant
&& (ss
!= constant_section
)) {
3572 // If we're a constant switch, we're only emitting
3573 // one single section - mark all the others as
3575 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
3576 if (!ss
.Block
.ResolveUnreachable (ec
, true)) {
3580 if (!ss
.Block
.Resolve (ec
))
3585 if (default_section
== null)
3586 ec
.CurrentBranching
.CreateSibling (
3587 null, FlowBranching
.SiblingType
.SwitchSection
);
3589 ec
.EndFlowBranching ();
3590 ec
.Switch
= old_switch
;
3592 Report
.Debug (1, "END OF SWITCH BLOCK", loc
, ec
.CurrentBranching
);
3597 if (SwitchType
== TypeManager
.string_type
&& !is_constant
) {
3598 // TODO: Optimize single case, and single+default case
3599 ResolveStringSwitchMap (ec
);
3605 void ResolveStringSwitchMap (ResolveContext ec
)
3607 FullNamedExpression string_dictionary_type
;
3608 if (TypeManager
.generic_ienumerable_type
!= null) {
3609 MemberAccess system_collections_generic
= new MemberAccess (new MemberAccess (
3610 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "System", loc
), "Collections", loc
), "Generic", loc
);
3612 string_dictionary_type
= new MemberAccess (system_collections_generic
, "Dictionary",
3614 new TypeExpression (TypeManager
.string_type
, loc
),
3615 new TypeExpression (TypeManager
.int32_type
, loc
)), loc
);
3617 MemberAccess system_collections_generic
= new MemberAccess (
3618 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "System", loc
), "Collections", loc
);
3620 string_dictionary_type
= new MemberAccess (system_collections_generic
, "Hashtable", loc
);
3623 var ctype
= ec
.CurrentMemberDefinition
.Parent
.PartialContainer
;
3624 Field field
= new Field (ctype
, string_dictionary_type
,
3625 Modifiers
.STATIC
| Modifiers
.PRIVATE
| Modifiers
.COMPILER_GENERATED
,
3626 new MemberName (CompilerGeneratedClass
.MakeName (null, "f", "switch$map", unique_counter
++), loc
), null);
3627 if (!field
.Define ())
3629 ctype
.AddField (field
);
3631 var init
= new List
<Expression
> ();
3634 string value = null;
3635 foreach (SwitchSection section
in Sections
) {
3636 int last_count
= init
.Count
;
3637 foreach (SwitchLabel sl
in section
.Labels
) {
3638 if (sl
.Label
== null || sl
.Converted
== SwitchLabel
.NullStringCase
)
3641 value = (string) sl
.Converted
;
3642 var init_args
= new List
<Expression
> (2);
3643 init_args
.Add (new StringLiteral (value, sl
.Location
));
3644 init_args
.Add (new IntConstant (counter
, loc
));
3645 init
.Add (new CollectionElementInitializer (init_args
, loc
));
3649 // Don't add empty sections
3651 if (last_count
== init
.Count
)
3654 Elements
.Add (counter
, section
.Labels
[0]);
3658 Arguments args
= new Arguments (1);
3659 args
.Add (new Argument (new IntConstant (init
.Count
, loc
)));
3660 Expression initializer
= new NewInitialize (string_dictionary_type
, args
,
3661 new CollectionOrObjectInitializers (init
, loc
), loc
);
3663 switch_cache_field
= new FieldExpr (field
, loc
);
3664 string_dictionary
= new SimpleAssign (switch_cache_field
, initializer
.Resolve (ec
));
3667 void DoEmitStringSwitch (LocalTemporary
value, EmitContext ec
)
3669 Label l_initialized
= ec
.DefineLabel ();
3672 // Skip initialization when value is null
3674 value.EmitBranchable (ec
, null_target
, false);
3677 // Check if string dictionary is initialized and initialize
3679 switch_cache_field
.EmitBranchable (ec
, l_initialized
, true);
3680 string_dictionary
.EmitStatement (ec
);
3681 ec
.MarkLabel (l_initialized
);
3683 LocalTemporary string_switch_variable
= new LocalTemporary (TypeManager
.int32_type
);
3685 ResolveContext rc
= new ResolveContext (ec
.MemberContext
);
3687 if (TypeManager
.generic_ienumerable_type
!= null) {
3688 Arguments get_value_args
= new Arguments (2);
3689 get_value_args
.Add (new Argument (value));
3690 get_value_args
.Add (new Argument (string_switch_variable
, Argument
.AType
.Out
));
3691 Expression get_item
= new Invocation (new MemberAccess (switch_cache_field
, "TryGetValue", loc
), get_value_args
).Resolve (rc
);
3692 if (get_item
== null)
3696 // A value was not found, go to default case
3698 get_item
.EmitBranchable (ec
, default_target
, false);
3700 Arguments get_value_args
= new Arguments (1);
3701 get_value_args
.Add (new Argument (value));
3703 Expression get_item
= new IndexerAccess (new ElementAccess (switch_cache_field
, get_value_args
), loc
).Resolve (rc
);
3704 if (get_item
== null)
3707 LocalTemporary get_item_object
= new LocalTemporary (TypeManager
.object_type
);
3708 get_item_object
.EmitAssign (ec
, get_item
, true, false);
3709 ec
.Emit (OpCodes
.Brfalse
, default_target
);
3711 ExpressionStatement get_item_int
= (ExpressionStatement
) new SimpleAssign (string_switch_variable
,
3712 new Cast (new TypeExpression (TypeManager
.int32_type
, loc
), get_item_object
, loc
)).Resolve (rc
);
3714 get_item_int
.EmitStatement (ec
);
3715 get_item_object
.Release (ec
);
3718 TableSwitchEmit (ec
, string_switch_variable
);
3719 string_switch_variable
.Release (ec
);
3722 protected override void DoEmit (EmitContext ec
)
3724 default_target
= ec
.DefineLabel ();
3725 null_target
= ec
.DefineLabel ();
3727 // Store variable for comparission purposes
3728 // TODO: Don't duplicate non-captured VariableReference
3729 LocalTemporary
value;
3731 value = new LocalTemporary (SwitchType
);
3732 unwrap
.EmitCheck (ec
);
3733 ec
.Emit (OpCodes
.Brfalse
, null_target
);
3736 } else if (!is_constant
) {
3737 value = new LocalTemporary (SwitchType
);
3744 // Setup the codegen context
3746 Label old_end
= ec
.LoopEnd
;
3747 Switch old_switch
= ec
.Switch
;
3749 ec
.LoopEnd
= ec
.DefineLabel ();
3754 if (constant_section
!= null)
3755 constant_section
.Block
.Emit (ec
);
3756 } else if (string_dictionary
!= null) {
3757 DoEmitStringSwitch (value, ec
);
3759 TableSwitchEmit (ec
, value);
3765 // Restore context state.
3766 ec
.MarkLabel (ec
.LoopEnd
);
3769 // Restore the previous context
3771 ec
.LoopEnd
= old_end
;
3772 ec
.Switch
= old_switch
;
3775 protected override void CloneTo (CloneContext clonectx
, Statement t
)
3777 Switch target
= (Switch
) t
;
3779 target
.Expr
= Expr
.Clone (clonectx
);
3780 target
.Sections
= new List
<SwitchSection
> ();
3781 foreach (SwitchSection ss
in Sections
){
3782 target
.Sections
.Add (ss
.Clone (clonectx
));
3787 // A place where execution can restart in an iterator
3788 public abstract class ResumableStatement
: Statement
3791 protected Label resume_point
;
3793 public Label
PrepareForEmit (EmitContext ec
)
3797 resume_point
= ec
.DefineLabel ();
3799 return resume_point
;
3802 public virtual Label
PrepareForDispose (EmitContext ec
, Label end
)
3806 public virtual void EmitForDispose (EmitContext ec
, Iterator iterator
, Label end
, bool have_dispatcher
)
3811 // Base class for statements that are implemented in terms of try...finally
3812 public abstract class ExceptionStatement
: ResumableStatement
3816 List
<ResumableStatement
> resume_points
;
3817 int first_resume_pc
;
3819 protected abstract void EmitPreTryBody (EmitContext ec
);
3820 protected abstract void EmitTryBody (EmitContext ec
);
3821 protected abstract void EmitFinallyBody (EmitContext ec
);
3823 protected sealed override void DoEmit (EmitContext ec
)
3825 EmitPreTryBody (ec
);
3827 if (resume_points
!= null) {
3828 ec
.EmitInt ((int) Iterator
.State
.Running
);
3829 ec
.Emit (OpCodes
.Stloc
, iter
.CurrentPC
);
3832 ec
.BeginExceptionBlock ();
3834 if (resume_points
!= null) {
3835 ec
.MarkLabel (resume_point
);
3837 // For normal control flow, we want to fall-through the Switch
3838 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3839 ec
.Emit (OpCodes
.Ldloc
, iter
.CurrentPC
);
3840 ec
.EmitInt (first_resume_pc
);
3841 ec
.Emit (OpCodes
.Sub
);
3843 Label
[] labels
= new Label
[resume_points
.Count
];
3844 for (int i
= 0; i
< resume_points
.Count
; ++i
)
3845 labels
[i
] = ((ResumableStatement
) resume_points
[i
]).PrepareForEmit (ec
);
3846 ec
.Emit (OpCodes
.Switch
, labels
);
3851 ec
.BeginFinallyBlock ();
3853 Label start_finally
= ec
.DefineLabel ();
3854 if (resume_points
!= null) {
3855 ec
.Emit (OpCodes
.Ldloc
, iter
.SkipFinally
);
3856 ec
.Emit (OpCodes
.Brfalse_S
, start_finally
);
3857 ec
.Emit (OpCodes
.Endfinally
);
3860 ec
.MarkLabel (start_finally
);
3861 EmitFinallyBody (ec
);
3863 ec
.EndExceptionBlock ();
3866 public void SomeCodeFollows ()
3868 code_follows
= true;
3871 public override bool Resolve (BlockContext ec
)
3873 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3874 // So, ensure there's some IL code after this statement.
3875 if (!code_follows
&& resume_points
== null && ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
)
3876 ec
.NeedReturnLabel ();
3878 iter
= ec
.CurrentIterator
;
3882 public void AddResumePoint (ResumableStatement stmt
, int pc
)
3884 if (resume_points
== null) {
3885 resume_points
= new List
<ResumableStatement
> ();
3886 first_resume_pc
= pc
;
3889 if (pc
!= first_resume_pc
+ resume_points
.Count
)
3890 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3892 resume_points
.Add (stmt
);
3895 Label dispose_try_block
;
3896 bool prepared_for_dispose
, emitted_dispose
;
3897 public override Label
PrepareForDispose (EmitContext ec
, Label end
)
3899 if (!prepared_for_dispose
) {
3900 prepared_for_dispose
= true;
3901 dispose_try_block
= ec
.DefineLabel ();
3903 return dispose_try_block
;
3906 public override void EmitForDispose (EmitContext ec
, Iterator iterator
, Label end
, bool have_dispatcher
)
3908 if (emitted_dispose
)
3911 emitted_dispose
= true;
3913 Label end_of_try
= ec
.DefineLabel ();
3915 // Ensure that the only way we can get into this code is through a dispatcher
3916 if (have_dispatcher
)
3917 ec
.Emit (OpCodes
.Br
, end
);
3919 ec
.BeginExceptionBlock ();
3921 ec
.MarkLabel (dispose_try_block
);
3923 Label
[] labels
= null;
3924 for (int i
= 0; i
< resume_points
.Count
; ++i
) {
3925 ResumableStatement s
= (ResumableStatement
) resume_points
[i
];
3926 Label ret
= s
.PrepareForDispose (ec
, end_of_try
);
3927 if (ret
.Equals (end_of_try
) && labels
== null)
3929 if (labels
== null) {
3930 labels
= new Label
[resume_points
.Count
];
3931 for (int j
= 0; j
< i
; ++j
)
3932 labels
[j
] = end_of_try
;
3937 if (labels
!= null) {
3939 for (j
= 1; j
< labels
.Length
; ++j
)
3940 if (!labels
[0].Equals (labels
[j
]))
3942 bool emit_dispatcher
= j
< labels
.Length
;
3944 if (emit_dispatcher
) {
3945 //SymbolWriter.StartIteratorDispatcher (ec.ig);
3946 ec
.Emit (OpCodes
.Ldloc
, iterator
.CurrentPC
);
3947 ec
.EmitInt (first_resume_pc
);
3948 ec
.Emit (OpCodes
.Sub
);
3949 ec
.Emit (OpCodes
.Switch
, labels
);
3950 //SymbolWriter.EndIteratorDispatcher (ec.ig);
3953 foreach (ResumableStatement s
in resume_points
)
3954 s
.EmitForDispose (ec
, iterator
, end_of_try
, emit_dispatcher
);
3957 ec
.MarkLabel (end_of_try
);
3959 ec
.BeginFinallyBlock ();
3961 EmitFinallyBody (ec
);
3963 ec
.EndExceptionBlock ();
3967 public class Lock
: ExceptionStatement
{
3969 public Statement Statement
;
3970 TemporaryVariable temp
;
3972 public Lock (Expression expr
, Statement stmt
, Location l
)
3979 public override bool Resolve (BlockContext ec
)
3981 expr
= expr
.Resolve (ec
);
3985 if (!TypeManager
.IsReferenceType (expr
.Type
)){
3986 ec
.Report
.Error (185, loc
,
3987 "`{0}' is not a reference type as required by the lock statement",
3988 TypeManager
.CSharpName (expr
.Type
));
3992 ec
.StartFlowBranching (this);
3993 bool ok
= Statement
.Resolve (ec
);
3994 ec
.EndFlowBranching ();
3996 ok
&= base.Resolve (ec
);
3998 // Avoid creating libraries that reference the internal
4000 TypeSpec t
= expr
.Type
;
4001 if (t
== TypeManager
.null_type
)
4002 t
= TypeManager
.object_type
;
4004 temp
= new TemporaryVariable (t
, loc
);
4007 if (TypeManager
.void_monitor_enter_object
== null || TypeManager
.void_monitor_exit_object
== null) {
4008 TypeSpec monitor_type
= TypeManager
.CoreLookupType (ec
.Compiler
, "System.Threading", "Monitor", MemberKind
.Class
, true);
4009 TypeManager
.void_monitor_enter_object
= TypeManager
.GetPredefinedMethod (
4010 monitor_type
, "Enter", loc
, TypeManager
.object_type
);
4011 TypeManager
.void_monitor_exit_object
= TypeManager
.GetPredefinedMethod (
4012 monitor_type
, "Exit", loc
, TypeManager
.object_type
);
4018 protected override void EmitPreTryBody (EmitContext ec
)
4020 temp
.EmitAssign (ec
, expr
);
4022 ec
.Emit (OpCodes
.Call
, TypeManager
.void_monitor_enter_object
);
4025 protected override void EmitTryBody (EmitContext ec
)
4027 Statement
.Emit (ec
);
4030 protected override void EmitFinallyBody (EmitContext ec
)
4033 ec
.Emit (OpCodes
.Call
, TypeManager
.void_monitor_exit_object
);
4036 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4038 Lock target
= (Lock
) t
;
4040 target
.expr
= expr
.Clone (clonectx
);
4041 target
.Statement
= Statement
.Clone (clonectx
);
4045 public class Unchecked
: Statement
{
4048 public Unchecked (Block b
)
4054 public override bool Resolve (BlockContext ec
)
4056 using (ec
.With (ResolveContext
.Options
.AllCheckStateFlags
, false))
4057 return Block
.Resolve (ec
);
4060 protected override void DoEmit (EmitContext ec
)
4062 using (ec
.With (EmitContext
.Options
.AllCheckStateFlags
, false))
4066 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4068 Unchecked target
= (Unchecked
) t
;
4070 target
.Block
= clonectx
.LookupBlock (Block
);
4074 public class Checked
: Statement
{
4077 public Checked (Block b
)
4080 b
.Unchecked
= false;
4083 public override bool Resolve (BlockContext ec
)
4085 using (ec
.With (ResolveContext
.Options
.AllCheckStateFlags
, true))
4086 return Block
.Resolve (ec
);
4089 protected override void DoEmit (EmitContext ec
)
4091 using (ec
.With (EmitContext
.Options
.AllCheckStateFlags
, true))
4095 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4097 Checked target
= (Checked
) t
;
4099 target
.Block
= clonectx
.LookupBlock (Block
);
4103 public class Unsafe
: Statement
{
4106 public Unsafe (Block b
)
4109 Block
.Unsafe
= true;
4110 loc
= b
.StartLocation
;
4113 public override bool Resolve (BlockContext ec
)
4115 if (ec
.CurrentIterator
!= null)
4116 ec
.Report
.Error (1629, loc
, "Unsafe code may not appear in iterators");
4118 using (ec
.Set (ResolveContext
.Options
.UnsafeScope
))
4119 return Block
.Resolve (ec
);
4122 protected override void DoEmit (EmitContext ec
)
4127 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4129 Unsafe target
= (Unsafe
) t
;
4131 target
.Block
= clonectx
.LookupBlock (Block
);
4138 public class Fixed
: Statement
{
4140 List
<KeyValuePair
<LocalInfo
, Expression
>> declarators
;
4141 Statement statement
;
4146 abstract class Emitter
4148 protected LocalInfo vi
;
4149 protected Expression converted
;
4151 protected Emitter (Expression expr
, LocalInfo li
)
4157 public abstract void Emit (EmitContext ec
);
4158 public abstract void EmitExit (EmitContext ec
);
4161 class ExpressionEmitter
: Emitter
{
4162 public ExpressionEmitter (Expression converted
, LocalInfo li
) :
4163 base (converted
, li
)
4167 public override void Emit (EmitContext ec
) {
4169 // Store pointer in pinned location
4171 converted
.Emit (ec
);
4175 public override void EmitExit (EmitContext ec
)
4177 ec
.Emit (OpCodes
.Ldc_I4_0
);
4178 ec
.Emit (OpCodes
.Conv_U
);
4183 class StringEmitter
: Emitter
4185 LocalInfo pinned_string
;
4187 public StringEmitter (Expression expr
, LocalInfo li
, Location loc
):
4190 pinned_string
= new LocalInfo (new TypeExpression (TypeManager
.string_type
, loc
), null, null, loc
);
4191 pinned_string
.Pinned
= true;
4194 public StringEmitter
Resolve (ResolveContext rc
)
4196 pinned_string
.Resolve (rc
);
4198 if (TypeManager
.int_get_offset_to_string_data
== null) {
4199 TypeManager
.int_get_offset_to_string_data
= TypeManager
.GetPredefinedProperty (
4200 TypeManager
.runtime_helpers_type
, "OffsetToStringData", pinned_string
.Location
, TypeManager
.int32_type
);
4206 public override void Emit (EmitContext ec
)
4208 pinned_string
.ResolveVariable (ec
);
4210 converted
.Emit (ec
);
4211 pinned_string
.EmitAssign (ec
);
4213 // TODO: Should use Binary::Add
4214 pinned_string
.Emit (ec
);
4215 ec
.Emit (OpCodes
.Conv_I
);
4217 PropertyExpr pe
= new PropertyExpr (TypeManager
.int_get_offset_to_string_data
, pinned_string
.Location
);
4218 //pe.InstanceExpression = pinned_string;
4219 pe
.Resolve (new ResolveContext (ec
.MemberContext
)).Emit (ec
);
4221 ec
.Emit (OpCodes
.Add
);
4225 public override void EmitExit (EmitContext ec
)
4227 ec
.Emit (OpCodes
.Ldnull
);
4228 pinned_string
.EmitAssign (ec
);
4232 public Fixed (Expression type
, List
<KeyValuePair
<LocalInfo
, Expression
>> decls
, Statement stmt
, Location l
)
4235 declarators
= decls
;
4240 public Statement Statement
{
4241 get { return statement; }
4244 public override bool Resolve (BlockContext ec
)
4247 Expression
.UnsafeError (ec
, loc
);
4251 TypeExpr texpr
= type
.ResolveAsContextualType (ec
, false);
4252 if (texpr
== null) {
4253 if (type
is VarExpr
)
4254 ec
.Report
.Error (821, type
.Location
, "A fixed statement cannot use an implicitly typed local variable");
4259 expr_type
= texpr
.Type
;
4261 data
= new Emitter
[declarators
.Count
];
4263 if (!expr_type
.IsPointer
){
4264 ec
.Report
.Error (209, loc
, "The type of locals declared in a fixed statement must be a pointer type");
4269 foreach (var p
in declarators
){
4270 LocalInfo vi
= p
.Key
;
4271 Expression e
= p
.Value
;
4273 vi
.VariableInfo
.SetAssigned (ec
);
4274 vi
.SetReadOnlyContext (LocalInfo
.ReadOnlyContext
.Fixed
);
4277 // The rules for the possible declarators are pretty wise,
4278 // but the production on the grammar is more concise.
4280 // So we have to enforce these rules here.
4282 // We do not resolve before doing the case 1 test,
4283 // because the grammar is explicit in that the token &
4284 // is present, so we need to test for this particular case.
4288 ec
.Report
.Error (254, loc
, "The right hand side of a fixed statement assignment may not be a cast expression");
4292 using (ec
.Set (ResolveContext
.Options
.FixedInitializerScope
)) {
4302 if (e
.Type
.IsArray
){
4303 TypeSpec array_type
= TypeManager
.GetElementType (e
.Type
);
4306 // Provided that array_type is unmanaged,
4308 if (!TypeManager
.VerifyUnmanaged (ec
.Compiler
, array_type
, loc
))
4312 // and T* is implicitly convertible to the
4313 // pointer type given in the fixed statement.
4315 ArrayPtr array_ptr
= new ArrayPtr (e
, array_type
, loc
);
4317 Expression converted
= Convert
.ImplicitConversionRequired (
4318 ec
, array_ptr
, vi
.VariableType
, loc
);
4319 if (converted
== null)
4323 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4325 converted
= new Conditional (new BooleanExpression (new Binary (Binary
.Operator
.LogicalOr
,
4326 new Binary (Binary
.Operator
.Equality
, e
, new NullLiteral (loc
), loc
),
4327 new Binary (Binary
.Operator
.Equality
, new MemberAccess (e
, "Length"), new IntConstant (0, loc
), loc
), loc
)),
4328 new NullPointer (loc
),
4331 converted
= converted
.Resolve (ec
);
4333 data
[i
] = new ExpressionEmitter (converted
, vi
);
4342 if (e
.Type
== TypeManager
.string_type
){
4343 data
[i
] = new StringEmitter (e
, vi
, loc
).Resolve (ec
);
4348 // Case 4: fixed buffer
4349 if (e
is FixedBufferPtr
) {
4350 data
[i
++] = new ExpressionEmitter (e
, vi
);
4355 // Case 1: & object.
4357 Unary u
= e
as Unary
;
4358 if (u
!= null && u
.Oper
== Unary
.Operator
.AddressOf
) {
4359 IVariableReference vr
= u
.Expr
as IVariableReference
;
4360 if (vr
== null || !vr
.IsFixed
) {
4361 data
[i
] = new ExpressionEmitter (e
, vi
);
4365 if (data
[i
++] == null)
4366 ec
.Report
.Error (213, vi
.Location
, "You cannot use the fixed statement to take the address of an already fixed expression");
4368 e
= Convert
.ImplicitConversionRequired (ec
, e
, expr_type
, loc
);
4371 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
4372 bool ok
= statement
.Resolve (ec
);
4373 bool flow_unreachable
= ec
.EndFlowBranching ();
4374 has_ret
= flow_unreachable
;
4379 protected override void DoEmit (EmitContext ec
)
4381 for (int i
= 0; i
< data
.Length
; i
++) {
4385 statement
.Emit (ec
);
4391 // Clear the pinned variable
4393 for (int i
= 0; i
< data
.Length
; i
++) {
4394 data
[i
].EmitExit (ec
);
4398 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4400 Fixed target
= (Fixed
) t
;
4402 target
.type
= type
.Clone (clonectx
);
4403 target
.declarators
= new List
<KeyValuePair
<LocalInfo
, Expression
>> (declarators
.Count
);
4404 foreach (var p
in declarators
) {
4405 target
.declarators
.Add (new KeyValuePair
<LocalInfo
, Expression
> (
4406 clonectx
.LookupVariable (p
.Key
), p
.Value
.Clone (clonectx
)));
4409 target
.statement
= statement
.Clone (clonectx
);
4413 public class Catch
: Statement
{
4414 public readonly string Name
;
4416 public Block VarBlock
;
4418 Expression type_expr
;
4421 public Catch (Expression type
, string name
, Block block
, Block var_block
, Location l
)
4426 VarBlock
= var_block
;
4430 public TypeSpec CatchType
{
4436 public bool IsGeneral
{
4438 return type_expr
== null;
4442 protected override void DoEmit (EmitContext ec
)
4444 if (CatchType
!= null)
4445 ec
.BeginCatchBlock (CatchType
);
4447 ec
.BeginCatchBlock (TypeManager
.object_type
);
4449 if (VarBlock
!= null)
4453 // TODO: Move to resolve
4454 LocalVariableReference lvr
= new LocalVariableReference (Block
, Name
, loc
);
4455 lvr
.Resolve (new ResolveContext (ec
.MemberContext
));
4457 // Only to make verifier happy
4458 if (TypeManager
.IsGenericParameter (lvr
.Type
))
4459 ec
.Emit (OpCodes
.Unbox_Any
, lvr
.Type
);
4462 if (lvr
.IsHoisted
) {
4463 LocalTemporary lt
= new LocalTemporary (lvr
.Type
);
4467 // Variable is at the top of the stack
4468 source
= EmptyExpression
.Null
;
4471 lvr
.EmitAssign (ec
, source
, false, false);
4473 ec
.Emit (OpCodes
.Pop
);
4478 public override bool Resolve (BlockContext ec
)
4480 using (ec
.With (ResolveContext
.Options
.CatchScope
, true)) {
4481 if (type_expr
!= null) {
4482 TypeExpr te
= type_expr
.ResolveAsTypeTerminal (ec
, false);
4488 if (type
!= TypeManager
.exception_type
&& !TypeManager
.IsSubclassOf (type
, TypeManager
.exception_type
)){
4489 ec
.Report
.Error (155, loc
, "The type caught or thrown must be derived from System.Exception");
4495 if (!Block
.Resolve (ec
))
4498 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4499 // emit the "unused variable" warnings.
4500 if (VarBlock
!= null)
4501 return VarBlock
.Resolve (ec
);
4507 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4509 Catch target
= (Catch
) t
;
4511 if (type_expr
!= null)
4512 target
.type_expr
= type_expr
.Clone (clonectx
);
4513 if (VarBlock
!= null)
4514 target
.VarBlock
= clonectx
.LookupBlock (VarBlock
);
4515 target
.Block
= clonectx
.LookupBlock (Block
);
4519 public class TryFinally
: ExceptionStatement
{
4523 public TryFinally (Statement stmt
, Block fini
, Location l
)
4530 public override bool Resolve (BlockContext ec
)
4534 ec
.StartFlowBranching (this);
4536 if (!stmt
.Resolve (ec
))
4540 ec
.CurrentBranching
.CreateSibling (fini
, FlowBranching
.SiblingType
.Finally
);
4541 using (ec
.With (ResolveContext
.Options
.FinallyScope
, true)) {
4542 if (!fini
.Resolve (ec
))
4546 ec
.EndFlowBranching ();
4548 ok
&= base.Resolve (ec
);
4553 protected override void EmitPreTryBody (EmitContext ec
)
4557 protected override void EmitTryBody (EmitContext ec
)
4562 protected override void EmitFinallyBody (EmitContext ec
)
4567 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4569 TryFinally target
= (TryFinally
) t
;
4571 target
.stmt
= (Statement
) stmt
.Clone (clonectx
);
4573 target
.fini
= clonectx
.LookupBlock (fini
);
4577 public class TryCatch
: Statement
{
4579 public List
<Catch
> Specific
;
4580 public Catch General
;
4581 bool inside_try_finally
, code_follows
;
4583 public TryCatch (Block block
, List
<Catch
> catch_clauses
, Location l
, bool inside_try_finally
)
4586 this.Specific
= catch_clauses
;
4587 this.inside_try_finally
= inside_try_finally
;
4589 Catch c
= catch_clauses
[0];
4592 catch_clauses
.RemoveAt (0);
4598 public override bool Resolve (BlockContext ec
)
4602 ec
.StartFlowBranching (this);
4604 if (!Block
.Resolve (ec
))
4607 TypeSpec
[] prev_catches
= new TypeSpec
[Specific
.Count
];
4609 foreach (Catch c
in Specific
){
4610 ec
.CurrentBranching
.CreateSibling (c
.Block
, FlowBranching
.SiblingType
.Catch
);
4612 if (c
.Name
!= null) {
4613 LocalInfo vi
= c
.Block
.GetLocalInfo (c
.Name
);
4615 throw new Exception ();
4617 vi
.VariableInfo
= null;
4620 if (!c
.Resolve (ec
)) {
4625 TypeSpec resolved_type
= c
.CatchType
;
4626 for (int ii
= 0; ii
< last_index
; ++ii
) {
4627 if (resolved_type
== prev_catches
[ii
] || TypeManager
.IsSubclassOf (resolved_type
, prev_catches
[ii
])) {
4628 ec
.Report
.Error (160, c
.loc
,
4629 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4630 TypeManager
.CSharpName (prev_catches
[ii
]));
4635 prev_catches
[last_index
++] = resolved_type
;
4638 if (General
!= null) {
4639 if (CodeGen
.Assembly
.WrapNonExceptionThrows
) {
4640 foreach (Catch c
in Specific
){
4641 if (c
.CatchType
== TypeManager
.exception_type
&& PredefinedAttributes
.Get
.RuntimeCompatibility
.IsDefined
) {
4642 ec
.Report
.Warning (1058, 1, c
.loc
, "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4647 ec
.CurrentBranching
.CreateSibling (General
.Block
, FlowBranching
.SiblingType
.Catch
);
4649 if (!General
.Resolve (ec
))
4653 ec
.EndFlowBranching ();
4655 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4656 // So, ensure there's some IL code after this statement
4657 if (!inside_try_finally
&& !code_follows
&& ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
)
4658 ec
.NeedReturnLabel ();
4663 public void SomeCodeFollows ()
4665 code_follows
= true;
4668 protected override void DoEmit (EmitContext ec
)
4670 if (!inside_try_finally
)
4671 ec
.BeginExceptionBlock ();
4675 foreach (Catch c
in Specific
)
4678 if (General
!= null)
4681 if (!inside_try_finally
)
4682 ec
.EndExceptionBlock ();
4685 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4687 TryCatch target
= (TryCatch
) t
;
4689 target
.Block
= clonectx
.LookupBlock (Block
);
4690 if (General
!= null)
4691 target
.General
= (Catch
) General
.Clone (clonectx
);
4692 if (Specific
!= null){
4693 target
.Specific
= new List
<Catch
> ();
4694 foreach (Catch c
in Specific
)
4695 target
.Specific
.Add ((Catch
) c
.Clone (clonectx
));
4700 // FIXME: Why is it almost exact copy of Using ??
4701 public class UsingTemporary
: ExceptionStatement
{
4702 TemporaryVariable local_copy
;
4703 public Statement Statement
;
4707 public UsingTemporary (Expression expr
, Statement stmt
, Location l
)
4714 public override bool Resolve (BlockContext ec
)
4716 expr
= expr
.Resolve (ec
);
4720 expr_type
= expr
.Type
;
4722 if (!expr_type
.ImplementsInterface (TypeManager
.idisposable_type
) &&
4723 Convert
.ImplicitConversion (ec
, expr
, TypeManager
.idisposable_type
, loc
) == null) {
4724 if (expr_type
!= InternalType
.Dynamic
) {
4725 Using
.Error_IsNotConvertibleToIDisposable (ec
, expr
);
4729 expr
= Convert
.ImplicitConversionRequired (ec
, expr
, TypeManager
.idisposable_type
, loc
);
4730 expr_type
= expr
.Type
;
4733 local_copy
= new TemporaryVariable (expr_type
, loc
);
4734 local_copy
.Resolve (ec
);
4736 ec
.StartFlowBranching (this);
4738 bool ok
= Statement
.Resolve (ec
);
4740 ec
.EndFlowBranching ();
4742 ok
&= base.Resolve (ec
);
4744 if (TypeManager
.void_dispose_void
== null) {
4745 TypeManager
.void_dispose_void
= TypeManager
.GetPredefinedMethod (
4746 TypeManager
.idisposable_type
, "Dispose", loc
, TypeSpec
.EmptyTypes
);
4752 protected override void EmitPreTryBody (EmitContext ec
)
4754 local_copy
.EmitAssign (ec
, expr
);
4757 protected override void EmitTryBody (EmitContext ec
)
4759 Statement
.Emit (ec
);
4762 protected override void EmitFinallyBody (EmitContext ec
)
4764 if (!TypeManager
.IsStruct (expr_type
)) {
4765 Label skip
= ec
.DefineLabel ();
4766 local_copy
.Emit (ec
);
4767 ec
.Emit (OpCodes
.Brfalse
, skip
);
4768 local_copy
.Emit (ec
);
4769 ec
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4770 ec
.MarkLabel (skip
);
4774 MethodSpec ms
= MemberCache
.FindMember (expr_type
,
4775 MemberFilter
.Method ("Dispose", 0, ParametersCompiled
.EmptyReadOnlyParameters
, TypeManager
.void_type
),
4776 BindingRestriction
.InstanceOnly
) as MethodSpec
;
4779 local_copy
.Emit (ec
);
4780 ec
.Emit (OpCodes
.Box
, expr_type
);
4781 ec
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4785 local_copy
.AddressOf (ec
, AddressOp
.Load
);
4786 ec
.Emit (OpCodes
.Call
, ms
);
4789 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4791 UsingTemporary target
= (UsingTemporary
) t
;
4793 target
.expr
= expr
.Clone (clonectx
);
4794 target
.Statement
= Statement
.Clone (clonectx
);
4798 public class Using
: ExceptionStatement
{
4800 public Statement EmbeddedStatement
{
4801 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4807 ExpressionStatement assign
;
4809 public Using (Expression
var, Expression init
, Statement stmt
, Location l
)
4817 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec
, Expression expr
)
4819 ec
.Report
.SymbolRelatedToPreviousError (expr
.Type
);
4820 ec
.Report
.Error (1674, expr
.Location
, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4821 TypeManager
.CSharpName (expr
.Type
));
4824 protected override void EmitPreTryBody (EmitContext ec
)
4826 assign
.EmitStatement (ec
);
4829 protected override void EmitTryBody (EmitContext ec
)
4834 protected override void EmitFinallyBody (EmitContext ec
)
4836 Label skip
= ec
.DefineLabel ();
4838 bool emit_null_check
= !TypeManager
.IsValueType (var.Type
);
4839 if (emit_null_check
) {
4841 ec
.Emit (OpCodes
.Brfalse
, skip
);
4844 Invocation
.EmitCall (ec
, false, var, TypeManager
.void_dispose_void
, null, loc
);
4846 if (emit_null_check
)
4847 ec
.MarkLabel (skip
);
4850 public override bool Resolve (BlockContext ec
)
4852 if (!ResolveVariable (ec
))
4855 ec
.StartFlowBranching (this);
4857 bool ok
= stmt
.Resolve (ec
);
4859 ec
.EndFlowBranching ();
4861 ok
&= base.Resolve (ec
);
4863 if (TypeManager
.void_dispose_void
== null) {
4864 TypeManager
.void_dispose_void
= TypeManager
.GetPredefinedMethod (
4865 TypeManager
.idisposable_type
, "Dispose", loc
, TypeSpec
.EmptyTypes
);
4871 bool ResolveVariable (BlockContext ec
)
4873 assign
= new SimpleAssign (var, init
, loc
);
4874 assign
= assign
.ResolveStatement (ec
);
4878 if (assign
.Type
== TypeManager
.idisposable_type
|| assign
.Type
.ImplementsInterface (TypeManager
.idisposable_type
)) {
4882 Expression e
= Convert
.ImplicitConversionStandard (ec
, assign
, TypeManager
.idisposable_type
, var.Location
);
4884 if (assign
.Type
== InternalType
.Dynamic
) {
4885 e
= Convert
.ImplicitConversionRequired (ec
, assign
, TypeManager
.idisposable_type
, loc
);
4886 var = new TemporaryVariable (e
.Type
, loc
);
4887 assign
= new SimpleAssign (var, e
, loc
).ResolveStatement (ec
);
4891 Error_IsNotConvertibleToIDisposable (ec
, var);
4895 throw new NotImplementedException ("covariance?");
4898 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4900 Using target
= (Using
) t
;
4902 target
.var = var.Clone (clonectx
);
4903 target
.init
= init
.Clone (clonectx
);
4904 target
.stmt
= stmt
.Clone (clonectx
);
4909 /// Implementation of the foreach C# statement
4911 public class Foreach
: Statement
{
4913 sealed class ArrayForeach
: Statement
4915 class ArrayCounter
: TemporaryVariable
4917 StatementExpression increment
;
4919 public ArrayCounter (Location loc
)
4920 : base (TypeManager
.int32_type
, loc
)
4924 public void ResolveIncrement (BlockContext ec
)
4926 increment
= new StatementExpression (new UnaryMutator (UnaryMutator
.Mode
.PostIncrement
, this, loc
));
4927 increment
.Resolve (ec
);
4930 public void EmitIncrement (EmitContext ec
)
4932 increment
.Emit (ec
);
4936 readonly Foreach for_each
;
4937 readonly Statement statement
;
4940 TemporaryVariable
[] lengths
;
4941 Expression
[] length_exprs
;
4942 ArrayCounter
[] counter
;
4944 TemporaryVariable copy
;
4947 public ArrayForeach (Foreach
@foreach, int rank
)
4949 for_each
= @foreach;
4950 statement
= for_each
.statement
;
4953 counter
= new ArrayCounter
[rank
];
4954 length_exprs
= new Expression
[rank
];
4957 // Only use temporary length variables when dealing with
4958 // multi-dimensional arrays
4961 lengths
= new TemporaryVariable
[rank
];
4964 protected override void CloneTo (CloneContext clonectx
, Statement target
)
4966 throw new NotImplementedException ();
4969 public override bool Resolve (BlockContext ec
)
4971 copy
= new TemporaryVariable (for_each
.expr
.Type
, loc
);
4974 int rank
= length_exprs
.Length
;
4975 Arguments list
= new Arguments (rank
);
4976 for (int i
= 0; i
< rank
; i
++) {
4977 counter
[i
] = new ArrayCounter (loc
);
4978 counter
[i
].ResolveIncrement (ec
);
4981 length_exprs
[i
] = new MemberAccess (copy
, "Length").Resolve (ec
);
4983 lengths
[i
] = new TemporaryVariable (TypeManager
.int32_type
, loc
);
4984 lengths
[i
].Resolve (ec
);
4986 Arguments args
= new Arguments (1);
4987 args
.Add (new Argument (new IntConstant (i
, loc
)));
4988 length_exprs
[i
] = new Invocation (new MemberAccess (copy
, "GetLength"), args
).Resolve (ec
);
4991 list
.Add (new Argument (counter
[i
]));
4994 access
= new ElementAccess (copy
, list
).Resolve (ec
);
4998 Expression var_type
= for_each
.type
;
4999 VarExpr ve
= var_type
as VarExpr
;
5001 // Infer implicitly typed local variable from foreach array type
5002 var_type
= new TypeExpression (access
.Type
, ve
.Location
);
5005 var_type
= var_type
.ResolveAsTypeTerminal (ec
, false);
5006 if (var_type
== null)
5009 conv
= Convert
.ExplicitConversion (ec
, access
, var_type
.Type
, loc
);
5015 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
5016 ec
.CurrentBranching
.CreateSibling ();
5018 for_each
.variable
= for_each
.variable
.ResolveLValue (ec
, conv
);
5019 if (for_each
.variable
== null)
5022 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
5023 if (!statement
.Resolve (ec
))
5025 ec
.EndFlowBranching ();
5027 // There's no direct control flow from the end of the embedded statement to the end of the loop
5028 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
5030 ec
.EndFlowBranching ();
5035 protected override void DoEmit (EmitContext ec
)
5037 copy
.EmitAssign (ec
, for_each
.expr
);
5039 int rank
= length_exprs
.Length
;
5040 Label
[] test
= new Label
[rank
];
5041 Label
[] loop
= new Label
[rank
];
5043 for (int i
= 0; i
< rank
; i
++) {
5044 test
[i
] = ec
.DefineLabel ();
5045 loop
[i
] = ec
.DefineLabel ();
5047 if (lengths
!= null)
5048 lengths
[i
].EmitAssign (ec
, length_exprs
[i
]);
5051 IntConstant zero
= new IntConstant (0, loc
);
5052 for (int i
= 0; i
< rank
; i
++) {
5053 counter
[i
].EmitAssign (ec
, zero
);
5055 ec
.Emit (OpCodes
.Br
, test
[i
]);
5056 ec
.MarkLabel (loop
[i
]);
5059 ((IAssignMethod
) for_each
.variable
).EmitAssign (ec
, conv
, false, false);
5061 statement
.Emit (ec
);
5063 ec
.MarkLabel (ec
.LoopBegin
);
5065 for (int i
= rank
- 1; i
>= 0; i
--){
5066 counter
[i
].EmitIncrement (ec
);
5068 ec
.MarkLabel (test
[i
]);
5069 counter
[i
].Emit (ec
);
5071 if (lengths
!= null)
5072 lengths
[i
].Emit (ec
);
5074 length_exprs
[i
].Emit (ec
);
5076 ec
.Emit (OpCodes
.Blt
, loop
[i
]);
5079 ec
.MarkLabel (ec
.LoopEnd
);
5083 sealed class CollectionForeach
: Statement
5085 class CollectionForeachStatement
: Statement
5088 Expression variable
, current
, conv
;
5089 Statement statement
;
5092 public CollectionForeachStatement (TypeSpec type
, Expression variable
,
5093 Expression current
, Statement statement
,
5097 this.variable
= variable
;
5098 this.current
= current
;
5099 this.statement
= statement
;
5103 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5105 throw new NotImplementedException ();
5108 public override bool Resolve (BlockContext ec
)
5110 current
= current
.Resolve (ec
);
5111 if (current
== null)
5114 conv
= Convert
.ExplicitConversion (ec
, current
, type
, loc
);
5118 assign
= new SimpleAssign (variable
, conv
, loc
);
5119 if (assign
.Resolve (ec
) == null)
5122 if (!statement
.Resolve (ec
))
5128 protected override void DoEmit (EmitContext ec
)
5130 assign
.EmitStatement (ec
);
5131 statement
.Emit (ec
);
5135 Expression variable
, expr
;
5136 Statement statement
;
5138 TemporaryVariable enumerator
;
5143 MethodGroupExpr get_enumerator
;
5144 PropertyExpr get_current
;
5145 MethodSpec move_next
;
5146 Expression var_type
;
5147 TypeSpec enumerator_type
;
5148 bool enumerator_found
;
5150 public CollectionForeach (Expression var_type
, Expression
var,
5151 Expression expr
, Statement stmt
, Location l
)
5153 this.var_type
= var_type
;
5154 this.variable
= var;
5160 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5162 throw new NotImplementedException ();
5165 bool GetEnumeratorFilter (ResolveContext ec
, MethodSpec mi
)
5167 TypeSpec return_type
= mi
.ReturnType
;
5170 // Ok, we can access it, now make sure that we can do something
5171 // with this `GetEnumerator'
5174 if (return_type
== TypeManager
.ienumerator_type
||
5175 return_type
.ImplementsInterface (TypeManager
.ienumerator_type
)) {
5177 // If it is not an interface, lets try to find the methods ourselves.
5178 // For example, if we have:
5179 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5180 // We can avoid the iface call. This is a runtime perf boost.
5181 // even bigger if we have a ValueType, because we avoid the cost
5184 // We have to make sure that both methods exist for us to take
5185 // this path. If one of the methods does not exist, we will just
5186 // use the interface. Sadly, this complex if statement is the only
5187 // way I could do this without a goto
5190 if (TypeManager
.bool_movenext_void
== null) {
5191 TypeManager
.bool_movenext_void
= TypeManager
.GetPredefinedMethod (
5192 TypeManager
.ienumerator_type
, "MoveNext", loc
, TypeSpec
.EmptyTypes
);
5195 if (TypeManager
.ienumerator_getcurrent
== null) {
5196 TypeManager
.ienumerator_getcurrent
= TypeManager
.GetPredefinedProperty (
5197 TypeManager
.ienumerator_type
, "Current", loc
, TypeManager
.object_type
);
5201 // Prefer a generic enumerator over a non-generic one.
5203 if (return_type
.IsInterface
&& TypeManager
.IsGenericType (return_type
)) {
5204 enumerator_type
= return_type
;
5205 if (!FetchGetCurrent (ec
, return_type
))
5206 get_current
= new PropertyExpr (TypeManager
.ienumerator_getcurrent
, loc
);
5207 if (!FetchMoveNext (return_type
))
5208 move_next
= TypeManager
.bool_movenext_void
;
5212 if (return_type
.IsInterface
||
5213 !FetchMoveNext (return_type
) ||
5214 !FetchGetCurrent (ec
, return_type
)) {
5215 enumerator_type
= return_type
;
5216 move_next
= TypeManager
.bool_movenext_void
;
5217 get_current
= new PropertyExpr (TypeManager
.ienumerator_getcurrent
, loc
);
5222 // Ok, so they dont return an IEnumerable, we will have to
5223 // find if they support the GetEnumerator pattern.
5226 if (TypeManager
.HasElementType (return_type
) || !FetchMoveNext (return_type
) || !FetchGetCurrent (ec
, return_type
)) {
5227 ec
.Report
.Error (202, loc
, "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5228 TypeManager
.CSharpName (return_type
), TypeManager
.CSharpSignature (mi
));
5233 enumerator_type
= return_type
;
5239 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5241 bool FetchMoveNext (TypeSpec t
)
5243 move_next
= MemberCache
.FindMember (t
,
5244 MemberFilter
.Method ("MoveNext", 0, ParametersCompiled
.EmptyReadOnlyParameters
, TypeManager
.bool_type
),
5245 BindingRestriction
.InstanceOnly
) as MethodSpec
;
5247 return move_next
!= null && (move_next
.Modifiers
& Modifiers
.PUBLIC
) != 0;
5251 // Retrieves a `public T get_Current ()' method from the Type `t'
5253 bool FetchGetCurrent (ResolveContext ec
, TypeSpec t
)
5255 PropertyExpr pe
= Expression
.MemberLookup (ec
.Compiler
,
5256 ec
.CurrentType
, t
, "Current", 0, MemberKind
.Property
,
5257 BindingRestriction
.AccessibleOnly
, loc
) as PropertyExpr
;
5265 void Error_Enumerator (BlockContext ec
)
5267 if (enumerator_found
) {
5271 ec
.Report
.Error (1579, loc
,
5272 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5273 TypeManager
.CSharpName (expr
.Type
));
5276 bool TryType (ResolveContext ec
, TypeSpec t
)
5278 var mg
= Expression
.MemberLookup (ec
.Compiler
, ec
.CurrentType
, null, t
, "GetEnumerator", 0,
5279 MemberKind
.Method
, BindingRestriction
.NoOverrides
| BindingRestriction
.InstanceOnly
, loc
) as MethodGroupExpr
;
5284 MethodSpec result
= null;
5285 MethodSpec tmp_move_next
= null;
5286 PropertyExpr tmp_get_cur
= null;
5287 TypeSpec tmp_enumerator_type
= enumerator_type
;
5288 foreach (MethodSpec mi
in mg
.Methods
) {
5289 if (!mi
.Parameters
.IsEmpty
)
5292 // Check whether GetEnumerator is public
5293 if ((mi
.Modifiers
& Modifiers
.AccessibilityMask
) != Modifiers
.PUBLIC
)
5296 enumerator_found
= true;
5298 if (!GetEnumeratorFilter (ec
, mi
))
5301 if (result
!= null) {
5302 if (TypeManager
.IsGenericType (result
.ReturnType
)) {
5303 if (!TypeManager
.IsGenericType (mi
.ReturnType
))
5306 ec
.Report
.SymbolRelatedToPreviousError (t
);
5307 ec
.Report
.Error(1640, loc
, "foreach statement cannot operate on variables of type `{0}' " +
5308 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5309 TypeManager
.CSharpName (t
), TypeManager
.generic_ienumerable_type
.GetSignatureForError ());
5313 // Always prefer generics enumerators
5314 if (!TypeManager
.IsGenericType (mi
.ReturnType
)) {
5315 if (mi
.DeclaringType
.ImplementsInterface (result
.DeclaringType
) ||
5316 result
.DeclaringType
.ImplementsInterface (mi
.DeclaringType
))
5319 ec
.Report
.SymbolRelatedToPreviousError (result
);
5320 ec
.Report
.SymbolRelatedToPreviousError (mi
);
5321 ec
.Report
.Warning (278, 2, loc
, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5322 TypeManager
.CSharpName (t
), "enumerable", result
.GetSignatureForError (), mi
.GetSignatureForError ());
5327 tmp_move_next
= move_next
;
5328 tmp_get_cur
= get_current
;
5329 tmp_enumerator_type
= enumerator_type
;
5330 if (mi
.DeclaringType
== t
)
5334 if (result
!= null) {
5335 move_next
= tmp_move_next
;
5336 get_current
= tmp_get_cur
;
5337 enumerator_type
= tmp_enumerator_type
;
5338 get_enumerator
= new MethodGroupExpr (result
, enumerator_type
, loc
);
5340 if (t
!= expr
.Type
) {
5341 expr
= Convert
.ExplicitConversion (
5344 throw new InternalErrorException ();
5347 get_enumerator
.InstanceExpression
= expr
;
5348 get_enumerator
.IsBase
= t
!= expr
.Type
;
5356 bool ProbeCollectionType (ResolveContext ec
, TypeSpec t
)
5358 int errors
= ec
.Report
.Errors
;
5359 for (TypeSpec tt
= t
; tt
!= null && tt
!= TypeManager
.object_type
;){
5360 if (TryType (ec
, tt
))
5365 if (ec
.Report
.Errors
> errors
)
5369 // Now try to find the method in the interfaces
5371 for (TypeSpec tt
= t
; tt
!= null && tt
!= TypeManager
.object_type
; ) {
5372 if (tt
.Interfaces
!= null) {
5373 foreach (TypeSpec i
in tt
.Interfaces
) {
5374 if (TryType (ec
, i
))
5384 public override bool Resolve (BlockContext ec
)
5386 enumerator_type
= TypeManager
.ienumerator_type
;
5388 bool is_dynamic
= expr
.Type
== InternalType
.Dynamic
;
5390 expr
= Convert
.ImplicitConversionRequired (ec
, expr
, TypeManager
.ienumerable_type
, loc
);
5392 if (!ProbeCollectionType (ec
, expr
.Type
)) {
5393 Error_Enumerator (ec
);
5397 VarExpr ve
= var_type
as VarExpr
;
5399 // Infer implicitly typed local variable from foreach enumerable type
5400 var_type
= new TypeExpression (
5401 is_dynamic
? InternalType
.Dynamic
: get_current
.Type
,
5405 var_type
= var_type
.ResolveAsTypeTerminal (ec
, false);
5406 if (var_type
== null)
5409 enumerator
= new TemporaryVariable (enumerator_type
, loc
);
5410 enumerator
.Resolve (ec
);
5412 init
= new Invocation (get_enumerator
, null);
5413 init
= init
.Resolve (ec
);
5417 Expression move_next_expr
;
5419 var mi
= new List
<MemberSpec
> (1) { move_next }
;
5420 MethodGroupExpr mg
= new MethodGroupExpr (mi
, var_type
.Type
, loc
);
5421 mg
.InstanceExpression
= enumerator
;
5423 move_next_expr
= new Invocation (mg
, null);
5426 get_current
.InstanceExpression
= enumerator
;
5428 Statement block
= new CollectionForeachStatement (
5429 var_type
.Type
, variable
, get_current
, statement
, loc
);
5431 loop
= new While (new BooleanExpression (move_next_expr
), block
, loc
);
5434 bool implements_idisposable
= enumerator_type
.ImplementsInterface (TypeManager
.idisposable_type
);
5435 if (implements_idisposable
|| !enumerator_type
.IsSealed
) {
5436 wrapper
= new DisposableWrapper (this, implements_idisposable
);
5438 wrapper
= new NonDisposableWrapper (this);
5441 return wrapper
.Resolve (ec
);
5444 protected override void DoEmit (EmitContext ec
)
5449 class NonDisposableWrapper
: Statement
{
5450 CollectionForeach parent
;
5452 internal NonDisposableWrapper (CollectionForeach parent
)
5454 this.parent
= parent
;
5457 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5459 throw new NotSupportedException ();
5462 public override bool Resolve (BlockContext ec
)
5464 return parent
.ResolveLoop (ec
);
5467 protected override void DoEmit (EmitContext ec
)
5469 parent
.EmitLoopInit (ec
);
5470 parent
.EmitLoopBody (ec
);
5474 sealed class DisposableWrapper
: ExceptionStatement
5476 CollectionForeach parent
;
5477 bool implements_idisposable
;
5479 internal DisposableWrapper (CollectionForeach parent
, bool implements
)
5481 this.parent
= parent
;
5482 this.implements_idisposable
= implements
;
5485 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5487 throw new NotSupportedException ();
5490 public override bool Resolve (BlockContext ec
)
5494 ec
.StartFlowBranching (this);
5496 if (!parent
.ResolveLoop (ec
))
5499 ec
.EndFlowBranching ();
5501 ok
&= base.Resolve (ec
);
5503 if (TypeManager
.void_dispose_void
== null) {
5504 TypeManager
.void_dispose_void
= TypeManager
.GetPredefinedMethod (
5505 TypeManager
.idisposable_type
, "Dispose", loc
, TypeSpec
.EmptyTypes
);
5510 protected override void EmitPreTryBody (EmitContext ec
)
5512 parent
.EmitLoopInit (ec
);
5515 protected override void EmitTryBody (EmitContext ec
)
5517 parent
.EmitLoopBody (ec
);
5520 protected override void EmitFinallyBody (EmitContext ec
)
5522 Expression instance
= parent
.enumerator
;
5523 if (!TypeManager
.IsValueType (parent
.enumerator_type
)) {
5525 parent
.enumerator
.Emit (ec
);
5527 Label call_dispose
= ec
.DefineLabel ();
5529 if (!implements_idisposable
) {
5530 ec
.Emit (OpCodes
.Isinst
, TypeManager
.idisposable_type
);
5531 LocalTemporary temp
= new LocalTemporary (TypeManager
.idisposable_type
);
5537 ec
.Emit (OpCodes
.Brtrue_S
, call_dispose
);
5539 // using 'endfinally' to empty the evaluation stack
5540 ec
.Emit (OpCodes
.Endfinally
);
5541 ec
.MarkLabel (call_dispose
);
5544 Invocation
.EmitCall (ec
, false, instance
, TypeManager
.void_dispose_void
, null, loc
);
5548 bool ResolveLoop (BlockContext ec
)
5550 return loop
.Resolve (ec
);
5553 void EmitLoopInit (EmitContext ec
)
5555 enumerator
.EmitAssign (ec
, init
);
5558 void EmitLoopBody (EmitContext ec
)
5565 Expression variable
;
5567 Statement statement
;
5569 public Foreach (Expression type
, LocalVariableReference
var, Expression expr
,
5570 Statement stmt
, Location l
)
5573 this.variable
= var;
5579 public Statement Statement
{
5580 get { return statement; }
5583 public override bool Resolve (BlockContext ec
)
5585 expr
= expr
.Resolve (ec
);
5590 ec
.Report
.Error (186, loc
, "Use of null is not valid in this context");
5594 if (expr
.Type
== TypeManager
.string_type
) {
5595 statement
= new ArrayForeach (this, 1);
5596 } else if (expr
.Type
is ArrayContainer
) {
5597 statement
= new ArrayForeach (this, ((ArrayContainer
) expr
.Type
).Rank
);
5599 if (expr
.eclass
== ExprClass
.MethodGroup
|| expr
is AnonymousMethodExpression
) {
5600 ec
.Report
.Error (446, expr
.Location
, "Foreach statement cannot operate on a `{0}'",
5601 expr
.ExprClassName
);
5605 statement
= new CollectionForeach (type
, variable
, expr
, statement
, loc
);
5608 return statement
.Resolve (ec
);
5611 protected override void DoEmit (EmitContext ec
)
5613 Label old_begin
= ec
.LoopBegin
, old_end
= ec
.LoopEnd
;
5614 ec
.LoopBegin
= ec
.DefineLabel ();
5615 ec
.LoopEnd
= ec
.DefineLabel ();
5617 statement
.Emit (ec
);
5619 ec
.LoopBegin
= old_begin
;
5620 ec
.LoopEnd
= old_end
;
5623 protected override void CloneTo (CloneContext clonectx
, Statement t
)
5625 Foreach target
= (Foreach
) t
;
5627 target
.type
= type
.Clone (clonectx
);
5628 target
.variable
= variable
.Clone (clonectx
);
5629 target
.expr
= expr
.Clone (clonectx
);
5630 target
.statement
= statement
.Clone (clonectx
);