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
;
19 using System
.Collections
.Specialized
;
21 namespace Mono
.CSharp
{
23 public abstract class Statement
{
27 /// Resolves the statement, true means that all sub-statements
30 public virtual bool Resolve (BlockContext ec
)
36 /// We already know that the statement is unreachable, but we still
37 /// need to resolve it to catch errors.
39 public virtual bool ResolveUnreachable (BlockContext ec
, bool warn
)
42 // This conflicts with csc's way of doing this, but IMHO it's
43 // the right thing to do.
45 // If something is unreachable, we still check whether it's
46 // correct. This means that you cannot use unassigned variables
47 // in unreachable code, for instance.
51 ec
.Report
.Warning (162, 2, loc
, "Unreachable code detected");
53 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Block
, loc
);
54 bool ok
= Resolve (ec
);
55 ec
.KillFlowBranching ();
61 /// Return value indicates whether all code paths emitted return.
63 protected abstract void DoEmit (EmitContext ec
);
65 public virtual void Emit (EmitContext ec
)
72 // This routine must be overrided in derived classes and make copies
73 // of all the data that might be modified if resolved
75 protected abstract void CloneTo (CloneContext clonectx
, Statement target
);
77 public Statement
Clone (CloneContext clonectx
)
79 Statement s
= (Statement
) this.MemberwiseClone ();
80 CloneTo (clonectx
, s
);
84 public virtual Expression
CreateExpressionTree (ResolveContext ec
)
86 ec
.Report
.Error (834, loc
, "A lambda expression with statement body cannot be converted to an expresion tree");
90 public Statement
PerformClone ()
92 CloneContext clonectx
= new CloneContext ();
94 return Clone (clonectx
);
97 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey
);
100 public sealed class EmptyStatement
: Statement
{
102 private EmptyStatement () {}
104 public static readonly EmptyStatement Value
= new EmptyStatement ();
106 public override bool Resolve (BlockContext ec
)
111 public override bool ResolveUnreachable (BlockContext ec
, bool warn
)
116 protected override void DoEmit (EmitContext ec
)
120 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
124 protected override void CloneTo (CloneContext clonectx
, Statement target
)
130 public class If
: Statement
{
132 public Statement TrueStatement
;
133 public Statement FalseStatement
;
137 public If (Expression bool_expr
, Statement true_statement
, Location l
)
138 : this (bool_expr
, true_statement
, null, l
)
142 public If (Expression bool_expr
,
143 Statement true_statement
,
144 Statement false_statement
,
147 this.expr
= bool_expr
;
148 TrueStatement
= true_statement
;
149 FalseStatement
= false_statement
;
153 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
155 expr
.MutateHoistedGenericType (storey
);
156 TrueStatement
.MutateHoistedGenericType (storey
);
157 if (FalseStatement
!= null)
158 FalseStatement
.MutateHoistedGenericType (storey
);
161 public override bool Resolve (BlockContext ec
)
165 Report
.Debug (1, "START IF BLOCK", loc
);
167 expr
= expr
.Resolve (ec
);
172 // Dead code elimination
174 if (expr
is Constant
) {
175 bool take
= !((Constant
) expr
).IsDefaultValue
;
178 if (!TrueStatement
.Resolve (ec
))
181 if ((FalseStatement
!= null) &&
182 !FalseStatement
.ResolveUnreachable (ec
, true))
184 FalseStatement
= null;
186 if (!TrueStatement
.ResolveUnreachable (ec
, true))
188 TrueStatement
= null;
190 if ((FalseStatement
!= null) &&
191 !FalseStatement
.Resolve (ec
))
199 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
201 ok
&= TrueStatement
.Resolve (ec
);
203 is_true_ret
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
205 ec
.CurrentBranching
.CreateSibling ();
207 if (FalseStatement
!= null)
208 ok
&= FalseStatement
.Resolve (ec
);
210 ec
.EndFlowBranching ();
212 Report
.Debug (1, "END IF BLOCK", loc
);
217 protected override void DoEmit (EmitContext ec
)
219 ILGenerator ig
= ec
.ig
;
220 Label false_target
= ig
.DefineLabel ();
224 // If we're a boolean constant, Resolve() already
225 // eliminated dead code for us.
227 Constant c
= expr
as Constant
;
229 c
.EmitSideEffect (ec
);
231 if (!c
.IsDefaultValue
)
232 TrueStatement
.Emit (ec
);
233 else if (FalseStatement
!= null)
234 FalseStatement
.Emit (ec
);
239 expr
.EmitBranchable (ec
, false_target
, false);
241 TrueStatement
.Emit (ec
);
243 if (FalseStatement
!= null){
244 bool branch_emitted
= false;
246 end
= ig
.DefineLabel ();
248 ig
.Emit (OpCodes
.Br
, end
);
249 branch_emitted
= true;
252 ig
.MarkLabel (false_target
);
253 FalseStatement
.Emit (ec
);
258 ig
.MarkLabel (false_target
);
262 protected override void CloneTo (CloneContext clonectx
, Statement t
)
266 target
.expr
= expr
.Clone (clonectx
);
267 target
.TrueStatement
= TrueStatement
.Clone (clonectx
);
268 if (FalseStatement
!= null)
269 target
.FalseStatement
= FalseStatement
.Clone (clonectx
);
273 public class Do
: Statement
{
274 public Expression expr
;
275 public Statement EmbeddedStatement
;
277 public Do (Statement statement
, BooleanExpression bool_expr
, Location l
)
280 EmbeddedStatement
= statement
;
284 public override bool Resolve (BlockContext ec
)
288 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
290 bool was_unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
292 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
293 if (!EmbeddedStatement
.Resolve (ec
))
295 ec
.EndFlowBranching ();
297 if (ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
&& !was_unreachable
)
298 ec
.Report
.Warning (162, 2, expr
.Location
, "Unreachable code detected");
300 expr
= expr
.Resolve (ec
);
303 else if (expr
is Constant
){
304 bool infinite
= !((Constant
) expr
).IsDefaultValue
;
306 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
309 ec
.EndFlowBranching ();
314 protected override void DoEmit (EmitContext ec
)
316 ILGenerator ig
= ec
.ig
;
317 Label loop
= ig
.DefineLabel ();
318 Label old_begin
= ec
.LoopBegin
;
319 Label old_end
= ec
.LoopEnd
;
321 ec
.LoopBegin
= ig
.DefineLabel ();
322 ec
.LoopEnd
= ig
.DefineLabel ();
325 EmbeddedStatement
.Emit (ec
);
326 ig
.MarkLabel (ec
.LoopBegin
);
329 // Dead code elimination
331 if (expr
is Constant
){
332 bool res
= !((Constant
) expr
).IsDefaultValue
;
334 expr
.EmitSideEffect (ec
);
336 ec
.ig
.Emit (OpCodes
.Br
, loop
);
338 expr
.EmitBranchable (ec
, loop
, true);
340 ig
.MarkLabel (ec
.LoopEnd
);
342 ec
.LoopBegin
= old_begin
;
343 ec
.LoopEnd
= old_end
;
346 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
348 expr
.MutateHoistedGenericType (storey
);
349 EmbeddedStatement
.MutateHoistedGenericType (storey
);
352 protected override void CloneTo (CloneContext clonectx
, Statement t
)
356 target
.EmbeddedStatement
= EmbeddedStatement
.Clone (clonectx
);
357 target
.expr
= expr
.Clone (clonectx
);
361 public class While
: Statement
{
362 public Expression expr
;
363 public Statement Statement
;
364 bool infinite
, empty
;
366 public While (BooleanExpression bool_expr
, Statement statement
, Location l
)
368 this.expr
= bool_expr
;
369 Statement
= statement
;
373 public override bool Resolve (BlockContext ec
)
377 expr
= expr
.Resolve (ec
);
382 // Inform whether we are infinite or not
384 if (expr
is Constant
){
385 bool value = !((Constant
) expr
).IsDefaultValue
;
388 if (!Statement
.ResolveUnreachable (ec
, true))
396 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
398 ec
.CurrentBranching
.CreateSibling ();
400 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
401 if (!Statement
.Resolve (ec
))
403 ec
.EndFlowBranching ();
405 // There's no direct control flow from the end of the embedded statement to the end of the loop
406 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
408 ec
.EndFlowBranching ();
413 protected override void DoEmit (EmitContext ec
)
416 expr
.EmitSideEffect (ec
);
420 ILGenerator ig
= ec
.ig
;
421 Label old_begin
= ec
.LoopBegin
;
422 Label old_end
= ec
.LoopEnd
;
424 ec
.LoopBegin
= ig
.DefineLabel ();
425 ec
.LoopEnd
= ig
.DefineLabel ();
428 // Inform whether we are infinite or not
430 if (expr
is Constant
){
431 // expr is 'true', since the 'empty' case above handles the 'false' case
432 ig
.MarkLabel (ec
.LoopBegin
);
433 expr
.EmitSideEffect (ec
);
435 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
438 // Inform that we are infinite (ie, `we return'), only
439 // if we do not `break' inside the code.
441 ig
.MarkLabel (ec
.LoopEnd
);
443 Label while_loop
= ig
.DefineLabel ();
445 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
446 ig
.MarkLabel (while_loop
);
450 ig
.MarkLabel (ec
.LoopBegin
);
453 expr
.EmitBranchable (ec
, while_loop
, true);
455 ig
.MarkLabel (ec
.LoopEnd
);
458 ec
.LoopBegin
= old_begin
;
459 ec
.LoopEnd
= old_end
;
462 public override void Emit (EmitContext ec
)
467 protected override void CloneTo (CloneContext clonectx
, Statement t
)
469 While target
= (While
) t
;
471 target
.expr
= expr
.Clone (clonectx
);
472 target
.Statement
= Statement
.Clone (clonectx
);
475 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
477 expr
.MutateHoistedGenericType (storey
);
478 Statement
.MutateHoistedGenericType (storey
);
482 public class For
: Statement
{
484 Statement InitStatement
;
486 public Statement Statement
;
487 bool infinite
, empty
;
489 public For (Statement init_statement
,
490 BooleanExpression test
,
495 InitStatement
= init_statement
;
497 Increment
= increment
;
498 Statement
= statement
;
502 public override bool Resolve (BlockContext ec
)
506 if (InitStatement
!= null){
507 if (!InitStatement
.Resolve (ec
))
512 Test
= Test
.Resolve (ec
);
515 else if (Test
is Constant
){
516 bool value = !((Constant
) Test
).IsDefaultValue
;
519 if (!Statement
.ResolveUnreachable (ec
, true))
521 if ((Increment
!= null) &&
522 !Increment
.ResolveUnreachable (ec
, false))
532 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
534 ec
.CurrentBranching
.CreateSibling ();
536 bool was_unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
538 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
539 if (!Statement
.Resolve (ec
))
541 ec
.EndFlowBranching ();
543 if (Increment
!= null){
544 if (ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
) {
545 if (!Increment
.ResolveUnreachable (ec
, !was_unreachable
))
548 if (!Increment
.Resolve (ec
))
553 // There's no direct control flow from the end of the embedded statement to the end of the loop
554 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
556 ec
.EndFlowBranching ();
561 protected override void DoEmit (EmitContext ec
)
563 if (InitStatement
!= null && InitStatement
!= EmptyStatement
.Value
)
564 InitStatement
.Emit (ec
);
567 Test
.EmitSideEffect (ec
);
571 ILGenerator ig
= ec
.ig
;
572 Label old_begin
= ec
.LoopBegin
;
573 Label old_end
= ec
.LoopEnd
;
574 Label loop
= ig
.DefineLabel ();
575 Label test
= ig
.DefineLabel ();
577 ec
.LoopBegin
= ig
.DefineLabel ();
578 ec
.LoopEnd
= ig
.DefineLabel ();
580 ig
.Emit (OpCodes
.Br
, test
);
584 ig
.MarkLabel (ec
.LoopBegin
);
585 if (Increment
!= EmptyStatement
.Value
)
590 // If test is null, there is no test, and we are just
595 // The Resolve code already catches the case for
596 // Test == Constant (false) so we know that
599 if (Test
is Constant
) {
600 Test
.EmitSideEffect (ec
);
601 ig
.Emit (OpCodes
.Br
, loop
);
603 Test
.EmitBranchable (ec
, loop
, true);
607 ig
.Emit (OpCodes
.Br
, loop
);
608 ig
.MarkLabel (ec
.LoopEnd
);
610 ec
.LoopBegin
= old_begin
;
611 ec
.LoopEnd
= old_end
;
614 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
616 if (InitStatement
!= null)
617 InitStatement
.MutateHoistedGenericType (storey
);
619 Test
.MutateHoistedGenericType (storey
);
620 if (Increment
!= null)
621 Increment
.MutateHoistedGenericType (storey
);
623 Statement
.MutateHoistedGenericType (storey
);
626 protected override void CloneTo (CloneContext clonectx
, Statement t
)
628 For target
= (For
) t
;
630 if (InitStatement
!= null)
631 target
.InitStatement
= InitStatement
.Clone (clonectx
);
633 target
.Test
= Test
.Clone (clonectx
);
634 if (Increment
!= null)
635 target
.Increment
= Increment
.Clone (clonectx
);
636 target
.Statement
= Statement
.Clone (clonectx
);
640 public class StatementExpression
: Statement
{
641 ExpressionStatement expr
;
643 public StatementExpression (ExpressionStatement expr
)
649 public override bool Resolve (BlockContext ec
)
651 if (expr
!= null && expr
.eclass
== ExprClass
.Invalid
)
652 expr
= expr
.ResolveStatement (ec
);
656 protected override void DoEmit (EmitContext ec
)
658 expr
.EmitStatement (ec
);
661 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
663 expr
.MutateHoistedGenericType (storey
);
666 public override string ToString ()
668 return "StatementExpression (" + expr
+ ")";
671 protected override void CloneTo (CloneContext clonectx
, Statement t
)
673 StatementExpression target
= (StatementExpression
) t
;
675 target
.expr
= (ExpressionStatement
) expr
.Clone (clonectx
);
679 // A 'return' or a 'yield break'
680 public abstract class ExitStatement
: Statement
682 protected bool unwind_protect
;
683 protected abstract bool DoResolve (BlockContext ec
);
685 public virtual void Error_FinallyClause (Report Report
)
687 Report
.Error (157, loc
, "Control cannot leave the body of a finally clause");
690 public sealed override bool Resolve (BlockContext ec
)
695 unwind_protect
= ec
.CurrentBranching
.AddReturnOrigin (ec
.CurrentBranching
.CurrentUsageVector
, this);
697 ec
.NeedReturnLabel ();
698 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
704 /// Implements the return statement
706 public class Return
: ExitStatement
{
707 protected Expression Expr
;
708 public Return (Expression expr
, Location l
)
714 protected override bool DoResolve (BlockContext ec
)
717 if (ec
.ReturnType
== TypeManager
.void_type
)
720 ec
.Report
.Error (126, loc
,
721 "An object of a type convertible to `{0}' is required for the return statement",
722 TypeManager
.CSharpName (ec
.ReturnType
));
726 if (ec
.CurrentBlock
.Toplevel
.IsIterator
) {
727 ec
.Report
.Error (1622, loc
, "Cannot return a value from iterators. Use the yield return " +
728 "statement to return a value, or yield break to end the iteration");
731 AnonymousExpression am
= ec
.CurrentAnonymousMethod
;
732 if (am
== null && ec
.ReturnType
== TypeManager
.void_type
) {
733 ec
.Report
.Error (127, loc
, "`{0}': A return keyword must not be followed by any expression when method returns void",
734 ec
.GetSignatureForError ());
737 Expr
= Expr
.Resolve (ec
);
741 if (ec
.HasSet (ResolveContext
.Options
.InferReturnType
)) {
742 ec
.ReturnTypeInference
.AddCommonTypeBound (Expr
.Type
);
746 if (Expr
.Type
!= ec
.ReturnType
) {
747 Expr
= Convert
.ImplicitConversionRequired (ec
, Expr
, ec
.ReturnType
, loc
);
751 ec
.Report
.Error (1662, loc
,
752 "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",
753 am
.ContainerType
, am
.GetSignatureForError ());
762 protected override void DoEmit (EmitContext ec
)
768 ec
.ig
.Emit (OpCodes
.Stloc
, ec
.TemporaryReturn ());
772 ec
.ig
.Emit (OpCodes
.Leave
, ec
.ReturnLabel
);
774 ec
.ig
.Emit (OpCodes
.Ret
);
777 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
780 Expr
.MutateHoistedGenericType (storey
);
783 protected override void CloneTo (CloneContext clonectx
, Statement t
)
785 Return target
= (Return
) t
;
786 // It's null for simple return;
788 target
.Expr
= Expr
.Clone (clonectx
);
792 public class Goto
: Statement
{
794 LabeledStatement label
;
797 public override bool Resolve (BlockContext ec
)
799 unwind_protect
= ec
.CurrentBranching
.AddGotoOrigin (ec
.CurrentBranching
.CurrentUsageVector
, this);
800 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
804 public Goto (string label
, Location l
)
810 public string Target
{
811 get { return target; }
814 public void SetResolvedTarget (LabeledStatement label
)
817 label
.AddReference ();
820 protected override void CloneTo (CloneContext clonectx
, Statement target
)
825 protected override void DoEmit (EmitContext ec
)
828 throw new InternalErrorException ("goto emitted before target resolved");
829 Label l
= label
.LabelTarget (ec
);
830 ec
.ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, l
);
833 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
838 public class LabeledStatement
: Statement
{
845 FlowBranching
.UsageVector vectors
;
847 public LabeledStatement (string name
, Location l
)
853 public Label
LabelTarget (EmitContext ec
)
858 label
= ec
.ig
.DefineLabel ();
868 public bool IsDefined
{
869 get { return defined; }
872 public bool HasBeenReferenced
{
873 get { return referenced; }
876 public FlowBranching
.UsageVector JumpOrigins
{
877 get { return vectors; }
880 public void AddUsageVector (FlowBranching
.UsageVector vector
)
882 vector
= vector
.Clone ();
883 vector
.Next
= vectors
;
887 protected override void CloneTo (CloneContext clonectx
, Statement target
)
892 public override bool Resolve (BlockContext ec
)
894 // this flow-branching will be terminated when the surrounding block ends
895 ec
.StartFlowBranching (this);
899 protected override void DoEmit (EmitContext ec
)
901 if (ig
!= null && ig
!= ec
.ig
)
902 throw new InternalErrorException ("cannot happen");
904 ec
.ig
.MarkLabel (label
);
907 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
911 public void AddReference ()
919 /// `goto default' statement
921 public class GotoDefault
: Statement
{
923 public GotoDefault (Location l
)
928 protected override void CloneTo (CloneContext clonectx
, Statement target
)
933 public override bool Resolve (BlockContext ec
)
935 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
937 if (ec
.Switch
== null) {
938 ec
.Report
.Error (153, loc
, "A goto case is only valid inside a switch statement");
942 if (!ec
.Switch
.GotDefault
) {
943 FlowBranchingBlock
.Error_UnknownLabel (loc
, "default", ec
.Report
);
950 protected override void DoEmit (EmitContext ec
)
952 ec
.ig
.Emit (OpCodes
.Br
, ec
.Switch
.DefaultTarget
);
955 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
961 /// `goto case' statement
963 public class GotoCase
: Statement
{
967 public GotoCase (Expression e
, Location l
)
973 public override bool Resolve (BlockContext ec
)
975 if (ec
.Switch
== null){
976 ec
.Report
.Error (153, loc
, "A goto case is only valid inside a switch statement");
980 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
982 expr
= expr
.Resolve (ec
);
986 Constant c
= expr
as Constant
;
988 ec
.Report
.Error (150, expr
.Location
, "A constant value is expected");
992 Type type
= ec
.Switch
.SwitchType
;
993 Constant res
= c
.TryReduce (ec
, type
, c
.Location
);
995 c
.Error_ValueCannotBeConverted (ec
, loc
, type
, true);
999 if (!Convert
.ImplicitStandardConversionExists (c
, type
))
1000 ec
.Report
.Warning (469, 2, loc
,
1001 "The `goto case' value is not implicitly convertible to type `{0}'",
1002 TypeManager
.CSharpName (type
));
1004 object val
= res
.GetValue ();
1006 val
= SwitchLabel
.NullStringCase
;
1008 sl
= (SwitchLabel
) ec
.Switch
.Elements
[val
];
1011 FlowBranchingBlock
.Error_UnknownLabel (loc
, "case " +
1012 (c
.GetValue () == null ? "null" : val
.ToString ()), ec
.Report
);
1019 protected override void DoEmit (EmitContext ec
)
1021 ec
.ig
.Emit (OpCodes
.Br
, sl
.GetILLabelCode (ec
));
1024 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1026 expr
.MutateHoistedGenericType (storey
);
1029 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1031 GotoCase target
= (GotoCase
) t
;
1033 target
.expr
= expr
.Clone (clonectx
);
1037 public class Throw
: Statement
{
1040 public Throw (Expression expr
, Location l
)
1046 public override bool Resolve (BlockContext ec
)
1049 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1050 return ec
.CurrentBranching
.CheckRethrow (loc
);
1053 expr
= expr
.Resolve (ec
, ResolveFlags
.Type
| ResolveFlags
.VariableOrValue
);
1054 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1059 if (Convert
.ImplicitConversionExists (ec
, expr
, TypeManager
.exception_type
))
1060 expr
= Convert
.ImplicitConversion (ec
, expr
, TypeManager
.exception_type
, loc
);
1062 ec
.Report
.Error (155, expr
.Location
, "The type caught or thrown must be derived from System.Exception");
1067 protected override void DoEmit (EmitContext ec
)
1070 ec
.ig
.Emit (OpCodes
.Rethrow
);
1074 ec
.ig
.Emit (OpCodes
.Throw
);
1078 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1081 expr
.MutateHoistedGenericType (storey
);
1084 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1086 Throw target
= (Throw
) t
;
1089 target
.expr
= expr
.Clone (clonectx
);
1093 public class Break
: Statement
{
1095 public Break (Location l
)
1100 bool unwind_protect
;
1102 public override bool Resolve (BlockContext ec
)
1104 unwind_protect
= ec
.CurrentBranching
.AddBreakOrigin (ec
.CurrentBranching
.CurrentUsageVector
, loc
);
1105 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1109 protected override void DoEmit (EmitContext ec
)
1111 ec
.ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, ec
.LoopEnd
);
1114 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1118 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1124 public class Continue
: Statement
{
1126 public Continue (Location l
)
1131 bool unwind_protect
;
1133 public override bool Resolve (BlockContext ec
)
1135 unwind_protect
= ec
.CurrentBranching
.AddContinueOrigin (ec
.CurrentBranching
.CurrentUsageVector
, loc
);
1136 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
1140 protected override void DoEmit (EmitContext ec
)
1142 ec
.ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, ec
.LoopBegin
);
1145 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1149 protected override void CloneTo (CloneContext clonectx
, Statement t
)
1155 public interface ILocalVariable
1157 void Emit (EmitContext ec
);
1158 void EmitAssign (EmitContext ec
);
1159 void EmitAddressOf (EmitContext ec
);
1162 public interface IKnownVariable
{
1163 Block Block { get; }
1164 Location Location { get; }
1168 // The information about a user-perceived local variable
1170 public class LocalInfo
: IKnownVariable
, ILocalVariable
{
1171 public readonly FullNamedExpression Type
;
1173 public Type VariableType
;
1174 public readonly string Name
;
1175 public readonly Location Location
;
1176 public readonly Block Block
;
1178 public VariableInfo VariableInfo
;
1179 HoistedVariable hoisted_variant
;
1188 CompilerGenerated
= 64,
1192 public enum ReadOnlyContext
: byte {
1199 ReadOnlyContext ro_context
;
1200 LocalBuilder builder
;
1202 public LocalInfo (FullNamedExpression type
, string name
, Block block
, Location l
)
1210 public LocalInfo (DeclSpace ds
, Block block
, Location l
)
1212 VariableType
= ds
.IsGeneric
? ds
.CurrentType
: ds
.TypeBuilder
;
1217 public void ResolveVariable (EmitContext ec
)
1219 if (HoistedVariant
!= null)
1222 if (builder
== null) {
1225 // This is needed to compile on both .NET 1.x and .NET 2.x
1226 // the later introduced `DeclareLocal (Type t, bool pinned)'
1228 builder
= TypeManager
.DeclareLocalPinned (ec
.ig
, VariableType
);
1230 builder
= ec
.ig
.DeclareLocal (TypeManager
.TypeToReflectionType (VariableType
));
1234 public void Emit (EmitContext ec
)
1236 ec
.ig
.Emit (OpCodes
.Ldloc
, builder
);
1239 public void EmitAssign (EmitContext ec
)
1241 ec
.ig
.Emit (OpCodes
.Stloc
, builder
);
1244 public void EmitAddressOf (EmitContext ec
)
1246 ec
.ig
.Emit (OpCodes
.Ldloca
, builder
);
1249 public void EmitSymbolInfo (EmitContext ec
)
1251 if (builder
!= null)
1252 ec
.DefineLocalVariable (Name
, builder
);
1256 // Hoisted local variable variant
1258 public HoistedVariable HoistedVariant
{
1260 return hoisted_variant
;
1263 hoisted_variant
= value;
1267 public bool IsThisAssigned (BlockContext ec
, Block block
)
1269 if (VariableInfo
== null)
1270 throw new Exception ();
1272 if (!ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
))
1275 return VariableInfo
.TypeInfo
.IsFullyInitialized (ec
, VariableInfo
, block
.StartLocation
);
1278 public bool IsAssigned (BlockContext ec
)
1280 if (VariableInfo
== null)
1281 throw new Exception ();
1283 return !ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
);
1286 public bool Resolve (ResolveContext ec
)
1288 if (VariableType
!= null)
1291 TypeExpr texpr
= Type
.ResolveAsContextualType (ec
, false);
1295 VariableType
= texpr
.Type
;
1297 if (TypeManager
.IsGenericParameter (VariableType
))
1300 if (VariableType
.IsAbstract
&& VariableType
.IsSealed
) {
1301 FieldBase
.Error_VariableOfStaticClass (Location
, Name
, VariableType
, ec
.Report
);
1305 if (VariableType
.IsPointer
&& !ec
.IsUnsafe
)
1306 Expression
.UnsafeError (ec
, Location
);
1311 public bool IsConstant
{
1312 get { return (flags & Flags.IsConstant) != 0; }
1313 set { flags |= Flags.IsConstant; }
1316 public bool AddressTaken
{
1317 get { return (flags & Flags.AddressTaken) != 0; }
1318 set { flags |= Flags.AddressTaken; }
1321 public bool CompilerGenerated
{
1322 get { return (flags & Flags.CompilerGenerated) != 0; }
1323 set { flags |= Flags.CompilerGenerated; }
1326 public override string ToString ()
1328 return String
.Format ("LocalInfo ({0},{1},{2},{3})",
1329 Name
, Type
, VariableInfo
, Location
);
1333 get { return (flags & Flags.Used) != 0; }
1334 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1337 public bool ReadOnly
{
1338 get { return (flags & Flags.ReadOnly) != 0; }
1341 public void SetReadOnlyContext (ReadOnlyContext context
)
1343 flags
|= Flags
.ReadOnly
;
1344 ro_context
= context
;
1347 public string GetReadOnlyContext ()
1350 throw new InternalErrorException ("Variable is not readonly");
1352 switch (ro_context
) {
1353 case ReadOnlyContext
.Fixed
:
1354 return "fixed variable";
1355 case ReadOnlyContext
.Foreach
:
1356 return "foreach iteration variable";
1357 case ReadOnlyContext
.Using
:
1358 return "using variable";
1360 throw new NotImplementedException ();
1364 // Whether the variable is pinned, if Pinned the variable has been
1365 // allocated in a pinned slot with DeclareLocal.
1367 public bool Pinned
{
1368 get { return (flags & Flags.Pinned) != 0; }
1369 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1372 public bool IsThis
{
1373 get { return (flags & Flags.IsThis) != 0; }
1374 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1377 Block IKnownVariable
.Block
{
1378 get { return Block; }
1381 Location IKnownVariable
.Location
{
1382 get { return Location; }
1385 public LocalInfo
Clone (CloneContext clonectx
)
1388 // Variables in anonymous block are not resolved yet
1390 if (VariableType
== null)
1391 return new LocalInfo ((FullNamedExpression
) Type
.Clone (clonectx
), Name
, clonectx
.LookupBlock (Block
), Location
);
1394 // Variables in method block are resolved
1396 LocalInfo li
= new LocalInfo (null, Name
, clonectx
.LookupBlock (Block
), Location
);
1397 li
.VariableType
= VariableType
;
1403 /// Block represents a C# block.
1407 /// This class is used in a number of places: either to represent
1408 /// explicit blocks that the programmer places or implicit blocks.
1410 /// Implicit blocks are used as labels or to introduce variable
1413 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1414 /// they contain extra information that is not necessary on normal blocks.
1416 public class Block
: Statement
{
1417 public Block Parent
;
1418 public Location StartLocation
;
1419 public Location EndLocation
= Location
.Null
;
1421 public ExplicitBlock Explicit
;
1422 public ToplevelBlock Toplevel
; // TODO: Use Explicit
1429 VariablesInitialized
= 4,
1433 HasCapturedVariable
= 64,
1434 HasCapturedThis
= 1 << 7,
1435 IsExpressionTree
= 1 << 8
1438 protected Flags flags
;
1440 public bool Unchecked
{
1441 get { return (flags & Flags.Unchecked) != 0; }
1442 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1445 public bool Unsafe
{
1446 get { return (flags & Flags.Unsafe) != 0; }
1447 set { flags |= Flags.Unsafe; }
1451 // The statements in this block
1453 protected ArrayList statements
;
1456 // An array of Blocks. We keep track of children just
1457 // to generate the local variable declarations.
1459 // Statements and child statements are handled through the
1465 // Labels. (label, block) pairs.
1467 protected HybridDictionary labels
;
1470 // Keeps track of (name, type) pairs
1472 IDictionary variables
;
1475 // Keeps track of constants
1476 HybridDictionary constants
;
1479 // Temporary variables.
1481 ArrayList temporary_variables
;
1484 // If this is a switch section, the enclosing switch block.
1488 protected ArrayList scope_initializers
;
1490 ArrayList anonymous_children
;
1492 protected static int id
;
1496 int assignable_slots
;
1497 bool unreachable_shown
;
1500 public Block (Block parent
)
1501 : this (parent
, (Flags
) 0, Location
.Null
, Location
.Null
)
1504 public Block (Block parent
, Flags flags
)
1505 : this (parent
, flags
, Location
.Null
, Location
.Null
)
1508 public Block (Block parent
, Location start
, Location end
)
1509 : this (parent
, (Flags
) 0, start
, end
)
1513 // Useful when TopLevel block is downgraded to normal block
1515 public Block (ToplevelBlock parent
, ToplevelBlock source
)
1516 : this (parent
, source
.flags
, source
.StartLocation
, source
.EndLocation
)
1518 statements
= source
.statements
;
1519 children
= source
.children
;
1520 labels
= source
.labels
;
1521 variables
= source
.variables
;
1522 constants
= source
.constants
;
1523 switch_block
= source
.switch_block
;
1526 public Block (Block parent
, Flags flags
, Location start
, Location end
)
1528 if (parent
!= null) {
1529 parent
.AddChild (this);
1531 // the appropriate constructors will fixup these fields
1532 Toplevel
= parent
.Toplevel
;
1533 Explicit
= parent
.Explicit
;
1536 this.Parent
= parent
;
1538 this.StartLocation
= start
;
1539 this.EndLocation
= end
;
1542 statements
= new ArrayList (4);
1545 public Block
CreateSwitchBlock (Location start
)
1547 // FIXME: should this be implicit?
1548 Block new_block
= new ExplicitBlock (this, start
, start
);
1549 new_block
.switch_block
= this;
1554 get { return this_id; }
1557 public IDictionary Variables
{
1559 if (variables
== null)
1560 variables
= new ListDictionary ();
1565 void AddChild (Block b
)
1567 if (children
== null)
1568 children
= new ArrayList (1);
1573 public void SetEndLocation (Location loc
)
1578 protected void Error_158 (string name
, Location loc
)
1580 Toplevel
.Report
.Error (158, loc
, "The label `{0}' shadows another label " +
1581 "by the same name in a contained scope", name
);
1585 /// Adds a label to the current block.
1589 /// false if the name already exists in this block. true
1593 public bool AddLabel (LabeledStatement target
)
1595 if (switch_block
!= null)
1596 return switch_block
.AddLabel (target
);
1598 string name
= target
.Name
;
1601 while (cur
!= null) {
1602 LabeledStatement s
= cur
.DoLookupLabel (name
);
1604 Toplevel
.Report
.SymbolRelatedToPreviousError (s
.loc
, s
.Name
);
1605 Toplevel
.Report
.Error (140, target
.loc
, "The label `{0}' is a duplicate", name
);
1609 if (this == Explicit
)
1615 while (cur
!= null) {
1616 if (cur
.DoLookupLabel (name
) != null) {
1617 Error_158 (name
, target
.loc
);
1621 if (children
!= null) {
1622 foreach (Block b
in children
) {
1623 LabeledStatement s
= b
.DoLookupLabel (name
);
1627 Toplevel
.Report
.SymbolRelatedToPreviousError (s
.loc
, s
.Name
);
1628 Error_158 (name
, target
.loc
);
1636 Toplevel
.CheckError158 (name
, target
.loc
);
1639 labels
= new HybridDictionary();
1641 labels
.Add (name
, target
);
1645 public LabeledStatement
LookupLabel (string name
)
1647 LabeledStatement s
= DoLookupLabel (name
);
1651 if (children
== null)
1654 foreach (Block child
in children
) {
1655 if (Explicit
!= child
.Explicit
)
1658 s
= child
.LookupLabel (name
);
1666 LabeledStatement
DoLookupLabel (string name
)
1668 if (switch_block
!= null)
1669 return switch_block
.LookupLabel (name
);
1672 if (labels
.Contains (name
))
1673 return ((LabeledStatement
) labels
[name
]);
1678 public bool CheckInvariantMeaningInBlock (string name
, Expression e
, Location loc
)
1681 IKnownVariable kvi
= b
.Explicit
.GetKnownVariable (name
);
1682 while (kvi
== null) {
1683 b
= b
.Explicit
.Parent
;
1686 kvi
= b
.Explicit
.GetKnownVariable (name
);
1692 // Is kvi.Block nested inside 'b'
1693 if (b
.Explicit
!= kvi
.Block
.Explicit
) {
1695 // If a variable by the same name it defined in a nested block of this
1696 // block, we violate the invariant meaning in a block.
1699 Toplevel
.Report
.SymbolRelatedToPreviousError (kvi
.Location
, name
);
1700 Toplevel
.Report
.Error (135, loc
, "`{0}' conflicts with a declaration in a child block", name
);
1705 // It's ok if the definition is in a nested subblock of b, but not
1706 // nested inside this block -- a definition in a sibling block
1707 // should not affect us.
1713 // Block 'b' and kvi.Block are the same textual block.
1714 // However, different variables are extant.
1716 // Check if the variable is in scope in both blocks. We use
1717 // an indirect check that depends on AddVariable doing its
1718 // part in maintaining the invariant-meaning-in-block property.
1720 if (e
is VariableReference
|| (e
is Constant
&& b
.GetLocalInfo (name
) != null))
1723 if (this is ToplevelBlock
) {
1724 Toplevel
.Report
.SymbolRelatedToPreviousError (kvi
.Location
, name
);
1725 e
.Error_VariableIsUsedBeforeItIsDeclared (Toplevel
.Report
, name
);
1730 // Even though we detected the error when the name is used, we
1731 // treat it as if the variable declaration was in error.
1733 Toplevel
.Report
.SymbolRelatedToPreviousError (loc
, name
);
1734 Error_AlreadyDeclared (kvi
.Location
, name
, "parent or current");
1738 protected virtual bool CheckParentConflictName (ToplevelBlock block
, string name
, Location l
)
1740 LocalInfo vi
= GetLocalInfo (name
);
1742 block
.Report
.SymbolRelatedToPreviousError (vi
.Location
, name
);
1743 if (Explicit
== vi
.Block
.Explicit
) {
1744 Error_AlreadyDeclared (l
, name
, null);
1746 Error_AlreadyDeclared (l
, name
, this is ToplevelBlock
?
1747 "parent or current" : "parent");
1752 if (block
!= null) {
1753 Expression e
= block
.GetParameterReference (name
, Location
.Null
);
1755 ParameterReference pr
= e
as ParameterReference
;
1756 if (this is Linq
.QueryBlock
&& (pr
!= null && pr
.Parameter
is Linq
.QueryBlock
.ImplicitQueryParameter
|| e
is MemberAccess
))
1757 Error_AlreadyDeclared (loc
, name
);
1759 Error_AlreadyDeclared (loc
, name
, "parent or current");
1767 public LocalInfo
AddVariable (Expression type
, string name
, Location l
)
1769 if (!CheckParentConflictName (Toplevel
, name
, l
))
1772 if (Toplevel
.GenericMethod
!= null) {
1773 foreach (TypeParameter tp
in Toplevel
.GenericMethod
.CurrentTypeParameters
) {
1774 if (tp
.Name
== name
) {
1775 Toplevel
.Report
.SymbolRelatedToPreviousError (tp
);
1776 Error_AlreadyDeclaredTypeParameter (Toplevel
.Report
, loc
, name
, "local variable");
1782 IKnownVariable kvi
= Explicit
.GetKnownVariable (name
);
1784 Toplevel
.Report
.SymbolRelatedToPreviousError (kvi
.Location
, name
);
1785 Error_AlreadyDeclared (l
, name
, "child");
1789 LocalInfo vi
= new LocalInfo ((FullNamedExpression
) type
, name
, this, l
);
1792 if ((flags
& Flags
.VariablesInitialized
) != 0)
1793 throw new InternalErrorException ("block has already been resolved");
1798 protected virtual void AddVariable (LocalInfo li
)
1800 Variables
.Add (li
.Name
, li
);
1801 Explicit
.AddKnownVariable (li
.Name
, li
);
1804 protected virtual void Error_AlreadyDeclared (Location loc
, string var, string reason
)
1806 if (reason
== null) {
1807 Error_AlreadyDeclared (loc
, var);
1811 Toplevel
.Report
.Error (136, loc
, "A local variable named `{0}' cannot be declared " +
1812 "in this scope because it would give a different meaning " +
1813 "to `{0}', which is already used in a `{1}' scope " +
1814 "to denote something else", var, reason
);
1817 protected virtual void Error_AlreadyDeclared (Location loc
, string name
)
1819 Toplevel
.Report
.Error (128, loc
,
1820 "A local variable named `{0}' is already defined in this scope", name
);
1823 public virtual void Error_AlreadyDeclaredTypeParameter (Report r
, Location loc
, string name
, string conflict
)
1825 r
.Error (412, loc
, "The type parameter name `{0}' is the same as `{1}'",
1829 public bool AddConstant (Expression type
, string name
, Expression
value, Location l
)
1831 if (AddVariable (type
, name
, l
) == null)
1834 if (constants
== null)
1835 constants
= new HybridDictionary();
1837 constants
.Add (name
, value);
1839 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1844 static int next_temp_id
= 0;
1846 public LocalInfo
AddTemporaryVariable (TypeExpr te
, Location loc
)
1848 Report
.Debug (64, "ADD TEMPORARY", this, Toplevel
, loc
);
1850 if (temporary_variables
== null)
1851 temporary_variables
= new ArrayList ();
1853 int id
= ++next_temp_id
;
1854 string name
= "$s_" + id
.ToString ();
1856 LocalInfo li
= new LocalInfo (te
, name
, this, loc
);
1857 li
.CompilerGenerated
= true;
1858 temporary_variables
.Add (li
);
1862 public LocalInfo
GetLocalInfo (string name
)
1865 for (Block b
= this; b
!= null; b
= b
.Parent
) {
1866 if (b
.variables
!= null) {
1867 ret
= (LocalInfo
) b
.variables
[name
];
1876 public Expression
GetVariableType (string name
)
1878 LocalInfo vi
= GetLocalInfo (name
);
1879 return vi
== null ? null : vi
.Type
;
1882 public Expression
GetConstantExpression (string name
)
1884 for (Block b
= this; b
!= null; b
= b
.Parent
) {
1885 if (b
.constants
!= null) {
1886 Expression ret
= b
.constants
[name
] as Expression
;
1895 // It should be used by expressions which require to
1896 // register a statement during resolve process.
1898 public void AddScopeStatement (Statement s
)
1900 if (scope_initializers
== null)
1901 scope_initializers
= new ArrayList ();
1903 scope_initializers
.Add (s
);
1906 public void AddStatement (Statement s
)
1909 flags
|= Flags
.BlockUsed
;
1913 get { return (flags & Flags.BlockUsed) != 0; }
1918 flags
|= Flags
.BlockUsed
;
1921 public bool HasRet
{
1922 get { return (flags & Flags.HasRet) != 0; }
1925 public int AssignableSlots
{
1928 // if ((flags & Flags.VariablesInitialized) == 0)
1929 // throw new Exception ("Variables have not been initialized yet");
1930 return assignable_slots
;
1934 public ArrayList AnonymousChildren
{
1935 get { return anonymous_children; }
1938 public void AddAnonymousChild (ToplevelBlock b
)
1940 if (anonymous_children
== null)
1941 anonymous_children
= new ArrayList ();
1943 anonymous_children
.Add (b
);
1946 void DoResolveConstants (BlockContext ec
)
1948 if (constants
== null)
1951 if (variables
== null)
1952 throw new InternalErrorException ("cannot happen");
1954 foreach (DictionaryEntry de
in variables
) {
1955 string name
= (string) de
.Key
;
1956 LocalInfo vi
= (LocalInfo
) de
.Value
;
1957 Type variable_type
= vi
.VariableType
;
1959 if (variable_type
== null) {
1960 if (vi
.Type
is VarExpr
)
1961 ec
.Report
.Error (822, vi
.Type
.Location
, "An implicitly typed local variable cannot be a constant");
1966 Expression cv
= (Expression
) constants
[name
];
1970 // Don't let 'const int Foo = Foo;' succeed.
1971 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1972 // which in turn causes the 'must be constant' error to be triggered.
1973 constants
.Remove (name
);
1975 if (!Const
.IsConstantTypeValid (variable_type
)) {
1976 Const
.Error_InvalidConstantType (variable_type
, loc
, ec
.Report
);
1980 ec
.CurrentBlock
= this;
1982 using (ec
.With (ResolveContext
.Options
.ConstantCheckState
, (flags
& Flags
.Unchecked
) == 0)) {
1983 e
= cv
.Resolve (ec
);
1988 Constant ce
= e
as Constant
;
1990 Const
.Error_ExpressionMustBeConstant (vi
.Location
, name
, ec
.Report
);
1994 e
= ce
.ConvertImplicitly (variable_type
);
1996 if (TypeManager
.IsReferenceType (variable_type
))
1997 Const
.Error_ConstantCanBeInitializedWithNullOnly (variable_type
, vi
.Location
, vi
.Name
, ec
.Report
);
1999 ce
.Error_ValueCannotBeConverted (ec
, vi
.Location
, variable_type
, false);
2003 constants
.Add (name
, e
);
2004 vi
.IsConstant
= true;
2008 protected void ResolveMeta (BlockContext ec
, int offset
)
2010 Report
.Debug (64, "BLOCK RESOLVE META", this, Parent
);
2012 // If some parent block was unsafe, we remain unsafe even if this block
2013 // isn't explicitly marked as such.
2014 using (ec
.With (ResolveContext
.Options
.UnsafeScope
, ec
.IsUnsafe
| Unsafe
)) {
2015 flags
|= Flags
.VariablesInitialized
;
2017 if (variables
!= null) {
2018 foreach (LocalInfo li
in variables
.Values
) {
2019 if (!li
.Resolve (ec
))
2021 li
.VariableInfo
= new VariableInfo (li
, offset
);
2022 offset
+= li
.VariableInfo
.Length
;
2025 assignable_slots
= offset
;
2027 DoResolveConstants (ec
);
2029 if (children
== null)
2031 foreach (Block b
in children
)
2032 b
.ResolveMeta (ec
, offset
);
2037 // Emits the local variable declarations for a block
2039 public virtual void EmitMeta (EmitContext ec
)
2041 if (variables
!= null){
2042 foreach (LocalInfo vi
in variables
.Values
)
2043 vi
.ResolveVariable (ec
);
2046 if (temporary_variables
!= null) {
2047 for (int i
= 0; i
< temporary_variables
.Count
; i
++)
2048 ((LocalInfo
)temporary_variables
[i
]).ResolveVariable(ec
);
2051 if (children
!= null) {
2052 for (int i
= 0; i
< children
.Count
; i
++)
2053 ((Block
)children
[i
]).EmitMeta(ec
);
2057 void UsageWarning (BlockContext ec
)
2059 if (variables
== null || ec
.Report
.WarningLevel
< 3)
2062 foreach (DictionaryEntry de
in variables
) {
2063 LocalInfo vi
= (LocalInfo
) de
.Value
;
2066 string name
= (string) de
.Key
;
2068 // vi.VariableInfo can be null for 'catch' variables
2069 if (vi
.VariableInfo
!= null && vi
.VariableInfo
.IsEverAssigned
)
2070 ec
.Report
.Warning (219, 3, vi
.Location
, "The variable `{0}' is assigned but its value is never used", name
);
2072 ec
.Report
.Warning (168, 3, vi
.Location
, "The variable `{0}' is declared but never used", name
);
2077 static void CheckPossibleMistakenEmptyStatement (BlockContext ec
, Statement s
)
2081 // Some statements are wrapped by a Block. Since
2082 // others' internal could be changed, here I treat
2083 // them as possibly wrapped by Block equally.
2084 Block b
= s
as Block
;
2085 if (b
!= null && b
.statements
.Count
== 1)
2086 s
= (Statement
) b
.statements
[0];
2089 body
= ((Lock
) s
).Statement
;
2091 body
= ((For
) s
).Statement
;
2092 else if (s
is Foreach
)
2093 body
= ((Foreach
) s
).Statement
;
2094 else if (s
is While
)
2095 body
= ((While
) s
).Statement
;
2096 else if (s
is Fixed
)
2097 body
= ((Fixed
) s
).Statement
;
2098 else if (s
is Using
)
2099 body
= ((Using
) s
).EmbeddedStatement
;
2100 else if (s
is UsingTemporary
)
2101 body
= ((UsingTemporary
) s
).Statement
;
2105 if (body
== null || body
is EmptyStatement
)
2106 ec
.Report
.Warning (642, 3, s
.loc
, "Possible mistaken empty statement");
2109 public override bool Resolve (BlockContext ec
)
2111 Block prev_block
= ec
.CurrentBlock
;
2114 int errors
= ec
.Report
.Errors
;
2116 ec
.CurrentBlock
= this;
2117 ec
.StartFlowBranching (this);
2119 Report
.Debug (4, "RESOLVE BLOCK", StartLocation
, ec
.CurrentBranching
);
2122 // Compiler generated scope statements
2124 if (scope_initializers
!= null) {
2125 foreach (Statement s
in scope_initializers
)
2130 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2131 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2132 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2133 // responsible for handling the situation.
2135 int statement_count
= statements
.Count
;
2136 for (int ix
= 0; ix
< statement_count
; ix
++){
2137 Statement s
= (Statement
) statements
[ix
];
2138 // Check possible empty statement (CS0642)
2139 if (ix
+ 1 < statement_count
&& ec
.Report
.WarningLevel
>= 3 &&
2140 statements
[ix
+ 1] is ExplicitBlock
)
2141 CheckPossibleMistakenEmptyStatement (ec
, s
);
2144 // Warn if we detect unreachable code.
2147 if (s
is EmptyStatement
)
2150 if (!unreachable_shown
&& !(s
is LabeledStatement
)) {
2151 ec
.Report
.Warning (162, 2, s
.loc
, "Unreachable code detected");
2152 unreachable_shown
= true;
2155 Block c_block
= s
as Block
;
2156 if (c_block
!= null)
2157 c_block
.unreachable
= c_block
.unreachable_shown
= true;
2161 // Note that we're not using ResolveUnreachable() for unreachable
2162 // statements here. ResolveUnreachable() creates a temporary
2163 // flow branching and kills it afterwards. This leads to problems
2164 // if you have two unreachable statements where the first one
2165 // assigns a variable and the second one tries to access it.
2168 if (!s
.Resolve (ec
)) {
2170 if (ec
.IsInProbingMode
)
2173 statements
[ix
] = EmptyStatement
.Value
;
2177 if (unreachable
&& !(s
is LabeledStatement
) && !(s
is Block
))
2178 statements
[ix
] = EmptyStatement
.Value
;
2180 unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
;
2181 if (unreachable
&& s
is LabeledStatement
)
2182 throw new InternalErrorException ("should not happen");
2185 Report
.Debug (4, "RESOLVE BLOCK DONE", StartLocation
,
2186 ec
.CurrentBranching
, statement_count
);
2188 while (ec
.CurrentBranching
is FlowBranchingLabeled
)
2189 ec
.EndFlowBranching ();
2191 bool flow_unreachable
= ec
.EndFlowBranching ();
2193 ec
.CurrentBlock
= prev_block
;
2195 if (flow_unreachable
)
2196 flags
|= Flags
.HasRet
;
2198 // If we're a non-static `struct' constructor which doesn't have an
2199 // initializer, then we must initialize all of the struct's fields.
2200 if (this == Toplevel
&& !Toplevel
.IsThisAssigned (ec
) && !flow_unreachable
)
2203 if ((labels
!= null) && (ec
.Report
.WarningLevel
>= 2)) {
2204 foreach (LabeledStatement label
in labels
.Values
)
2205 if (!label
.HasBeenReferenced
)
2206 ec
.Report
.Warning (164, 2, label
.loc
, "This label has not been referenced");
2209 if (ok
&& errors
== ec
.Report
.Errors
)
2215 public override bool ResolveUnreachable (BlockContext ec
, bool warn
)
2217 unreachable_shown
= true;
2221 ec
.Report
.Warning (162, 2, loc
, "Unreachable code detected");
2223 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Block
, loc
);
2224 bool ok
= Resolve (ec
);
2225 ec
.KillFlowBranching ();
2230 protected override void DoEmit (EmitContext ec
)
2232 for (int ix
= 0; ix
< statements
.Count
; ix
++){
2233 Statement s
= (Statement
) statements
[ix
];
2238 public override void Emit (EmitContext ec
)
2240 if (scope_initializers
!= null)
2241 EmitScopeInitializers (ec
);
2243 ec
.Mark (StartLocation
);
2246 if (SymbolWriter
.HasSymbolWriter
)
2247 EmitSymbolInfo (ec
);
2250 protected void EmitScopeInitializers (EmitContext ec
)
2252 SymbolWriter
.OpenCompilerGeneratedBlock (ec
.ig
);
2254 using (ec
.With (EmitContext
.Options
.OmitDebugInfo
, true)) {
2255 foreach (Statement s
in scope_initializers
)
2259 SymbolWriter
.CloseCompilerGeneratedBlock (ec
.ig
);
2262 protected virtual void EmitSymbolInfo (EmitContext ec
)
2264 if (variables
!= null) {
2265 foreach (LocalInfo vi
in variables
.Values
) {
2266 vi
.EmitSymbolInfo (ec
);
2271 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
2273 MutateVariables (storey
);
2275 if (scope_initializers
!= null) {
2276 foreach (Statement s
in scope_initializers
)
2277 s
.MutateHoistedGenericType (storey
);
2280 foreach (Statement s
in statements
)
2281 s
.MutateHoistedGenericType (storey
);
2284 void MutateVariables (AnonymousMethodStorey storey
)
2286 if (variables
!= null) {
2287 foreach (LocalInfo vi
in variables
.Values
) {
2288 vi
.VariableType
= storey
.MutateType (vi
.VariableType
);
2292 if (temporary_variables
!= null) {
2293 foreach (LocalInfo vi
in temporary_variables
)
2294 vi
.VariableType
= storey
.MutateType (vi
.VariableType
);
2298 public override string ToString ()
2300 return String
.Format ("{0} ({1}:{2})", GetType (),ID
, StartLocation
);
2303 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2305 Block target
= (Block
) t
;
2307 clonectx
.AddBlockMap (this, target
);
2309 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2310 target
.Explicit
= (ExplicitBlock
) clonectx
.LookupBlock (Explicit
);
2312 target
.Parent
= clonectx
.RemapBlockCopy (Parent
);
2314 if (variables
!= null){
2315 target
.variables
= new Hashtable ();
2317 foreach (DictionaryEntry de
in variables
){
2318 LocalInfo newlocal
= ((LocalInfo
) de
.Value
).Clone (clonectx
);
2319 target
.variables
[de
.Key
] = newlocal
;
2320 clonectx
.AddVariableMap ((LocalInfo
) de
.Value
, newlocal
);
2324 target
.statements
= new ArrayList (statements
.Count
);
2325 foreach (Statement s
in statements
)
2326 target
.statements
.Add (s
.Clone (clonectx
));
2328 if (target
.children
!= null){
2329 target
.children
= new ArrayList (children
.Count
);
2330 foreach (Block b
in children
){
2331 target
.children
.Add (clonectx
.LookupBlock (b
));
2336 // TODO: labels, switch_block, constants (?), anonymous_children
2341 public class ExplicitBlock
: Block
{
2342 HybridDictionary known_variables
;
2343 protected AnonymousMethodStorey am_storey
;
2345 public ExplicitBlock (Block parent
, Location start
, Location end
)
2346 : this (parent
, (Flags
) 0, start
, end
)
2350 public ExplicitBlock (Block parent
, Flags flags
, Location start
, Location end
)
2351 : base (parent
, flags
, start
, end
)
2353 this.Explicit
= this;
2357 // Marks a variable with name @name as being used in this or a child block.
2358 // If a variable name has been used in a child block, it's illegal to
2359 // declare a variable with the same name in the current block.
2361 internal void AddKnownVariable (string name
, IKnownVariable info
)
2363 if (known_variables
== null)
2364 known_variables
= new HybridDictionary();
2366 known_variables
[name
] = info
;
2369 Parent
.Explicit
.AddKnownVariable (name
, info
);
2372 public AnonymousMethodStorey AnonymousMethodStorey
{
2373 get { return am_storey; }
2377 // Creates anonymous method storey in current block
2379 public AnonymousMethodStorey
CreateAnonymousMethodStorey (ResolveContext ec
)
2382 // When referencing a variable in iterator storey from children anonymous method
2384 if (Toplevel
.am_storey
is IteratorStorey
) {
2385 return Toplevel
.am_storey
;
2389 // An iterator has only 1 storey block
2391 if (ec
.CurrentIterator
!= null)
2392 return ec
.CurrentIterator
.Storey
;
2394 if (am_storey
== null) {
2395 MemberBase mc
= ec
.MemberContext
as MemberBase
;
2396 GenericMethod gm
= mc
== null ? null : mc
.GenericMethod
;
2399 // Creates anonymous method storey for this block
2401 am_storey
= new AnonymousMethodStorey (this, ec
.CurrentTypeDefinition
, mc
, gm
, "AnonStorey");
2407 public override void Emit (EmitContext ec
)
2409 if (am_storey
!= null)
2410 am_storey
.EmitStoreyInstantiation (ec
);
2412 bool emit_debug_info
= SymbolWriter
.HasSymbolWriter
&& Parent
!= null && !(am_storey
is IteratorStorey
);
2413 if (emit_debug_info
)
2418 if (emit_debug_info
)
2422 public override void EmitMeta (EmitContext ec
)
2425 // Creates anonymous method storey
2427 if (am_storey
!= null) {
2428 if (ec
.CurrentAnonymousMethod
!= null && ec
.CurrentAnonymousMethod
.Storey
!= null) {
2430 // Creates parent storey reference when hoisted this is accessible
2432 if (am_storey
.OriginalSourceBlock
.Explicit
.HasCapturedThis
) {
2433 ExplicitBlock parent
= Toplevel
.Parent
.Explicit
;
2436 // Hoisted this exists in top-level parent storey only
2438 while (parent
.am_storey
== null || parent
.am_storey
.Parent
is AnonymousMethodStorey
)
2439 parent
= parent
.Parent
.Explicit
;
2441 am_storey
.AddParentStoreyReference (parent
.am_storey
);
2444 am_storey
.ChangeParentStorey (ec
.CurrentAnonymousMethod
.Storey
);
2447 am_storey
.DefineType ();
2448 am_storey
.ResolveType ();
2449 am_storey
.Define ();
2450 am_storey
.Parent
.PartialContainer
.AddCompilerGeneratedClass (am_storey
);
2452 ArrayList ref_blocks
= am_storey
.ReferencesFromChildrenBlock
;
2453 if (ref_blocks
!= null) {
2454 foreach (ExplicitBlock ref_block
in ref_blocks
) {
2455 for (ExplicitBlock b
= ref_block
.Explicit
; b
!= this; b
= b
.Parent
.Explicit
) {
2456 if (b
.am_storey
!= null) {
2457 b
.am_storey
.AddParentStoreyReference (am_storey
);
2459 // Stop propagation inside same top block
2460 if (b
.Toplevel
== Toplevel
)
2465 b
.HasCapturedVariable
= true;
2474 internal IKnownVariable
GetKnownVariable (string name
)
2476 return known_variables
== null ? null : (IKnownVariable
) known_variables
[name
];
2479 public bool HasCapturedThis
2481 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2482 get { return (flags & Flags.HasCapturedThis) != 0; }
2485 public bool HasCapturedVariable
2487 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2488 get { return (flags & Flags.HasCapturedVariable) != 0; }
2491 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2493 ExplicitBlock target
= (ExplicitBlock
) t
;
2494 target
.known_variables
= null;
2495 base.CloneTo (clonectx
, t
);
2499 public class ToplevelParameterInfo
: IKnownVariable
{
2500 public readonly ToplevelBlock Block
;
2501 public readonly int Index
;
2502 public VariableInfo VariableInfo
;
2504 Block IKnownVariable
.Block
{
2505 get { return Block; }
2507 public Parameter Parameter
{
2508 get { return Block.Parameters [Index]; }
2511 public Type ParameterType
{
2512 get { return Block.Parameters.Types [Index]; }
2515 public Location Location
{
2516 get { return Parameter.Location; }
2519 public ToplevelParameterInfo (ToplevelBlock block
, int idx
)
2527 // A toplevel block contains extra information, the split is done
2528 // only to separate information that would otherwise bloat the more
2529 // lightweight Block.
2531 // In particular, this was introduced when the support for Anonymous
2532 // Methods was implemented.
2534 public class ToplevelBlock
: ExplicitBlock
2537 // Block is converted to an expression
2539 sealed class BlockScopeExpression
: Expression
2542 readonly ToplevelBlock block
;
2544 public BlockScopeExpression (Expression child
, ToplevelBlock block
)
2550 public override Expression
CreateExpressionTree (ResolveContext ec
)
2552 throw new NotSupportedException ();
2555 public override Expression
DoResolve (ResolveContext ec
)
2560 child
= child
.Resolve (ec
);
2564 eclass
= child
.eclass
;
2569 public override void Emit (EmitContext ec
)
2571 block
.EmitMeta (ec
);
2572 block
.EmitScopeInitializers (ec
);
2576 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
2578 type
= storey
.MutateType (type
);
2579 child
.MutateHoistedGenericType (storey
);
2580 block
.MutateHoistedGenericType (storey
);
2584 GenericMethod generic
;
2585 protected ParametersCompiled parameters
;
2586 ToplevelParameterInfo
[] parameter_info
;
2587 LocalInfo this_variable
;
2590 CompilerContext compiler
;
2592 public HoistedVariable HoistedThisVariable
;
2594 public bool Resolved
{
2601 // The parameters for the block.
2603 public ParametersCompiled Parameters
{
2604 get { return parameters; }
2607 public Report Report
{
2608 get { return compiler.Report; }
2611 public GenericMethod GenericMethod
{
2612 get { return generic; }
2615 public ToplevelBlock Container
{
2616 get { return Parent == null ? null : Parent.Toplevel; }
2619 public ToplevelBlock (CompilerContext ctx
, Block parent
, ParametersCompiled parameters
, Location start
) :
2620 this (ctx
, parent
, (Flags
) 0, parameters
, start
)
2624 public ToplevelBlock (CompilerContext ctx
, Block parent
, ParametersCompiled parameters
, GenericMethod generic
, Location start
) :
2625 this (ctx
, parent
, parameters
, start
)
2627 this.generic
= generic
;
2630 public ToplevelBlock (CompilerContext ctx
, ParametersCompiled parameters
, Location start
) :
2631 this (ctx
, null, (Flags
) 0, parameters
, start
)
2635 ToplevelBlock (CompilerContext ctx
, Flags flags
, ParametersCompiled parameters
, Location start
) :
2636 this (ctx
, null, flags
, parameters
, start
)
2640 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2641 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2642 public ToplevelBlock (CompilerContext ctx
, Block parent
, Flags flags
, ParametersCompiled parameters
, Location start
) :
2643 base (null, flags
, start
, Location
.Null
)
2645 this.compiler
= ctx
;
2646 this.Toplevel
= this;
2648 this.parameters
= parameters
;
2649 this.Parent
= parent
;
2651 parent
.AddAnonymousChild (this);
2653 if (!this.parameters
.IsEmpty
)
2654 ProcessParameters ();
2657 public ToplevelBlock (CompilerContext ctx
, Location loc
)
2658 : this (ctx
, null, (Flags
) 0, ParametersCompiled
.EmptyReadOnlyParameters
, loc
)
2662 protected override void CloneTo (CloneContext clonectx
, Statement t
)
2664 ToplevelBlock target
= (ToplevelBlock
) t
;
2665 base.CloneTo (clonectx
, t
);
2667 if (parameters
.Count
!= 0)
2668 target
.parameter_info
= new ToplevelParameterInfo
[parameters
.Count
];
2669 for (int i
= 0; i
< parameters
.Count
; ++i
)
2670 target
.parameter_info
[i
] = new ToplevelParameterInfo (target
, i
);
2673 public bool CheckError158 (string name
, Location loc
)
2675 if (AnonymousChildren
!= null) {
2676 foreach (ToplevelBlock child
in AnonymousChildren
) {
2677 if (!child
.CheckError158 (name
, loc
))
2682 for (ToplevelBlock c
= Container
; c
!= null; c
= c
.Container
) {
2683 if (!c
.DoCheckError158 (name
, loc
))
2690 void ProcessParameters ()
2692 int n
= parameters
.Count
;
2693 parameter_info
= new ToplevelParameterInfo
[n
];
2694 ToplevelBlock top_parent
= Parent
== null ? null : Parent
.Toplevel
;
2695 for (int i
= 0; i
< n
; ++i
) {
2696 parameter_info
[i
] = new ToplevelParameterInfo (this, i
);
2698 Parameter p
= parameters
[i
];
2702 string name
= p
.Name
;
2703 if (CheckParentConflictName (top_parent
, name
, loc
))
2704 AddKnownVariable (name
, parameter_info
[i
]);
2707 // mark this block as "used" so that we create local declarations in a sub-block
2708 // FIXME: This appears to uncover a lot of bugs
2712 bool DoCheckError158 (string name
, Location loc
)
2714 LabeledStatement s
= LookupLabel (name
);
2716 Report
.SymbolRelatedToPreviousError (s
.loc
, s
.Name
);
2717 Error_158 (name
, loc
);
2724 public override Expression
CreateExpressionTree (ResolveContext ec
)
2726 if (statements
.Count
== 1) {
2727 Expression expr
= ((Statement
) statements
[0]).CreateExpressionTree (ec
);
2728 if (scope_initializers
!= null)
2729 expr
= new BlockScopeExpression (expr
, this);
2734 return base.CreateExpressionTree (ec
);
2738 // Reformats this block to be top-level iterator block
2740 public IteratorStorey
ChangeToIterator (Iterator iterator
, ToplevelBlock source
)
2744 // Creates block with original statements
2745 AddStatement (new IteratorStatement (iterator
, new Block (this, source
)));
2747 source
.statements
= new ArrayList (1);
2748 source
.AddStatement (new Return (iterator
, iterator
.Location
));
2749 source
.IsIterator
= false;
2751 IteratorStorey iterator_storey
= new IteratorStorey (iterator
);
2752 source
.am_storey
= iterator_storey
;
2753 return iterator_storey
;
2757 // Returns a parameter reference expression for the given name,
2758 // or null if there is no such parameter
2760 public Expression
GetParameterReference (string name
, Location loc
)
2762 for (ToplevelBlock t
= this; t
!= null; t
= t
.Container
) {
2763 Expression expr
= t
.GetParameterReferenceExpression (name
, loc
);
2771 protected virtual Expression
GetParameterReferenceExpression (string name
, Location loc
)
2773 int idx
= parameters
.GetParameterIndexByName (name
);
2775 null : new ParameterReference (parameter_info
[idx
], loc
);
2779 // Returns the "this" instance variable of this block.
2780 // See AddThisVariable() for more information.
2782 public LocalInfo ThisVariable
{
2783 get { return this_variable; }
2787 // This is used by non-static `struct' constructors which do not have an
2788 // initializer - in this case, the constructor must initialize all of the
2789 // struct's fields. To do this, we add a "this" variable and use the flow
2790 // analysis code to ensure that it's been fully initialized before control
2791 // leaves the constructor.
2793 public LocalInfo
AddThisVariable (DeclSpace ds
, Location l
)
2795 if (this_variable
== null) {
2796 this_variable
= new LocalInfo (ds
, this, l
);
2797 this_variable
.Used
= true;
2798 this_variable
.IsThis
= true;
2800 Variables
.Add ("this", this_variable
);
2803 return this_variable
;
2806 public bool IsIterator
{
2807 get { return (flags & Flags.IsIterator) != 0; }
2808 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2812 // Block has been converted to expression tree
2814 public bool IsExpressionTree
{
2815 get { return (flags & Flags.IsExpressionTree) != 0; }
2818 public bool IsThisAssigned (BlockContext ec
)
2820 return this_variable
== null || this_variable
.IsThisAssigned (ec
, this);
2823 public bool Resolve (FlowBranching parent
, BlockContext rc
, ParametersCompiled ip
, IMethodData md
)
2830 if (rc
.HasSet (ResolveContext
.Options
.ExpressionTreeConversion
))
2831 flags
|= Flags
.IsExpressionTree
;
2834 if (!ResolveMeta (rc
, ip
))
2837 using (rc
.With (ResolveContext
.Options
.DoFlowAnalysis
, true)) {
2838 FlowBranchingToplevel top_level
= rc
.StartFlowBranching (this, parent
);
2843 unreachable
= top_level
.End ();
2845 } catch (Exception
) {
2847 if (rc
.CurrentBlock
!= null) {
2848 ec
.Report
.Error (584, rc
.CurrentBlock
.StartLocation
, "Internal compiler error: Phase Resolve");
2850 ec
.Report
.Error (587, "Internal compiler error: Phase Resolve");
2856 if (rc
.ReturnType
!= TypeManager
.void_type
&& !unreachable
) {
2857 if (rc
.CurrentAnonymousMethod
== null) {
2858 rc
.Report
.Error (161, md
.Location
, "`{0}': not all code paths return a value", md
.GetSignatureForError ());
2860 } else if (!rc
.CurrentAnonymousMethod
.IsIterator
) {
2861 rc
.Report
.Error (1643, rc
.CurrentAnonymousMethod
.Location
, "Not all code paths return a value in anonymous method of type `{0}'",
2862 rc
.CurrentAnonymousMethod
.GetSignatureForError ());
2870 bool ResolveMeta (BlockContext ec
, ParametersCompiled ip
)
2872 int errors
= ec
.Report
.Errors
;
2873 int orig_count
= parameters
.Count
;
2878 // Assert: orig_count != parameter.Count => orig_count == 0
2879 if (orig_count
!= 0 && orig_count
!= parameters
.Count
)
2880 throw new InternalErrorException ("parameter information mismatch");
2882 int offset
= Parent
== null ? 0 : Parent
.AssignableSlots
;
2884 for (int i
= 0; i
< orig_count
; ++i
) {
2885 Parameter
.Modifier mod
= parameters
.FixedParameters
[i
].ModFlags
;
2887 if ((mod
& Parameter
.Modifier
.OUT
) != Parameter
.Modifier
.OUT
)
2890 VariableInfo vi
= new VariableInfo (ip
, i
, offset
);
2891 parameter_info
[i
].VariableInfo
= vi
;
2892 offset
+= vi
.Length
;
2895 ResolveMeta (ec
, offset
);
2897 return ec
.Report
.Errors
== errors
;
2901 // Check whether all `out' parameters have been assigned.
2903 public void CheckOutParameters (FlowBranching
.UsageVector vector
, Location loc
)
2905 if (vector
.IsUnreachable
)
2908 int n
= parameter_info
== null ? 0 : parameter_info
.Length
;
2910 for (int i
= 0; i
< n
; i
++) {
2911 VariableInfo
var = parameter_info
[i
].VariableInfo
;
2916 if (vector
.IsAssigned (var, false))
2919 Report
.Error (177, loc
, "The out parameter `{0}' must be assigned to before control leaves the current method",
2924 public override void Emit (EmitContext ec
)
2926 if (Report
.Errors
> 0)
2934 if (ec
.HasReturnLabel
)
2935 ec
.ReturnLabel
= ec
.ig
.DefineLabel ();
2939 ec
.Mark (EndLocation
);
2941 if (ec
.HasReturnLabel
)
2942 ec
.ig
.MarkLabel (ec
.ReturnLabel
);
2944 if (ec
.return_value
!= null) {
2945 ec
.ig
.Emit (OpCodes
.Ldloc
, ec
.return_value
);
2946 ec
.ig
.Emit (OpCodes
.Ret
);
2949 // If `HasReturnLabel' is set, then we already emitted a
2950 // jump to the end of the method, so we must emit a `ret'
2953 // Unfortunately, System.Reflection.Emit automatically emits
2954 // a leave to the end of a finally block. This is a problem
2955 // if no code is following the try/finally block since we may
2956 // jump to a point after the end of the method.
2957 // As a workaround, we're always creating a return label in
2961 if (ec
.HasReturnLabel
|| !unreachable
) {
2962 if (ec
.ReturnType
!= TypeManager
.void_type
)
2963 ec
.ig
.Emit (OpCodes
.Ldloc
, ec
.TemporaryReturn ());
2964 ec
.ig
.Emit (OpCodes
.Ret
);
2969 } catch (Exception e
){
2970 Console
.WriteLine ("Exception caught by the compiler while emitting:");
2971 Console
.WriteLine (" Block that caused the problem begin at: " + block
.loc
);
2973 Console
.WriteLine (e
.GetType ().FullName
+ ": " + e
.Message
);
2979 public override void EmitMeta (EmitContext ec
)
2981 parameters
.ResolveVariable ();
2983 // Avoid declaring an IL variable for this_variable since it is not accessed
2984 // from the generated IL
2985 if (this_variable
!= null)
2986 Variables
.Remove ("this");
2990 protected override void EmitSymbolInfo (EmitContext ec
)
2992 AnonymousExpression ae
= ec
.CurrentAnonymousMethod
;
2993 if ((ae
!= null) && (ae
.Storey
!= null))
2994 SymbolWriter
.DefineScopeVariable (ae
.Storey
.ID
);
2996 base.EmitSymbolInfo (ec
);
3000 public class SwitchLabel
{
3007 Label il_label_code
;
3008 bool il_label_code_set
;
3010 public static readonly object NullStringCase
= new object ();
3013 // if expr == null, then it is the default case.
3015 public SwitchLabel (Expression expr
, Location l
)
3021 public Expression Label
{
3027 public Location Location
{
3031 public object Converted
{
3037 public Label
GetILLabel (EmitContext ec
)
3040 il_label
= ec
.ig
.DefineLabel ();
3041 il_label_set
= true;
3046 public Label
GetILLabelCode (EmitContext ec
)
3048 if (!il_label_code_set
){
3049 il_label_code
= ec
.ig
.DefineLabel ();
3050 il_label_code_set
= true;
3052 return il_label_code
;
3056 // Resolves the expression, reduces it to a literal if possible
3057 // and then converts it to the requested type.
3059 public bool ResolveAndReduce (ResolveContext ec
, Type required_type
, bool allow_nullable
)
3061 Expression e
= label
.Resolve (ec
);
3066 Constant c
= e
as Constant
;
3068 ec
.Report
.Error (150, loc
, "A constant value is expected");
3072 if (required_type
== TypeManager
.string_type
&& c
.GetValue () == null) {
3073 converted
= NullStringCase
;
3077 if (allow_nullable
&& c
.GetValue () == null) {
3078 converted
= NullStringCase
;
3082 c
= c
.ImplicitConversionRequired (ec
, required_type
, loc
);
3086 converted
= c
.GetValue ();
3090 public void Error_AlreadyOccurs (ResolveContext ec
, Type switch_type
, SwitchLabel collision_with
)
3093 if (converted
== null)
3095 else if (converted
== NullStringCase
)
3098 label
= converted
.ToString ();
3100 ec
.Report
.SymbolRelatedToPreviousError (collision_with
.loc
, null);
3101 ec
.Report
.Error (152, loc
, "The label `case {0}:' already occurs in this switch statement", label
);
3104 public SwitchLabel
Clone (CloneContext clonectx
)
3106 return new SwitchLabel (label
.Clone (clonectx
), loc
);
3110 public class SwitchSection
{
3111 // An array of SwitchLabels.
3112 public readonly ArrayList Labels
;
3113 public readonly Block Block
;
3115 public SwitchSection (ArrayList labels
, Block block
)
3121 public SwitchSection
Clone (CloneContext clonectx
)
3123 ArrayList cloned_labels
= new ArrayList ();
3125 foreach (SwitchLabel sl
in cloned_labels
)
3126 cloned_labels
.Add (sl
.Clone (clonectx
));
3128 return new SwitchSection (cloned_labels
, clonectx
.LookupBlock (Block
));
3132 public class Switch
: Statement
{
3133 public ArrayList Sections
;
3134 public Expression Expr
;
3137 /// Maps constants whose type type SwitchType to their SwitchLabels.
3139 public IDictionary Elements
;
3142 /// The governing switch type
3144 public Type SwitchType
;
3149 Label default_target
;
3151 Expression new_expr
;
3154 SwitchSection constant_section
;
3155 SwitchSection default_section
;
3157 ExpressionStatement string_dictionary
;
3158 FieldExpr switch_cache_field
;
3159 static int unique_counter
;
3162 // Nullable Types support
3164 Nullable
.Unwrap unwrap
;
3166 protected bool HaveUnwrap
{
3167 get { return unwrap != null; }
3171 // The types allowed to be implicitly cast from
3172 // on the governing type
3174 static Type
[] allowed_types
;
3176 public Switch (Expression e
, ArrayList sects
, Location l
)
3183 public bool GotDefault
{
3185 return default_section
!= null;
3189 public Label DefaultTarget
{
3191 return default_target
;
3196 // Determines the governing type for a switch. The returned
3197 // expression might be the expression from the switch, or an
3198 // expression that includes any potential conversions to the
3199 // integral types or to string.
3201 Expression
SwitchGoverningType (ResolveContext ec
, Expression expr
)
3205 if (t
== TypeManager
.byte_type
||
3206 t
== TypeManager
.sbyte_type
||
3207 t
== TypeManager
.ushort_type
||
3208 t
== TypeManager
.short_type
||
3209 t
== TypeManager
.uint32_type
||
3210 t
== TypeManager
.int32_type
||
3211 t
== TypeManager
.uint64_type
||
3212 t
== TypeManager
.int64_type
||
3213 t
== TypeManager
.char_type
||
3214 t
== TypeManager
.string_type
||
3215 t
== TypeManager
.bool_type
||
3216 TypeManager
.IsEnumType (t
))
3219 if (allowed_types
== null){
3220 allowed_types
= new Type
[] {
3221 TypeManager
.sbyte_type
,
3222 TypeManager
.byte_type
,
3223 TypeManager
.short_type
,
3224 TypeManager
.ushort_type
,
3225 TypeManager
.int32_type
,
3226 TypeManager
.uint32_type
,
3227 TypeManager
.int64_type
,
3228 TypeManager
.uint64_type
,
3229 TypeManager
.char_type
,
3230 TypeManager
.string_type
3235 // Try to find a *user* defined implicit conversion.
3237 // If there is no implicit conversion, or if there are multiple
3238 // conversions, we have to report an error
3240 Expression converted
= null;
3241 foreach (Type tt
in allowed_types
){
3244 e
= Convert
.ImplicitUserConversion (ec
, expr
, tt
, loc
);
3249 // Ignore over-worked ImplicitUserConversions that do
3250 // an implicit conversion in addition to the user conversion.
3252 if (!(e
is UserCast
))
3255 if (converted
!= null){
3256 ec
.Report
.ExtraInformation (loc
, "(Ambiguous implicit user defined conversion in previous ");
3266 // Performs the basic sanity checks on the switch statement
3267 // (looks for duplicate keys and non-constant expressions).
3269 // It also returns a hashtable with the keys that we will later
3270 // use to compute the switch tables
3272 bool CheckSwitch (ResolveContext ec
)
3275 Elements
= Sections
.Count
> 10 ?
3276 (IDictionary
)new Hashtable () :
3277 (IDictionary
)new ListDictionary ();
3279 foreach (SwitchSection ss
in Sections
){
3280 foreach (SwitchLabel sl
in ss
.Labels
){
3281 if (sl
.Label
== null){
3282 if (default_section
!= null){
3283 sl
.Error_AlreadyOccurs (ec
, SwitchType
, (SwitchLabel
)default_section
.Labels
[0]);
3286 default_section
= ss
;
3290 if (!sl
.ResolveAndReduce (ec
, SwitchType
, HaveUnwrap
)) {
3295 object key
= sl
.Converted
;
3296 if (key
== SwitchLabel
.NullStringCase
)
3297 has_null_case
= true;
3300 Elements
.Add (key
, sl
);
3301 } catch (ArgumentException
) {
3302 sl
.Error_AlreadyOccurs (ec
, SwitchType
, (SwitchLabel
)Elements
[key
]);
3310 void EmitObjectInteger (ILGenerator ig
, object k
)
3313 IntConstant
.EmitInt (ig
, (int) k
);
3314 else if (k
is Constant
) {
3315 EmitObjectInteger (ig
, ((Constant
) k
).GetValue ());
3318 IntConstant
.EmitInt (ig
, unchecked ((int) (uint) k
));
3321 if ((long) k
>= int.MinValue
&& (long) k
<= int.MaxValue
)
3323 IntConstant
.EmitInt (ig
, (int) (long) k
);
3324 ig
.Emit (OpCodes
.Conv_I8
);
3327 LongConstant
.EmitLong (ig
, (long) k
);
3329 else if (k
is ulong)
3331 ulong ul
= (ulong) k
;
3334 IntConstant
.EmitInt (ig
, unchecked ((int) ul
));
3335 ig
.Emit (OpCodes
.Conv_U8
);
3339 LongConstant
.EmitLong (ig
, unchecked ((long) ul
));
3343 IntConstant
.EmitInt (ig
, (int) ((char) k
));
3344 else if (k
is sbyte)
3345 IntConstant
.EmitInt (ig
, (int) ((sbyte) k
));
3347 IntConstant
.EmitInt (ig
, (int) ((byte) k
));
3348 else if (k
is short)
3349 IntConstant
.EmitInt (ig
, (int) ((short) k
));
3350 else if (k
is ushort)
3351 IntConstant
.EmitInt (ig
, (int) ((ushort) k
));
3353 IntConstant
.EmitInt (ig
, ((bool) k
) ? 1 : 0);
3355 throw new Exception ("Unhandled case");
3358 // structure used to hold blocks of keys while calculating table switch
3359 class KeyBlock
: IComparable
3361 public KeyBlock (long _first
)
3363 first
= last
= _first
;
3367 public ArrayList element_keys
= null;
3368 // how many items are in the bucket
3369 public int Size
= 1;
3372 get { return (int) (last - first + 1); }
3374 public static long TotalLength (KeyBlock kb_first
, KeyBlock kb_last
)
3376 return kb_last
.last
- kb_first
.first
+ 1;
3378 public int CompareTo (object obj
)
3380 KeyBlock kb
= (KeyBlock
) obj
;
3381 int nLength
= Length
;
3382 int nLengthOther
= kb
.Length
;
3383 if (nLengthOther
== nLength
)
3384 return (int) (kb
.first
- first
);
3385 return nLength
- nLengthOther
;
3390 /// This method emits code for a lookup-based switch statement (non-string)
3391 /// Basically it groups the cases into blocks that are at least half full,
3392 /// and then spits out individual lookup opcodes for each block.
3393 /// It emits the longest blocks first, and short blocks are just
3394 /// handled with direct compares.
3396 /// <param name="ec"></param>
3397 /// <param name="val"></param>
3398 /// <returns></returns>
3399 void TableSwitchEmit (EmitContext ec
, Expression val
)
3401 int element_count
= Elements
.Count
;
3402 object [] element_keys
= new object [element_count
];
3403 Elements
.Keys
.CopyTo (element_keys
, 0);
3404 Array
.Sort (element_keys
);
3406 // initialize the block list with one element per key
3407 ArrayList key_blocks
= new ArrayList (element_count
);
3408 foreach (object key
in element_keys
)
3409 key_blocks
.Add (new KeyBlock (System
.Convert
.ToInt64 (key
)));
3411 KeyBlock current_kb
;
3412 // iteratively merge the blocks while they are at least half full
3413 // there's probably a really cool way to do this with a tree...
3414 while (key_blocks
.Count
> 1)
3416 ArrayList key_blocks_new
= new ArrayList ();
3417 current_kb
= (KeyBlock
) key_blocks
[0];
3418 for (int ikb
= 1; ikb
< key_blocks
.Count
; ikb
++)
3420 KeyBlock kb
= (KeyBlock
) key_blocks
[ikb
];
3421 if ((current_kb
.Size
+ kb
.Size
) * 2 >= KeyBlock
.TotalLength (current_kb
, kb
))
3424 current_kb
.last
= kb
.last
;
3425 current_kb
.Size
+= kb
.Size
;
3429 // start a new block
3430 key_blocks_new
.Add (current_kb
);
3434 key_blocks_new
.Add (current_kb
);
3435 if (key_blocks
.Count
== key_blocks_new
.Count
)
3437 key_blocks
= key_blocks_new
;
3440 // initialize the key lists
3441 foreach (KeyBlock kb
in key_blocks
)
3442 kb
.element_keys
= new ArrayList ();
3444 // fill the key lists
3446 if (key_blocks
.Count
> 0) {
3447 current_kb
= (KeyBlock
) key_blocks
[0];
3448 foreach (object key
in element_keys
)
3450 bool next_block
= (key
is UInt64
) ? (ulong) key
> (ulong) current_kb
.last
:
3451 System
.Convert
.ToInt64 (key
) > current_kb
.last
;
3453 current_kb
= (KeyBlock
) key_blocks
[++iBlockCurr
];
3454 current_kb
.element_keys
.Add (key
);
3458 // sort the blocks so we can tackle the largest ones first
3461 // okay now we can start...
3462 ILGenerator ig
= ec
.ig
;
3463 Label lbl_end
= ig
.DefineLabel (); // at the end ;-)
3464 Label lbl_default
= default_target
;
3466 Type type_keys
= null;
3467 if (element_keys
.Length
> 0)
3468 type_keys
= element_keys
[0].GetType (); // used for conversions
3472 if (TypeManager
.IsEnumType (SwitchType
))
3473 compare_type
= TypeManager
.GetEnumUnderlyingType (SwitchType
);
3475 compare_type
= SwitchType
;
3477 for (int iBlock
= key_blocks
.Count
- 1; iBlock
>= 0; --iBlock
)
3479 KeyBlock kb
= ((KeyBlock
) key_blocks
[iBlock
]);
3480 lbl_default
= (iBlock
== 0) ? default_target
: ig
.DefineLabel ();
3483 foreach (object key
in kb
.element_keys
) {
3484 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
3485 if (key
is int && (int) key
== 0) {
3486 val
.EmitBranchable (ec
, sl
.GetILLabel (ec
), false);
3489 EmitObjectInteger (ig
, key
);
3490 ig
.Emit (OpCodes
.Beq
, sl
.GetILLabel (ec
));
3496 // TODO: if all the keys in the block are the same and there are
3497 // no gaps/defaults then just use a range-check.
3498 if (compare_type
== TypeManager
.int64_type
||
3499 compare_type
== TypeManager
.uint64_type
)
3501 // TODO: optimize constant/I4 cases
3503 // check block range (could be > 2^31)
3505 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.first
, type_keys
));
3506 ig
.Emit (OpCodes
.Blt
, lbl_default
);
3508 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.last
, type_keys
));
3509 ig
.Emit (OpCodes
.Bgt
, lbl_default
);
3515 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.first
, type_keys
));
3516 ig
.Emit (OpCodes
.Sub
);
3518 ig
.Emit (OpCodes
.Conv_I4
); // assumes < 2^31 labels!
3524 int first
= (int) kb
.first
;
3527 IntConstant
.EmitInt (ig
, first
);
3528 ig
.Emit (OpCodes
.Sub
);
3532 IntConstant
.EmitInt (ig
, -first
);
3533 ig
.Emit (OpCodes
.Add
);
3537 // first, build the list of labels for the switch
3539 int cJumps
= kb
.Length
;
3540 Label
[] switch_labels
= new Label
[cJumps
];
3541 for (int iJump
= 0; iJump
< cJumps
; iJump
++)
3543 object key
= kb
.element_keys
[iKey
];
3544 if (System
.Convert
.ToInt64 (key
) == kb
.first
+ iJump
)
3546 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
3547 switch_labels
[iJump
] = sl
.GetILLabel (ec
);
3551 switch_labels
[iJump
] = lbl_default
;
3553 // emit the switch opcode
3554 ig
.Emit (OpCodes
.Switch
, switch_labels
);
3557 // mark the default for this block
3559 ig
.MarkLabel (lbl_default
);
3562 // TODO: find the default case and emit it here,
3563 // to prevent having to do the following jump.
3564 // make sure to mark other labels in the default section
3566 // the last default just goes to the end
3567 if (element_keys
.Length
> 0)
3568 ig
.Emit (OpCodes
.Br
, lbl_default
);
3570 // now emit the code for the sections
3571 bool found_default
= false;
3573 foreach (SwitchSection ss
in Sections
) {
3574 foreach (SwitchLabel sl
in ss
.Labels
) {
3575 if (sl
.Converted
== SwitchLabel
.NullStringCase
) {
3576 ig
.MarkLabel (null_target
);
3577 } else if (sl
.Label
== null) {
3578 ig
.MarkLabel (lbl_default
);
3579 found_default
= true;
3581 ig
.MarkLabel (null_target
);
3583 ig
.MarkLabel (sl
.GetILLabel (ec
));
3584 ig
.MarkLabel (sl
.GetILLabelCode (ec
));
3589 if (!found_default
) {
3590 ig
.MarkLabel (lbl_default
);
3591 if (!has_null_case
) {
3592 ig
.MarkLabel (null_target
);
3596 ig
.MarkLabel (lbl_end
);
3599 SwitchSection
FindSection (SwitchLabel label
)
3601 foreach (SwitchSection ss
in Sections
){
3602 foreach (SwitchLabel sl
in ss
.Labels
){
3611 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
3613 foreach (SwitchSection ss
in Sections
)
3614 ss
.Block
.MutateHoistedGenericType (storey
);
3617 public static void Reset ()
3620 allowed_types
= null;
3623 public override bool Resolve (BlockContext ec
)
3625 Expr
= Expr
.Resolve (ec
);
3629 new_expr
= SwitchGoverningType (ec
, Expr
);
3631 if ((new_expr
== null) && TypeManager
.IsNullableType (Expr
.Type
)) {
3632 unwrap
= Nullable
.Unwrap
.Create (Expr
, false);
3636 new_expr
= SwitchGoverningType (ec
, unwrap
);
3639 if (new_expr
== null){
3640 ec
.Report
.Error (151, loc
,
3641 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3642 TypeManager
.CSharpName (Expr
.Type
));
3647 SwitchType
= new_expr
.Type
;
3649 if (RootContext
.Version
== LanguageVersion
.ISO_1
&& SwitchType
== TypeManager
.bool_type
) {
3650 ec
.Report
.FeatureIsNotAvailable (loc
, "switch expression of boolean type");
3654 if (!CheckSwitch (ec
))
3658 Elements
.Remove (SwitchLabel
.NullStringCase
);
3660 Switch old_switch
= ec
.Switch
;
3662 ec
.Switch
.SwitchType
= SwitchType
;
3664 Report
.Debug (1, "START OF SWITCH BLOCK", loc
, ec
.CurrentBranching
);
3665 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Switch
, loc
);
3667 is_constant
= new_expr
is Constant
;
3669 object key
= ((Constant
) new_expr
).GetValue ();
3670 SwitchLabel label
= (SwitchLabel
) Elements
[key
];
3672 constant_section
= FindSection (label
);
3673 if (constant_section
== null)
3674 constant_section
= default_section
;
3679 foreach (SwitchSection ss
in Sections
){
3681 ec
.CurrentBranching
.CreateSibling (
3682 null, FlowBranching
.SiblingType
.SwitchSection
);
3686 if (is_constant
&& (ss
!= constant_section
)) {
3687 // If we're a constant switch, we're only emitting
3688 // one single section - mark all the others as
3690 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
3691 if (!ss
.Block
.ResolveUnreachable (ec
, true)) {
3695 if (!ss
.Block
.Resolve (ec
))
3700 if (default_section
== null)
3701 ec
.CurrentBranching
.CreateSibling (
3702 null, FlowBranching
.SiblingType
.SwitchSection
);
3704 ec
.EndFlowBranching ();
3705 ec
.Switch
= old_switch
;
3707 Report
.Debug (1, "END OF SWITCH BLOCK", loc
, ec
.CurrentBranching
);
3712 if (SwitchType
== TypeManager
.string_type
&& !is_constant
) {
3713 // TODO: Optimize single case, and single+default case
3714 ResolveStringSwitchMap (ec
);
3720 void ResolveStringSwitchMap (ResolveContext ec
)
3722 FullNamedExpression string_dictionary_type
;
3723 if (TypeManager
.generic_ienumerable_type
!= null) {
3724 MemberAccess system_collections_generic
= new MemberAccess (new MemberAccess (
3725 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "System", loc
), "Collections", loc
), "Generic", loc
);
3727 string_dictionary_type
= new MemberAccess (system_collections_generic
, "Dictionary",
3729 new TypeExpression (TypeManager
.string_type
, loc
),
3730 new TypeExpression (TypeManager
.int32_type
, loc
)), loc
);
3732 MemberAccess system_collections_generic
= new MemberAccess (
3733 new QualifiedAliasMember (QualifiedAliasMember
.GlobalAlias
, "System", loc
), "Collections", loc
);
3735 string_dictionary_type
= new MemberAccess (system_collections_generic
, "Hashtable", loc
);
3738 Field field
= new Field (ec
.CurrentTypeDefinition
, string_dictionary_type
,
3739 Modifiers
.STATIC
| Modifiers
.PRIVATE
| Modifiers
.COMPILER_GENERATED
,
3740 new MemberName (CompilerGeneratedClass
.MakeName (null, "f", "switch$map", unique_counter
++), loc
), null);
3741 if (!field
.Define ())
3743 ec
.CurrentTypeDefinition
.PartialContainer
.AddField (field
);
3745 ArrayList init
= new ArrayList ();
3748 string value = null;
3749 foreach (SwitchSection section
in Sections
) {
3750 int last_count
= init
.Count
;
3751 foreach (SwitchLabel sl
in section
.Labels
) {
3752 if (sl
.Label
== null || sl
.Converted
== SwitchLabel
.NullStringCase
)
3755 value = (string) sl
.Converted
;
3756 ArrayList init_args
= new ArrayList (2);
3757 init_args
.Add (new StringLiteral (value, sl
.Location
));
3758 init_args
.Add (new IntConstant (counter
, loc
));
3759 init
.Add (new CollectionElementInitializer (init_args
, loc
));
3763 // Don't add empty sections
3765 if (last_count
== init
.Count
)
3768 Elements
.Add (counter
, section
.Labels
[0]);
3772 Arguments args
= new Arguments (1);
3773 args
.Add (new Argument (new IntConstant (init
.Count
, loc
)));
3774 Expression initializer
= new NewInitialize (string_dictionary_type
, args
,
3775 new CollectionOrObjectInitializers (init
, loc
), loc
);
3777 switch_cache_field
= new FieldExpr (field
.FieldBuilder
, loc
);
3778 string_dictionary
= new SimpleAssign (switch_cache_field
, initializer
.Resolve (ec
));
3781 void DoEmitStringSwitch (LocalTemporary
value, EmitContext ec
)
3783 ILGenerator ig
= ec
.ig
;
3784 Label l_initialized
= ig
.DefineLabel ();
3787 // Skip initialization when value is null
3789 value.EmitBranchable (ec
, null_target
, false);
3792 // Check if string dictionary is initialized and initialize
3794 switch_cache_field
.EmitBranchable (ec
, l_initialized
, true);
3795 string_dictionary
.EmitStatement (ec
);
3796 ig
.MarkLabel (l_initialized
);
3798 LocalTemporary string_switch_variable
= new LocalTemporary (TypeManager
.int32_type
);
3800 ResolveContext rc
= new ResolveContext (ec
.MemberContext
);
3802 if (TypeManager
.generic_ienumerable_type
!= null) {
3803 Arguments get_value_args
= new Arguments (2);
3804 get_value_args
.Add (new Argument (value));
3805 get_value_args
.Add (new Argument (string_switch_variable
, Argument
.AType
.Out
));
3806 Expression get_item
= new Invocation (new MemberAccess (switch_cache_field
, "TryGetValue", loc
), get_value_args
).Resolve (rc
);
3807 if (get_item
== null)
3811 // A value was not found, go to default case
3813 get_item
.EmitBranchable (ec
, default_target
, false);
3815 Arguments get_value_args
= new Arguments (1);
3816 get_value_args
.Add (new Argument (value));
3818 Expression get_item
= new IndexerAccess (new ElementAccess (switch_cache_field
, get_value_args
), loc
).Resolve (rc
);
3819 if (get_item
== null)
3822 LocalTemporary get_item_object
= new LocalTemporary (TypeManager
.object_type
);
3823 get_item_object
.EmitAssign (ec
, get_item
, true, false);
3824 ec
.ig
.Emit (OpCodes
.Brfalse
, default_target
);
3826 ExpressionStatement get_item_int
= (ExpressionStatement
) new SimpleAssign (string_switch_variable
,
3827 new Cast (new TypeExpression (TypeManager
.int32_type
, loc
), get_item_object
, loc
)).Resolve (rc
);
3829 get_item_int
.EmitStatement (ec
);
3830 get_item_object
.Release (ec
);
3833 TableSwitchEmit (ec
, string_switch_variable
);
3834 string_switch_variable
.Release (ec
);
3837 protected override void DoEmit (EmitContext ec
)
3839 ILGenerator ig
= ec
.ig
;
3841 default_target
= ig
.DefineLabel ();
3842 null_target
= ig
.DefineLabel ();
3844 // Store variable for comparission purposes
3845 // TODO: Don't duplicate non-captured VariableReference
3846 LocalTemporary
value;
3848 value = new LocalTemporary (SwitchType
);
3849 unwrap
.EmitCheck (ec
);
3850 ig
.Emit (OpCodes
.Brfalse
, null_target
);
3853 } else if (!is_constant
) {
3854 value = new LocalTemporary (SwitchType
);
3861 // Setup the codegen context
3863 Label old_end
= ec
.LoopEnd
;
3864 Switch old_switch
= ec
.Switch
;
3866 ec
.LoopEnd
= ig
.DefineLabel ();
3871 if (constant_section
!= null)
3872 constant_section
.Block
.Emit (ec
);
3873 } else if (string_dictionary
!= null) {
3874 DoEmitStringSwitch (value, ec
);
3876 TableSwitchEmit (ec
, value);
3882 // Restore context state.
3883 ig
.MarkLabel (ec
.LoopEnd
);
3886 // Restore the previous context
3888 ec
.LoopEnd
= old_end
;
3889 ec
.Switch
= old_switch
;
3892 protected override void CloneTo (CloneContext clonectx
, Statement t
)
3894 Switch target
= (Switch
) t
;
3896 target
.Expr
= Expr
.Clone (clonectx
);
3897 target
.Sections
= new ArrayList ();
3898 foreach (SwitchSection ss
in Sections
){
3899 target
.Sections
.Add (ss
.Clone (clonectx
));
3904 // A place where execution can restart in an iterator
3905 public abstract class ResumableStatement
: Statement
3908 protected Label resume_point
;
3910 public Label
PrepareForEmit (EmitContext ec
)
3914 resume_point
= ec
.ig
.DefineLabel ();
3916 return resume_point
;
3919 public virtual Label
PrepareForDispose (EmitContext ec
, Label end
)
3923 public virtual void EmitForDispose (EmitContext ec
, Iterator iterator
, Label end
, bool have_dispatcher
)
3928 // Base class for statements that are implemented in terms of try...finally
3929 public abstract class ExceptionStatement
: ResumableStatement
3934 protected abstract void EmitPreTryBody (EmitContext ec
);
3935 protected abstract void EmitTryBody (EmitContext ec
);
3936 protected abstract void EmitFinallyBody (EmitContext ec
);
3938 protected sealed override void DoEmit (EmitContext ec
)
3940 ILGenerator ig
= ec
.ig
;
3942 EmitPreTryBody (ec
);
3944 if (resume_points
!= null) {
3945 IntConstant
.EmitInt (ig
, (int) Iterator
.State
.Running
);
3946 ig
.Emit (OpCodes
.Stloc
, iter
.CurrentPC
);
3949 ig
.BeginExceptionBlock ();
3951 if (resume_points
!= null) {
3952 ig
.MarkLabel (resume_point
);
3954 // For normal control flow, we want to fall-through the Switch
3955 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3956 ig
.Emit (OpCodes
.Ldloc
, iter
.CurrentPC
);
3957 IntConstant
.EmitInt (ig
, first_resume_pc
);
3958 ig
.Emit (OpCodes
.Sub
);
3960 Label
[] labels
= new Label
[resume_points
.Count
];
3961 for (int i
= 0; i
< resume_points
.Count
; ++i
)
3962 labels
[i
] = ((ResumableStatement
) resume_points
[i
]).PrepareForEmit (ec
);
3963 ig
.Emit (OpCodes
.Switch
, labels
);
3968 ig
.BeginFinallyBlock ();
3970 Label start_finally
= ec
.ig
.DefineLabel ();
3971 if (resume_points
!= null) {
3972 ig
.Emit (OpCodes
.Ldloc
, iter
.SkipFinally
);
3973 ig
.Emit (OpCodes
.Brfalse_S
, start_finally
);
3974 ig
.Emit (OpCodes
.Endfinally
);
3977 ig
.MarkLabel (start_finally
);
3978 EmitFinallyBody (ec
);
3980 ig
.EndExceptionBlock ();
3983 public void SomeCodeFollows ()
3985 code_follows
= true;
3988 public override bool Resolve (BlockContext ec
)
3990 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3991 // So, ensure there's some IL code after this statement.
3992 if (!code_follows
&& resume_points
== null && ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
)
3993 ec
.NeedReturnLabel ();
3995 iter
= ec
.CurrentIterator
;
3999 ArrayList resume_points
;
4000 int first_resume_pc
;
4001 public void AddResumePoint (ResumableStatement stmt
, int pc
)
4003 if (resume_points
== null) {
4004 resume_points
= new ArrayList ();
4005 first_resume_pc
= pc
;
4008 if (pc
!= first_resume_pc
+ resume_points
.Count
)
4009 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4011 resume_points
.Add (stmt
);
4014 Label dispose_try_block
;
4015 bool prepared_for_dispose
, emitted_dispose
;
4016 public override Label
PrepareForDispose (EmitContext ec
, Label end
)
4018 if (!prepared_for_dispose
) {
4019 prepared_for_dispose
= true;
4020 dispose_try_block
= ec
.ig
.DefineLabel ();
4022 return dispose_try_block
;
4025 public override void EmitForDispose (EmitContext ec
, Iterator iterator
, Label end
, bool have_dispatcher
)
4027 if (emitted_dispose
)
4030 emitted_dispose
= true;
4032 ILGenerator ig
= ec
.ig
;
4034 Label end_of_try
= ig
.DefineLabel ();
4036 // Ensure that the only way we can get into this code is through a dispatcher
4037 if (have_dispatcher
)
4038 ig
.Emit (OpCodes
.Br
, end
);
4040 ig
.BeginExceptionBlock ();
4042 ig
.MarkLabel (dispose_try_block
);
4044 Label
[] labels
= null;
4045 for (int i
= 0; i
< resume_points
.Count
; ++i
) {
4046 ResumableStatement s
= (ResumableStatement
) resume_points
[i
];
4047 Label ret
= s
.PrepareForDispose (ec
, end_of_try
);
4048 if (ret
.Equals (end_of_try
) && labels
== null)
4050 if (labels
== null) {
4051 labels
= new Label
[resume_points
.Count
];
4052 for (int j
= 0; j
< i
; ++j
)
4053 labels
[j
] = end_of_try
;
4058 if (labels
!= null) {
4060 for (j
= 1; j
< labels
.Length
; ++j
)
4061 if (!labels
[0].Equals (labels
[j
]))
4063 bool emit_dispatcher
= j
< labels
.Length
;
4065 if (emit_dispatcher
) {
4066 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4067 ig
.Emit (OpCodes
.Ldloc
, iterator
.CurrentPC
);
4068 IntConstant
.EmitInt (ig
, first_resume_pc
);
4069 ig
.Emit (OpCodes
.Sub
);
4070 ig
.Emit (OpCodes
.Switch
, labels
);
4071 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4074 foreach (ResumableStatement s
in resume_points
)
4075 s
.EmitForDispose (ec
, iterator
, end_of_try
, emit_dispatcher
);
4078 ig
.MarkLabel (end_of_try
);
4080 ig
.BeginFinallyBlock ();
4082 EmitFinallyBody (ec
);
4084 ig
.EndExceptionBlock ();
4088 public class Lock
: ExceptionStatement
{
4090 public Statement Statement
;
4091 TemporaryVariable temp
;
4093 public Lock (Expression expr
, Statement stmt
, Location l
)
4100 public override bool Resolve (BlockContext ec
)
4102 expr
= expr
.Resolve (ec
);
4106 if (!TypeManager
.IsReferenceType (expr
.Type
)){
4107 ec
.Report
.Error (185, loc
,
4108 "`{0}' is not a reference type as required by the lock statement",
4109 TypeManager
.CSharpName (expr
.Type
));
4113 ec
.StartFlowBranching (this);
4114 bool ok
= Statement
.Resolve (ec
);
4115 ec
.EndFlowBranching ();
4117 ok
&= base.Resolve (ec
);
4119 // Avoid creating libraries that reference the internal
4122 if (t
== TypeManager
.null_type
)
4123 t
= TypeManager
.object_type
;
4125 temp
= new TemporaryVariable (t
, loc
);
4128 if (TypeManager
.void_monitor_enter_object
== null || TypeManager
.void_monitor_exit_object
== null) {
4129 Type monitor_type
= TypeManager
.CoreLookupType (ec
.Compiler
, "System.Threading", "Monitor", Kind
.Class
, true);
4130 TypeManager
.void_monitor_enter_object
= TypeManager
.GetPredefinedMethod (
4131 monitor_type
, "Enter", loc
, TypeManager
.object_type
);
4132 TypeManager
.void_monitor_exit_object
= TypeManager
.GetPredefinedMethod (
4133 monitor_type
, "Exit", loc
, TypeManager
.object_type
);
4139 protected override void EmitPreTryBody (EmitContext ec
)
4141 ILGenerator ig
= ec
.ig
;
4143 temp
.EmitAssign (ec
, expr
);
4145 ig
.Emit (OpCodes
.Call
, TypeManager
.void_monitor_enter_object
);
4148 protected override void EmitTryBody (EmitContext ec
)
4150 Statement
.Emit (ec
);
4153 protected override void EmitFinallyBody (EmitContext ec
)
4156 ec
.ig
.Emit (OpCodes
.Call
, TypeManager
.void_monitor_exit_object
);
4159 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4161 expr
.MutateHoistedGenericType (storey
);
4162 temp
.MutateHoistedGenericType (storey
);
4163 Statement
.MutateHoistedGenericType (storey
);
4166 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4168 Lock target
= (Lock
) t
;
4170 target
.expr
= expr
.Clone (clonectx
);
4171 target
.Statement
= Statement
.Clone (clonectx
);
4175 public class Unchecked
: Statement
{
4178 public Unchecked (Block b
)
4184 public override bool Resolve (BlockContext ec
)
4186 using (ec
.With (ResolveContext
.Options
.AllCheckStateFlags
, false))
4187 return Block
.Resolve (ec
);
4190 protected override void DoEmit (EmitContext ec
)
4192 using (ec
.With (EmitContext
.Options
.AllCheckStateFlags
, false))
4196 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4198 Block
.MutateHoistedGenericType (storey
);
4201 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4203 Unchecked target
= (Unchecked
) t
;
4205 target
.Block
= clonectx
.LookupBlock (Block
);
4209 public class Checked
: Statement
{
4212 public Checked (Block b
)
4215 b
.Unchecked
= false;
4218 public override bool Resolve (BlockContext ec
)
4220 using (ec
.With (ResolveContext
.Options
.AllCheckStateFlags
, true))
4221 return Block
.Resolve (ec
);
4224 protected override void DoEmit (EmitContext ec
)
4226 using (ec
.With (EmitContext
.Options
.AllCheckStateFlags
, true))
4230 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4232 Block
.MutateHoistedGenericType (storey
);
4235 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4237 Checked target
= (Checked
) t
;
4239 target
.Block
= clonectx
.LookupBlock (Block
);
4243 public class Unsafe
: Statement
{
4246 public Unsafe (Block b
)
4249 Block
.Unsafe
= true;
4250 loc
= b
.StartLocation
;
4253 public override bool Resolve (BlockContext ec
)
4255 if (ec
.CurrentIterator
!= null)
4256 ec
.Report
.Error (1629, loc
, "Unsafe code may not appear in iterators");
4258 using (ec
.Set (ResolveContext
.Options
.UnsafeScope
))
4259 return Block
.Resolve (ec
);
4262 protected override void DoEmit (EmitContext ec
)
4267 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4269 Block
.MutateHoistedGenericType (storey
);
4272 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4274 Unsafe target
= (Unsafe
) t
;
4276 target
.Block
= clonectx
.LookupBlock (Block
);
4283 public class Fixed
: Statement
{
4285 ArrayList declarators
;
4286 Statement statement
;
4291 abstract class Emitter
4293 protected LocalInfo vi
;
4294 protected Expression converted
;
4296 protected Emitter (Expression expr
, LocalInfo li
)
4302 public abstract void Emit (EmitContext ec
);
4303 public abstract void EmitExit (EmitContext ec
);
4306 class ExpressionEmitter
: Emitter
{
4307 public ExpressionEmitter (Expression converted
, LocalInfo li
) :
4308 base (converted
, li
)
4312 public override void Emit (EmitContext ec
) {
4314 // Store pointer in pinned location
4316 converted
.Emit (ec
);
4320 public override void EmitExit (EmitContext ec
)
4322 ec
.ig
.Emit (OpCodes
.Ldc_I4_0
);
4323 ec
.ig
.Emit (OpCodes
.Conv_U
);
4328 class StringEmitter
: Emitter
4330 LocalInfo pinned_string
;
4332 public StringEmitter (Expression expr
, LocalInfo li
, Location loc
):
4335 pinned_string
= new LocalInfo (new TypeExpression (TypeManager
.string_type
, loc
), null, null, loc
);
4336 pinned_string
.Pinned
= true;
4339 public StringEmitter
Resolve (ResolveContext rc
)
4341 pinned_string
.Resolve (rc
);
4343 if (TypeManager
.int_get_offset_to_string_data
== null) {
4344 TypeManager
.int_get_offset_to_string_data
= TypeManager
.GetPredefinedProperty (
4345 TypeManager
.runtime_helpers_type
, "OffsetToStringData", pinned_string
.Location
, TypeManager
.int32_type
);
4351 public override void Emit (EmitContext ec
)
4353 pinned_string
.ResolveVariable (ec
);
4355 converted
.Emit (ec
);
4356 pinned_string
.EmitAssign (ec
);
4358 // TODO: Should use Binary::Add
4359 pinned_string
.Emit (ec
);
4360 ec
.ig
.Emit (OpCodes
.Conv_I
);
4362 PropertyExpr pe
= new PropertyExpr (pinned_string
.VariableType
, TypeManager
.int_get_offset_to_string_data
, pinned_string
.Location
);
4363 //pe.InstanceExpression = pinned_string;
4364 pe
.Resolve (new ResolveContext (ec
.MemberContext
)).Emit (ec
);
4366 ec
.ig
.Emit (OpCodes
.Add
);
4370 public override void EmitExit (EmitContext ec
)
4372 ec
.ig
.Emit (OpCodes
.Ldnull
);
4373 pinned_string
.EmitAssign (ec
);
4377 public Fixed (Expression type
, ArrayList decls
, Statement stmt
, Location l
)
4380 declarators
= decls
;
4385 public Statement Statement
{
4386 get { return statement; }
4389 public override bool Resolve (BlockContext ec
)
4392 Expression
.UnsafeError (ec
, loc
);
4396 TypeExpr texpr
= type
.ResolveAsContextualType (ec
, false);
4397 if (texpr
== null) {
4398 if (type
is VarExpr
)
4399 ec
.Report
.Error (821, type
.Location
, "A fixed statement cannot use an implicitly typed local variable");
4404 expr_type
= texpr
.Type
;
4406 data
= new Emitter
[declarators
.Count
];
4408 if (!expr_type
.IsPointer
){
4409 ec
.Report
.Error (209, loc
, "The type of locals declared in a fixed statement must be a pointer type");
4414 foreach (Pair p
in declarators
){
4415 LocalInfo vi
= (LocalInfo
) p
.First
;
4416 Expression e
= (Expression
) p
.Second
;
4418 vi
.VariableInfo
.SetAssigned (ec
);
4419 vi
.SetReadOnlyContext (LocalInfo
.ReadOnlyContext
.Fixed
);
4422 // The rules for the possible declarators are pretty wise,
4423 // but the production on the grammar is more concise.
4425 // So we have to enforce these rules here.
4427 // We do not resolve before doing the case 1 test,
4428 // because the grammar is explicit in that the token &
4429 // is present, so we need to test for this particular case.
4433 ec
.Report
.Error (254, loc
, "The right hand side of a fixed statement assignment may not be a cast expression");
4437 using (ec
.Set (ResolveContext
.Options
.FixedInitializerScope
)) {
4447 if (e
.Type
.IsArray
){
4448 Type array_type
= TypeManager
.GetElementType (e
.Type
);
4451 // Provided that array_type is unmanaged,
4453 if (!TypeManager
.VerifyUnManaged (array_type
, loc
))
4457 // and T* is implicitly convertible to the
4458 // pointer type given in the fixed statement.
4460 ArrayPtr array_ptr
= new ArrayPtr (e
, array_type
, loc
);
4462 Expression converted
= Convert
.ImplicitConversionRequired (
4463 ec
, array_ptr
, vi
.VariableType
, loc
);
4464 if (converted
== null)
4468 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4470 converted
= new Conditional (new BooleanExpression (new Binary (Binary
.Operator
.LogicalOr
,
4471 new Binary (Binary
.Operator
.Equality
, e
, new NullLiteral (loc
)),
4472 new Binary (Binary
.Operator
.Equality
, new MemberAccess (e
, "Length"), new IntConstant (0, loc
)))),
4473 new NullPointer (loc
),
4476 converted
= converted
.Resolve (ec
);
4478 data
[i
] = new ExpressionEmitter (converted
, vi
);
4487 if (e
.Type
== TypeManager
.string_type
){
4488 data
[i
] = new StringEmitter (e
, vi
, loc
).Resolve (ec
);
4493 // Case 4: fixed buffer
4494 if (e
is FixedBufferPtr
) {
4495 data
[i
++] = new ExpressionEmitter (e
, vi
);
4500 // Case 1: & object.
4502 Unary u
= e
as Unary
;
4503 if (u
!= null && u
.Oper
== Unary
.Operator
.AddressOf
) {
4504 IVariableReference vr
= u
.Expr
as IVariableReference
;
4505 if (vr
== null || !vr
.IsFixed
) {
4506 data
[i
] = new ExpressionEmitter (e
, vi
);
4510 if (data
[i
++] == null)
4511 ec
.Report
.Error (213, vi
.Location
, "You cannot use the fixed statement to take the address of an already fixed expression");
4513 e
= Convert
.ImplicitConversionRequired (ec
, e
, expr_type
, loc
);
4516 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
4517 bool ok
= statement
.Resolve (ec
);
4518 bool flow_unreachable
= ec
.EndFlowBranching ();
4519 has_ret
= flow_unreachable
;
4524 protected override void DoEmit (EmitContext ec
)
4526 for (int i
= 0; i
< data
.Length
; i
++) {
4530 statement
.Emit (ec
);
4536 // Clear the pinned variable
4538 for (int i
= 0; i
< data
.Length
; i
++) {
4539 data
[i
].EmitExit (ec
);
4543 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4545 // Fixed statement cannot be used inside anonymous methods or lambdas
4546 throw new NotSupportedException ();
4549 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4551 Fixed target
= (Fixed
) t
;
4553 target
.type
= type
.Clone (clonectx
);
4554 target
.declarators
= new ArrayList (declarators
.Count
);
4555 foreach (Pair p
in declarators
) {
4556 LocalInfo vi
= (LocalInfo
) p
.First
;
4557 Expression e
= (Expression
) p
.Second
;
4559 target
.declarators
.Add (
4560 new Pair (clonectx
.LookupVariable (vi
), e
.Clone (clonectx
)));
4563 target
.statement
= statement
.Clone (clonectx
);
4567 public class Catch
: Statement
{
4568 public readonly string Name
;
4570 public Block VarBlock
;
4572 Expression type_expr
;
4575 public Catch (Expression type
, string name
, Block block
, Block var_block
, Location l
)
4580 VarBlock
= var_block
;
4584 public Type CatchType
{
4590 public bool IsGeneral
{
4592 return type_expr
== null;
4596 protected override void DoEmit (EmitContext ec
)
4598 ILGenerator ig
= ec
.ig
;
4600 if (CatchType
!= null)
4601 ig
.BeginCatchBlock (CatchType
);
4603 ig
.BeginCatchBlock (TypeManager
.object_type
);
4605 if (VarBlock
!= null)
4609 // TODO: Move to resolve
4610 LocalVariableReference lvr
= new LocalVariableReference (Block
, Name
, loc
);
4611 lvr
.Resolve (new ResolveContext (ec
.MemberContext
));
4613 // Only to make verifier happy
4614 if (TypeManager
.IsGenericParameter (lvr
.Type
))
4615 ig
.Emit (OpCodes
.Unbox_Any
, lvr
.Type
);
4618 if (lvr
.IsHoisted
) {
4619 LocalTemporary lt
= new LocalTemporary (lvr
.Type
);
4623 // Variable is at the top of the stack
4624 source
= EmptyExpression
.Null
;
4627 lvr
.EmitAssign (ec
, source
, false, false);
4629 ig
.Emit (OpCodes
.Pop
);
4634 public override bool Resolve (BlockContext ec
)
4636 using (ec
.With (ResolveContext
.Options
.CatchScope
, true)) {
4637 if (type_expr
!= null) {
4638 TypeExpr te
= type_expr
.ResolveAsTypeTerminal (ec
, false);
4644 if (type
!= TypeManager
.exception_type
&& !TypeManager
.IsSubclassOf (type
, TypeManager
.exception_type
)){
4645 ec
.Report
.Error (155, loc
, "The type caught or thrown must be derived from System.Exception");
4651 if (!Block
.Resolve (ec
))
4654 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4655 // emit the "unused variable" warnings.
4656 if (VarBlock
!= null)
4657 return VarBlock
.Resolve (ec
);
4663 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4666 type
= storey
.MutateType (type
);
4667 if (VarBlock
!= null)
4668 VarBlock
.MutateHoistedGenericType (storey
);
4669 Block
.MutateHoistedGenericType (storey
);
4672 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4674 Catch target
= (Catch
) t
;
4676 if (type_expr
!= null)
4677 target
.type_expr
= type_expr
.Clone (clonectx
);
4678 if (VarBlock
!= null)
4679 target
.VarBlock
= clonectx
.LookupBlock (VarBlock
);
4680 target
.Block
= clonectx
.LookupBlock (Block
);
4684 public class TryFinally
: ExceptionStatement
{
4688 public TryFinally (Statement stmt
, Block fini
, Location l
)
4695 public override bool Resolve (BlockContext ec
)
4699 ec
.StartFlowBranching (this);
4701 if (!stmt
.Resolve (ec
))
4705 ec
.CurrentBranching
.CreateSibling (fini
, FlowBranching
.SiblingType
.Finally
);
4706 using (ec
.With (ResolveContext
.Options
.FinallyScope
, true)) {
4707 if (!fini
.Resolve (ec
))
4711 ec
.EndFlowBranching ();
4713 ok
&= base.Resolve (ec
);
4718 protected override void EmitPreTryBody (EmitContext ec
)
4722 protected override void EmitTryBody (EmitContext ec
)
4727 protected override void EmitFinallyBody (EmitContext ec
)
4732 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4734 stmt
.MutateHoistedGenericType (storey
);
4735 fini
.MutateHoistedGenericType (storey
);
4738 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4740 TryFinally target
= (TryFinally
) t
;
4742 target
.stmt
= (Statement
) stmt
.Clone (clonectx
);
4744 target
.fini
= clonectx
.LookupBlock (fini
);
4748 public class TryCatch
: Statement
{
4750 public ArrayList Specific
;
4751 public Catch General
;
4752 bool inside_try_finally
, code_follows
;
4754 public TryCatch (Block block
, ArrayList catch_clauses
, Location l
, bool inside_try_finally
)
4757 this.Specific
= catch_clauses
;
4758 this.inside_try_finally
= inside_try_finally
;
4760 Catch c
= (Catch
) catch_clauses
[0];
4763 catch_clauses
.RemoveAt (0);
4769 public override bool Resolve (BlockContext ec
)
4773 ec
.StartFlowBranching (this);
4775 if (!Block
.Resolve (ec
))
4778 Type
[] prev_catches
= new Type
[Specific
.Count
];
4780 foreach (Catch c
in Specific
){
4781 ec
.CurrentBranching
.CreateSibling (c
.Block
, FlowBranching
.SiblingType
.Catch
);
4783 if (c
.Name
!= null) {
4784 LocalInfo vi
= c
.Block
.GetLocalInfo (c
.Name
);
4786 throw new Exception ();
4788 vi
.VariableInfo
= null;
4791 if (!c
.Resolve (ec
)) {
4796 Type resolved_type
= c
.CatchType
;
4797 for (int ii
= 0; ii
< last_index
; ++ii
) {
4798 if (resolved_type
== prev_catches
[ii
] || TypeManager
.IsSubclassOf (resolved_type
, prev_catches
[ii
])) {
4799 ec
.Report
.Error (160, c
.loc
,
4800 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4801 TypeManager
.CSharpName (prev_catches
[ii
]));
4806 prev_catches
[last_index
++] = resolved_type
;
4809 if (General
!= null) {
4810 if (CodeGen
.Assembly
.WrapNonExceptionThrows
) {
4811 foreach (Catch c
in Specific
){
4812 if (c
.CatchType
== TypeManager
.exception_type
&& PredefinedAttributes
.Get
.RuntimeCompatibility
.IsDefined
) {
4813 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'");
4818 ec
.CurrentBranching
.CreateSibling (General
.Block
, FlowBranching
.SiblingType
.Catch
);
4820 if (!General
.Resolve (ec
))
4824 ec
.EndFlowBranching ();
4826 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4827 // So, ensure there's some IL code after this statement
4828 if (!inside_try_finally
&& !code_follows
&& ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
)
4829 ec
.NeedReturnLabel ();
4834 public void SomeCodeFollows ()
4836 code_follows
= true;
4839 protected override void DoEmit (EmitContext ec
)
4841 ILGenerator ig
= ec
.ig
;
4843 if (!inside_try_finally
)
4844 ig
.BeginExceptionBlock ();
4848 foreach (Catch c
in Specific
)
4851 if (General
!= null)
4854 if (!inside_try_finally
)
4855 ig
.EndExceptionBlock ();
4858 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4860 Block
.MutateHoistedGenericType (storey
);
4862 if (General
!= null)
4863 General
.MutateHoistedGenericType (storey
);
4864 if (Specific
!= null) {
4865 foreach (Catch c
in Specific
)
4866 c
.MutateHoistedGenericType (storey
);
4870 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4872 TryCatch target
= (TryCatch
) t
;
4874 target
.Block
= clonectx
.LookupBlock (Block
);
4875 if (General
!= null)
4876 target
.General
= (Catch
) General
.Clone (clonectx
);
4877 if (Specific
!= null){
4878 target
.Specific
= new ArrayList ();
4879 foreach (Catch c
in Specific
)
4880 target
.Specific
.Add (c
.Clone (clonectx
));
4885 // FIXME: Why is it almost exact copy of Using ??
4886 public class UsingTemporary
: ExceptionStatement
{
4887 TemporaryVariable local_copy
;
4888 public Statement Statement
;
4892 public UsingTemporary (Expression expr
, Statement stmt
, Location l
)
4899 public override bool Resolve (BlockContext ec
)
4901 expr
= expr
.Resolve (ec
);
4905 expr_type
= expr
.Type
;
4907 if (!TypeManager
.ImplementsInterface (expr_type
, TypeManager
.idisposable_type
) &&
4908 Convert
.ImplicitConversion (ec
, expr
, TypeManager
.idisposable_type
, loc
) == null) {
4909 if (!TypeManager
.IsDynamicType (expr_type
)) {
4910 Using
.Error_IsNotConvertibleToIDisposable (ec
, expr
);
4914 expr
= Convert
.ImplicitConversionRequired (ec
, expr
, TypeManager
.idisposable_type
, loc
);
4915 expr_type
= expr
.Type
;
4918 local_copy
= new TemporaryVariable (expr_type
, loc
);
4919 local_copy
.Resolve (ec
);
4921 ec
.StartFlowBranching (this);
4923 bool ok
= Statement
.Resolve (ec
);
4925 ec
.EndFlowBranching ();
4927 ok
&= base.Resolve (ec
);
4929 if (TypeManager
.void_dispose_void
== null) {
4930 TypeManager
.void_dispose_void
= TypeManager
.GetPredefinedMethod (
4931 TypeManager
.idisposable_type
, "Dispose", loc
, Type
.EmptyTypes
);
4937 protected override void EmitPreTryBody (EmitContext ec
)
4939 local_copy
.EmitAssign (ec
, expr
);
4942 protected override void EmitTryBody (EmitContext ec
)
4944 Statement
.Emit (ec
);
4947 protected override void EmitFinallyBody (EmitContext ec
)
4949 ILGenerator ig
= ec
.ig
;
4950 if (!TypeManager
.IsStruct (expr_type
)) {
4951 Label skip
= ig
.DefineLabel ();
4952 local_copy
.Emit (ec
);
4953 ig
.Emit (OpCodes
.Brfalse
, skip
);
4954 local_copy
.Emit (ec
);
4955 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4956 ig
.MarkLabel (skip
);
4960 Expression ml
= Expression
.MemberLookup (RootContext
.ToplevelTypes
.Compiler
,
4961 ec
.CurrentType
, TypeManager
.idisposable_type
, expr_type
,
4962 "Dispose", Location
.Null
);
4964 if (!(ml
is MethodGroupExpr
)) {
4965 local_copy
.Emit (ec
);
4966 ig
.Emit (OpCodes
.Box
, expr_type
);
4967 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4971 MethodInfo mi
= null;
4973 foreach (MethodInfo mk
in ((MethodGroupExpr
) ml
).Methods
) {
4974 if (TypeManager
.GetParameterData (mk
).Count
== 0) {
4981 RootContext
.ToplevelTypes
.Compiler
.Report
.Error(-100, Mono
.CSharp
.Location
.Null
, "Internal error: No Dispose method which takes 0 parameters.");
4985 local_copy
.AddressOf (ec
, AddressOp
.Load
);
4986 ig
.Emit (OpCodes
.Call
, mi
);
4989 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
4991 expr_type
= storey
.MutateType (expr_type
);
4992 local_copy
.MutateHoistedGenericType (storey
);
4993 Statement
.MutateHoistedGenericType (storey
);
4996 protected override void CloneTo (CloneContext clonectx
, Statement t
)
4998 UsingTemporary target
= (UsingTemporary
) t
;
5000 target
.expr
= expr
.Clone (clonectx
);
5001 target
.Statement
= Statement
.Clone (clonectx
);
5005 public class Using
: ExceptionStatement
{
5007 public Statement EmbeddedStatement
{
5008 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
5014 ExpressionStatement assign
;
5016 public Using (Expression
var, Expression init
, Statement stmt
, Location l
)
5024 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec
, Expression expr
)
5026 ec
.Report
.SymbolRelatedToPreviousError (expr
.Type
);
5027 ec
.Report
.Error (1674, expr
.Location
, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5028 TypeManager
.CSharpName (expr
.Type
));
5031 protected override void EmitPreTryBody (EmitContext ec
)
5033 assign
.EmitStatement (ec
);
5036 protected override void EmitTryBody (EmitContext ec
)
5041 protected override void EmitFinallyBody (EmitContext ec
)
5043 ILGenerator ig
= ec
.ig
;
5044 Label skip
= ig
.DefineLabel ();
5046 bool emit_null_check
= !TypeManager
.IsValueType (var.Type
);
5047 if (emit_null_check
) {
5049 ig
.Emit (OpCodes
.Brfalse
, skip
);
5052 Invocation
.EmitCall (ec
, false, var, TypeManager
.void_dispose_void
, null, loc
);
5054 if (emit_null_check
)
5055 ig
.MarkLabel (skip
);
5058 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5060 assign
.MutateHoistedGenericType (storey
);
5061 var.MutateHoistedGenericType (storey
);
5062 stmt
.MutateHoistedGenericType (storey
);
5065 public override bool Resolve (BlockContext ec
)
5067 if (!ResolveVariable (ec
))
5070 ec
.StartFlowBranching (this);
5072 bool ok
= stmt
.Resolve (ec
);
5074 ec
.EndFlowBranching ();
5076 ok
&= base.Resolve (ec
);
5078 if (TypeManager
.void_dispose_void
== null) {
5079 TypeManager
.void_dispose_void
= TypeManager
.GetPredefinedMethod (
5080 TypeManager
.idisposable_type
, "Dispose", loc
, Type
.EmptyTypes
);
5086 bool ResolveVariable (BlockContext ec
)
5088 assign
= new SimpleAssign (var, init
, loc
);
5089 assign
= assign
.ResolveStatement (ec
);
5093 if (assign
.Type
== TypeManager
.idisposable_type
||
5094 TypeManager
.ImplementsInterface (assign
.Type
, TypeManager
.idisposable_type
)) {
5098 Expression e
= Convert
.ImplicitConversionStandard (ec
, assign
, TypeManager
.idisposable_type
, var.Location
);
5100 if (TypeManager
.IsDynamicType (assign
.Type
)) {
5101 e
= Convert
.ImplicitConversionRequired (ec
, assign
, TypeManager
.idisposable_type
, loc
);
5102 var = new TemporaryVariable (e
.Type
, loc
);
5103 assign
= new SimpleAssign (var, e
, loc
).ResolveStatement (ec
);
5107 Error_IsNotConvertibleToIDisposable (ec
, var);
5111 throw new NotImplementedException ("covariance?");
5114 protected override void CloneTo (CloneContext clonectx
, Statement t
)
5116 Using target
= (Using
) t
;
5118 target
.var = var.Clone (clonectx
);
5119 target
.init
= init
.Clone (clonectx
);
5120 target
.stmt
= stmt
.Clone (clonectx
);
5125 /// Implementation of the foreach C# statement
5127 public class Foreach
: Statement
{
5129 sealed class ArrayForeach
: Statement
5131 class ArrayCounter
: TemporaryVariable
5133 StatementExpression increment
;
5135 public ArrayCounter (Location loc
)
5136 : base (TypeManager
.int32_type
, loc
)
5140 public void ResolveIncrement (BlockContext ec
)
5142 increment
= new StatementExpression (new UnaryMutator (UnaryMutator
.Mode
.PostIncrement
, this));
5143 increment
.Resolve (ec
);
5146 public void EmitIncrement (EmitContext ec
)
5148 increment
.Emit (ec
);
5152 readonly Foreach for_each
;
5153 readonly Statement statement
;
5156 TemporaryVariable
[] lengths
;
5157 Expression
[] length_exprs
;
5158 ArrayCounter
[] counter
;
5160 TemporaryVariable copy
;
5163 public ArrayForeach (Foreach
@foreach, int rank
)
5165 for_each
= @foreach;
5166 statement
= for_each
.statement
;
5169 counter
= new ArrayCounter
[rank
];
5170 length_exprs
= new Expression
[rank
];
5173 // Only use temporary length variables when dealing with
5174 // multi-dimensional arrays
5177 lengths
= new TemporaryVariable
[rank
];
5180 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5182 throw new NotImplementedException ();
5185 public override bool Resolve (BlockContext ec
)
5187 copy
= new TemporaryVariable (for_each
.expr
.Type
, loc
);
5190 int rank
= length_exprs
.Length
;
5191 Arguments list
= new Arguments (rank
);
5192 for (int i
= 0; i
< rank
; i
++) {
5193 counter
[i
] = new ArrayCounter (loc
);
5194 counter
[i
].ResolveIncrement (ec
);
5197 length_exprs
[i
] = new MemberAccess (copy
, "Length").Resolve (ec
);
5199 lengths
[i
] = new TemporaryVariable (TypeManager
.int32_type
, loc
);
5200 lengths
[i
].Resolve (ec
);
5202 Arguments args
= new Arguments (1);
5203 args
.Add (new Argument (new IntConstant (i
, loc
)));
5204 length_exprs
[i
] = new Invocation (new MemberAccess (copy
, "GetLength"), args
).Resolve (ec
);
5207 list
.Add (new Argument (counter
[i
]));
5210 access
= new ElementAccess (copy
, list
).Resolve (ec
);
5214 Expression var_type
= for_each
.type
;
5215 VarExpr ve
= var_type
as VarExpr
;
5217 // Infer implicitly typed local variable from foreach array type
5218 var_type
= new TypeExpression (access
.Type
, ve
.Location
);
5221 var_type
= var_type
.ResolveAsTypeTerminal (ec
, false);
5222 if (var_type
== null)
5225 conv
= Convert
.ExplicitConversion (ec
, access
, var_type
.Type
, loc
);
5231 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
5232 ec
.CurrentBranching
.CreateSibling ();
5234 for_each
.variable
= for_each
.variable
.ResolveLValue (ec
, conv
);
5235 if (for_each
.variable
== null)
5238 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Embedded
, loc
);
5239 if (!statement
.Resolve (ec
))
5241 ec
.EndFlowBranching ();
5243 // There's no direct control flow from the end of the embedded statement to the end of the loop
5244 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
5246 ec
.EndFlowBranching ();
5251 protected override void DoEmit (EmitContext ec
)
5253 ILGenerator ig
= ec
.ig
;
5255 copy
.EmitAssign (ec
, for_each
.expr
);
5257 int rank
= length_exprs
.Length
;
5258 Label
[] test
= new Label
[rank
];
5259 Label
[] loop
= new Label
[rank
];
5261 for (int i
= 0; i
< rank
; i
++) {
5262 test
[i
] = ig
.DefineLabel ();
5263 loop
[i
] = ig
.DefineLabel ();
5265 if (lengths
!= null)
5266 lengths
[i
].EmitAssign (ec
, length_exprs
[i
]);
5269 IntConstant zero
= new IntConstant (0, loc
);
5270 for (int i
= 0; i
< rank
; i
++) {
5271 counter
[i
].EmitAssign (ec
, zero
);
5273 ig
.Emit (OpCodes
.Br
, test
[i
]);
5274 ig
.MarkLabel (loop
[i
]);
5277 ((IAssignMethod
) for_each
.variable
).EmitAssign (ec
, conv
, false, false);
5279 statement
.Emit (ec
);
5281 ig
.MarkLabel (ec
.LoopBegin
);
5283 for (int i
= rank
- 1; i
>= 0; i
--){
5284 counter
[i
].EmitIncrement (ec
);
5286 ig
.MarkLabel (test
[i
]);
5287 counter
[i
].Emit (ec
);
5289 if (lengths
!= null)
5290 lengths
[i
].Emit (ec
);
5292 length_exprs
[i
].Emit (ec
);
5294 ig
.Emit (OpCodes
.Blt
, loop
[i
]);
5297 ig
.MarkLabel (ec
.LoopEnd
);
5300 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5302 for_each
.expr
.MutateHoistedGenericType (storey
);
5304 copy
.MutateHoistedGenericType (storey
);
5305 conv
.MutateHoistedGenericType (storey
);
5306 statement
.MutateHoistedGenericType (storey
);
5308 for (int i
= 0; i
< counter
.Length
; i
++) {
5309 counter
[i
].MutateHoistedGenericType (storey
);
5310 if (lengths
!= null)
5311 lengths
[i
].MutateHoistedGenericType (storey
);
5316 sealed class CollectionForeach
: Statement
5318 class CollectionForeachStatement
: Statement
5321 Expression variable
, current
, conv
;
5322 Statement statement
;
5325 public CollectionForeachStatement (Type type
, Expression variable
,
5326 Expression current
, Statement statement
,
5330 this.variable
= variable
;
5331 this.current
= current
;
5332 this.statement
= statement
;
5336 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5338 throw new NotImplementedException ();
5341 public override bool Resolve (BlockContext ec
)
5343 current
= current
.Resolve (ec
);
5344 if (current
== null)
5347 conv
= Convert
.ExplicitConversion (ec
, current
, type
, loc
);
5351 assign
= new SimpleAssign (variable
, conv
, loc
);
5352 if (assign
.Resolve (ec
) == null)
5355 if (!statement
.Resolve (ec
))
5361 protected override void DoEmit (EmitContext ec
)
5363 assign
.EmitStatement (ec
);
5364 statement
.Emit (ec
);
5367 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5369 assign
.MutateHoistedGenericType (storey
);
5370 statement
.MutateHoistedGenericType (storey
);
5374 Expression variable
, expr
;
5375 Statement statement
;
5377 TemporaryVariable enumerator
;
5382 MethodGroupExpr get_enumerator
;
5383 PropertyExpr get_current
;
5384 MethodInfo move_next
;
5385 Expression var_type
;
5386 Type enumerator_type
;
5387 bool enumerator_found
;
5389 public CollectionForeach (Expression var_type
, Expression
var,
5390 Expression expr
, Statement stmt
, Location l
)
5392 this.var_type
= var_type
;
5393 this.variable
= var;
5399 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5401 throw new NotImplementedException ();
5404 bool GetEnumeratorFilter (ResolveContext ec
, MethodInfo mi
)
5406 Type return_type
= mi
.ReturnType
;
5409 // Ok, we can access it, now make sure that we can do something
5410 // with this `GetEnumerator'
5413 if (return_type
== TypeManager
.ienumerator_type
||
5414 TypeManager
.ImplementsInterface (return_type
, TypeManager
.ienumerator_type
)) {
5416 // If it is not an interface, lets try to find the methods ourselves.
5417 // For example, if we have:
5418 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5419 // We can avoid the iface call. This is a runtime perf boost.
5420 // even bigger if we have a ValueType, because we avoid the cost
5423 // We have to make sure that both methods exist for us to take
5424 // this path. If one of the methods does not exist, we will just
5425 // use the interface. Sadly, this complex if statement is the only
5426 // way I could do this without a goto
5429 if (TypeManager
.bool_movenext_void
== null) {
5430 TypeManager
.bool_movenext_void
= TypeManager
.GetPredefinedMethod (
5431 TypeManager
.ienumerator_type
, "MoveNext", loc
, Type
.EmptyTypes
);
5434 if (TypeManager
.ienumerator_getcurrent
== null) {
5435 TypeManager
.ienumerator_getcurrent
= TypeManager
.GetPredefinedProperty (
5436 TypeManager
.ienumerator_type
, "Current", loc
, TypeManager
.object_type
);
5440 // Prefer a generic enumerator over a non-generic one.
5442 if (return_type
.IsInterface
&& TypeManager
.IsGenericType (return_type
)) {
5443 enumerator_type
= return_type
;
5444 if (!FetchGetCurrent (ec
, return_type
))
5445 get_current
= new PropertyExpr (
5446 ec
.CurrentType
, TypeManager
.ienumerator_getcurrent
, loc
);
5447 if (!FetchMoveNext (return_type
))
5448 move_next
= TypeManager
.bool_movenext_void
;
5452 if (return_type
.IsInterface
||
5453 !FetchMoveNext (return_type
) ||
5454 !FetchGetCurrent (ec
, return_type
)) {
5455 enumerator_type
= return_type
;
5456 move_next
= TypeManager
.bool_movenext_void
;
5457 get_current
= new PropertyExpr (
5458 ec
.CurrentType
, TypeManager
.ienumerator_getcurrent
, loc
);
5463 // Ok, so they dont return an IEnumerable, we will have to
5464 // find if they support the GetEnumerator pattern.
5467 if (TypeManager
.HasElementType (return_type
) || !FetchMoveNext (return_type
) || !FetchGetCurrent (ec
, return_type
)) {
5468 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",
5469 TypeManager
.CSharpName (return_type
), TypeManager
.CSharpSignature (mi
));
5474 enumerator_type
= return_type
;
5480 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5482 bool FetchMoveNext (Type t
)
5484 MemberInfo
[] move_next_list
= TypeManager
.MemberLookup (null, null, t
,
5486 BindingFlags
.Public
| BindingFlags
.Instance
,
5489 if (move_next_list
== null)
5492 foreach (MemberInfo m
in move_next_list
){
5493 MethodInfo mi
= (MethodInfo
) m
;
5495 if ((TypeManager
.GetParameterData (mi
).Count
== 0) &&
5496 TypeManager
.TypeToCoreType (mi
.ReturnType
) == TypeManager
.bool_type
) {
5506 // Retrieves a `public T get_Current ()' method from the Type `t'
5508 bool FetchGetCurrent (ResolveContext ec
, Type t
)
5510 PropertyExpr pe
= Expression
.MemberLookup (ec
.Compiler
,
5511 ec
.CurrentType
, t
, "Current", MemberTypes
.Property
,
5512 Expression
.AllBindingFlags
, loc
) as PropertyExpr
;
5520 void Error_Enumerator (BlockContext ec
)
5522 if (enumerator_found
) {
5526 ec
.Report
.Error (1579, loc
,
5527 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5528 TypeManager
.CSharpName (expr
.Type
));
5531 bool IsOverride (MethodInfo m
)
5533 m
= (MethodInfo
) TypeManager
.DropGenericMethodArguments (m
);
5535 if (!m
.IsVirtual
|| ((m
.Attributes
& MethodAttributes
.NewSlot
) != 0))
5537 if (m
is MethodBuilder
)
5540 MethodInfo base_method
= m
.GetBaseDefinition ();
5541 return base_method
!= m
;
5544 bool TryType (ResolveContext ec
, Type t
)
5546 MethodGroupExpr mg
= Expression
.MemberLookup (ec
.Compiler
,
5547 ec
.CurrentType
, t
, "GetEnumerator", MemberTypes
.Method
,
5548 Expression
.AllBindingFlags
, loc
) as MethodGroupExpr
;
5552 MethodInfo result
= null;
5553 MethodInfo tmp_move_next
= null;
5554 PropertyExpr tmp_get_cur
= null;
5555 Type tmp_enumerator_type
= enumerator_type
;
5556 foreach (MethodInfo mi
in mg
.Methods
) {
5557 if (TypeManager
.GetParameterData (mi
).Count
!= 0)
5560 // Check whether GetEnumerator is public
5561 if ((mi
.Attributes
& MethodAttributes
.Public
) != MethodAttributes
.Public
)
5564 if (IsOverride (mi
))
5567 enumerator_found
= true;
5569 if (!GetEnumeratorFilter (ec
, mi
))
5572 if (result
!= null) {
5573 if (TypeManager
.IsGenericType (result
.ReturnType
)) {
5574 if (!TypeManager
.IsGenericType (mi
.ReturnType
))
5577 MethodBase mb
= TypeManager
.DropGenericMethodArguments (mi
);
5578 ec
.Report
.SymbolRelatedToPreviousError (t
);
5579 ec
.Report
.Error(1640, loc
, "foreach statement cannot operate on variables of type `{0}' " +
5580 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5581 TypeManager
.CSharpName (t
), TypeManager
.CSharpSignature (mb
));
5585 // Always prefer generics enumerators
5586 if (!TypeManager
.IsGenericType (mi
.ReturnType
)) {
5587 if (TypeManager
.ImplementsInterface (mi
.DeclaringType
, result
.DeclaringType
) ||
5588 TypeManager
.ImplementsInterface (result
.DeclaringType
, mi
.DeclaringType
))
5591 ec
.Report
.SymbolRelatedToPreviousError (result
);
5592 ec
.Report
.SymbolRelatedToPreviousError (mi
);
5593 ec
.Report
.Warning (278, 2, loc
, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5594 TypeManager
.CSharpName (t
), "enumerable", TypeManager
.CSharpSignature (result
), TypeManager
.CSharpSignature (mi
));
5599 tmp_move_next
= move_next
;
5600 tmp_get_cur
= get_current
;
5601 tmp_enumerator_type
= enumerator_type
;
5602 if (mi
.DeclaringType
== t
)
5606 if (result
!= null) {
5607 move_next
= tmp_move_next
;
5608 get_current
= tmp_get_cur
;
5609 enumerator_type
= tmp_enumerator_type
;
5610 MethodInfo
[] mi
= new MethodInfo
[] { (MethodInfo) result }
;
5611 get_enumerator
= new MethodGroupExpr (mi
, enumerator_type
, loc
);
5613 if (t
!= expr
.Type
) {
5614 expr
= Convert
.ExplicitConversion (
5617 throw new InternalErrorException ();
5620 get_enumerator
.InstanceExpression
= expr
;
5621 get_enumerator
.IsBase
= t
!= expr
.Type
;
5629 bool ProbeCollectionType (ResolveContext ec
, Type t
)
5631 int errors
= ec
.Report
.Errors
;
5632 for (Type tt
= t
; tt
!= null && tt
!= TypeManager
.object_type
;){
5633 if (TryType (ec
, tt
))
5638 if (ec
.Report
.Errors
> errors
)
5642 // Now try to find the method in the interfaces
5644 Type
[] ifaces
= TypeManager
.GetInterfaces (t
);
5645 foreach (Type i
in ifaces
){
5646 if (TryType (ec
, i
))
5653 public override bool Resolve (BlockContext ec
)
5655 enumerator_type
= TypeManager
.ienumerator_type
;
5657 bool is_dynamic
= TypeManager
.IsDynamicType (expr
.Type
);
5659 expr
= Convert
.ImplicitConversionRequired (ec
, expr
, TypeManager
.ienumerable_type
, loc
);
5661 if (!ProbeCollectionType (ec
, expr
.Type
)) {
5662 Error_Enumerator (ec
);
5666 VarExpr ve
= var_type
as VarExpr
;
5668 // Infer implicitly typed local variable from foreach enumerable type
5669 var_type
= new TypeExpression (
5670 is_dynamic
? InternalType
.Dynamic
: get_current
.PropertyInfo
.PropertyType
,
5674 var_type
= var_type
.ResolveAsTypeTerminal (ec
, false);
5675 if (var_type
== null)
5678 enumerator
= new TemporaryVariable (enumerator_type
, loc
);
5679 enumerator
.Resolve (ec
);
5681 init
= new Invocation (get_enumerator
, null);
5682 init
= init
.Resolve (ec
);
5686 Expression move_next_expr
;
5688 MemberInfo
[] mi
= new MemberInfo
[] { move_next }
;
5689 MethodGroupExpr mg
= new MethodGroupExpr (mi
, var_type
.Type
, loc
);
5690 mg
.InstanceExpression
= enumerator
;
5692 move_next_expr
= new Invocation (mg
, null);
5695 get_current
.InstanceExpression
= enumerator
;
5697 Statement block
= new CollectionForeachStatement (
5698 var_type
.Type
, variable
, get_current
, statement
, loc
);
5700 loop
= new While (new BooleanExpression (move_next_expr
), block
, loc
);
5703 bool implements_idisposable
= TypeManager
.ImplementsInterface (enumerator_type
, TypeManager
.idisposable_type
);
5704 if (implements_idisposable
|| !enumerator_type
.IsSealed
) {
5705 wrapper
= new DisposableWrapper (this, implements_idisposable
);
5707 wrapper
= new NonDisposableWrapper (this);
5710 return wrapper
.Resolve (ec
);
5713 protected override void DoEmit (EmitContext ec
)
5718 class NonDisposableWrapper
: Statement
{
5719 CollectionForeach parent
;
5721 internal NonDisposableWrapper (CollectionForeach parent
)
5723 this.parent
= parent
;
5726 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5728 throw new NotSupportedException ();
5731 public override bool Resolve (BlockContext ec
)
5733 return parent
.ResolveLoop (ec
);
5736 protected override void DoEmit (EmitContext ec
)
5738 parent
.EmitLoopInit (ec
);
5739 parent
.EmitLoopBody (ec
);
5742 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5744 throw new NotSupportedException ();
5748 sealed class DisposableWrapper
: ExceptionStatement
5750 CollectionForeach parent
;
5751 bool implements_idisposable
;
5753 internal DisposableWrapper (CollectionForeach parent
, bool implements
)
5755 this.parent
= parent
;
5756 this.implements_idisposable
= implements
;
5759 protected override void CloneTo (CloneContext clonectx
, Statement target
)
5761 throw new NotSupportedException ();
5764 public override bool Resolve (BlockContext ec
)
5768 ec
.StartFlowBranching (this);
5770 if (!parent
.ResolveLoop (ec
))
5773 ec
.EndFlowBranching ();
5775 ok
&= base.Resolve (ec
);
5777 if (TypeManager
.void_dispose_void
== null) {
5778 TypeManager
.void_dispose_void
= TypeManager
.GetPredefinedMethod (
5779 TypeManager
.idisposable_type
, "Dispose", loc
, Type
.EmptyTypes
);
5784 protected override void EmitPreTryBody (EmitContext ec
)
5786 parent
.EmitLoopInit (ec
);
5789 protected override void EmitTryBody (EmitContext ec
)
5791 parent
.EmitLoopBody (ec
);
5794 protected override void EmitFinallyBody (EmitContext ec
)
5796 Expression instance
= parent
.enumerator
;
5797 if (!TypeManager
.IsValueType (parent
.enumerator_type
)) {
5798 ILGenerator ig
= ec
.ig
;
5800 parent
.enumerator
.Emit (ec
);
5802 Label call_dispose
= ig
.DefineLabel ();
5804 if (!implements_idisposable
) {
5805 ec
.ig
.Emit (OpCodes
.Isinst
, TypeManager
.idisposable_type
);
5806 LocalTemporary temp
= new LocalTemporary (TypeManager
.idisposable_type
);
5812 ig
.Emit (OpCodes
.Brtrue_S
, call_dispose
);
5814 // using 'endfinally' to empty the evaluation stack
5815 ig
.Emit (OpCodes
.Endfinally
);
5816 ig
.MarkLabel (call_dispose
);
5819 Invocation
.EmitCall (ec
, false, instance
, TypeManager
.void_dispose_void
, null, loc
);
5822 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5824 throw new NotSupportedException ();
5828 bool ResolveLoop (BlockContext ec
)
5830 return loop
.Resolve (ec
);
5833 void EmitLoopInit (EmitContext ec
)
5835 enumerator
.EmitAssign (ec
, init
);
5838 void EmitLoopBody (EmitContext ec
)
5843 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5845 enumerator_type
= storey
.MutateType (enumerator_type
);
5846 init
.MutateHoistedGenericType (storey
);
5847 loop
.MutateHoistedGenericType (storey
);
5852 Expression variable
;
5854 Statement statement
;
5856 public Foreach (Expression type
, LocalVariableReference
var, Expression expr
,
5857 Statement stmt
, Location l
)
5860 this.variable
= var;
5866 public Statement Statement
{
5867 get { return statement; }
5870 public override bool Resolve (BlockContext ec
)
5872 expr
= expr
.Resolve (ec
);
5877 ec
.Report
.Error (186, loc
, "Use of null is not valid in this context");
5881 if (expr
.Type
== TypeManager
.string_type
) {
5882 statement
= new ArrayForeach (this, 1);
5883 } else if (expr
.Type
.IsArray
) {
5884 statement
= new ArrayForeach (this, expr
.Type
.GetArrayRank ());
5886 if (expr
.eclass
== ExprClass
.MethodGroup
|| expr
is AnonymousMethodExpression
) {
5887 ec
.Report
.Error (446, expr
.Location
, "Foreach statement cannot operate on a `{0}'",
5888 expr
.ExprClassName
);
5892 statement
= new CollectionForeach (type
, variable
, expr
, statement
, loc
);
5895 return statement
.Resolve (ec
);
5898 protected override void DoEmit (EmitContext ec
)
5900 ILGenerator ig
= ec
.ig
;
5902 Label old_begin
= ec
.LoopBegin
, old_end
= ec
.LoopEnd
;
5903 ec
.LoopBegin
= ig
.DefineLabel ();
5904 ec
.LoopEnd
= ig
.DefineLabel ();
5906 statement
.Emit (ec
);
5908 ec
.LoopBegin
= old_begin
;
5909 ec
.LoopEnd
= old_end
;
5912 protected override void CloneTo (CloneContext clonectx
, Statement t
)
5914 Foreach target
= (Foreach
) t
;
5916 target
.type
= type
.Clone (clonectx
);
5917 target
.variable
= variable
.Clone (clonectx
);
5918 target
.expr
= expr
.Clone (clonectx
);
5919 target
.statement
= statement
.Clone (clonectx
);
5922 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
5924 statement
.MutateHoistedGenericType (storey
);